feat: 模态框选择表格

This commit is contained in:
2023-07-21 21:41:51 +08:00
parent 49a39a22bb
commit 88ea85a136
4 changed files with 262 additions and 5 deletions

View File

@ -1,8 +1,189 @@
<!--
* @Author: zhaojinfeng 121016171@qq.com
* @Date: 2023-07-18 12:30:07
* @LastEditors: zhaojinfeng 121016171@qq.com
* @LastEditTime: 2023-07-21 21:39:13
* @FilePath: \vue3\packages\select-table-modal\index.vue
* @Description: 模态框选择表格
*
-->
<template>
<div>
SelectTableModal
</div>
<span @click="showDialog = true">
<slot>
<el-button @click="showDialog = true">点击选择</el-button>
</slot>
</span>
<el-config-provider :locale="locale">
<el-dialog v-model="showDialog" :title="title" :width="width" append-to-body @open="getList" @closed="closed">
<el-form ref="queryRef" :modal="queryParams" inline>
<el-form-item v-for="formItem in formItems" :key="formItem.prop" :label="formItem.label" :prop="formItem.prop">
<el-select v-if="formItem.selectOptions" :placeholder="formItem.placeholder">
<el-option v-for="option in formItem.selectOptions" :key="option.value" :value="option.value">
{{ option.label }}
</el-option>
</el-select>
<el-input v-else v-model="queryParams[formItem.prop]" :placeholder="formItem.placeholder" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="getList">
查询
</el-button>
<el-button :loading="loading" @click="resetQuery">
重置
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
border
:data="tableData"
highlight-current-row
:row-key="rowKey"
:empty-text="emptyText"
@current-change="handleCurrentChange"
>
<el-table-column v-for="column in columns" :key="column.prop" :align="column.align || 'center'" :label="column.label" :prop="column.prop" :formatter="column.formatter" />
</el-table>
<div w-full h-70px relative>
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
absolute
right-50px
top-20px
background
hide-on-single-page
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
@size-change="handleSizeChange"
@current-change="getList"
/>
</div>
<template #footer>
<el-space>
<el-button type="primary" @click="confirm">
确定
</el-button>
<el-button @click="showDialog = false">
取消
</el-button>
</el-space>
</template>
</el-dialog>
</el-config-provider>
</template>
<script lang="ts" setup name="ThSelectTableModal">
import type { FormInstance, TableColumnCtx, TableInstance } from 'element-plus'
import locale from 'element-plus/lib/locale/lang/zh-cn'
const props = withDefaults(
defineProps<{
emptyText?: string
value?: any
rowKey?: string
show: boolean
columns: Partial<TableColumnCtx<any>>[]
formItems: FormItem[]
defaultParams?: Record<string, any>
request: (args: any) => Promise<Page>
layout?: string
pageSizes?: number[]
pagerCount?: number
hideButton?: boolean
hidePagination?: boolean
title?: string
width?: string | number
}>(),
{
hidePagination: false,
layout: 'total, sizes, prev, pager, next, jumper',
rowKey: 'id',
pageSizes: () => [10, 20, 30, 50],
pagerCount: () => document.body.clientWidth < 992 ? 5 : 7,
hideButton: false,
width: '1000px',
title: '选择',
emptyText: '暂无数据',
},
)
const emit = defineEmits<{
(event: 'update:show', show: boolean): void
(event: 'update:value', show: any): void
(event: 'confirm', row: any): void
}>()
interface FormItem {
label: string
prop: string
placeholder?: string
selectOptions?: {
label: string
value: string
}[]
}
const showDialog = useVModel(props, 'show')
let tableData = $ref<any[]>([])
let selection = $ref<any>()
function confirm() {
emit('confirm', selection)
showDialog.value = false
}
let total = $ref(0)
function handleCurrentChange(val?: any) {
selection = val
}
/* 表单绑定的ref */
const queryRef = $ref<FormInstance>(null)
const tableRef = $ref<TableInstance>(null)
/* 初始加载 */
let loading = $ref(false)
/* 查询参数 */
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
})
/** 查询表格数据 */
async function getList() {
loading = true
tableData = []
try {
const response = await props.request({
...props.defaultParams,
...queryParams,
})
tableData = response.rows
const row = tableData.find(row => row[props.rowKey] === props.value)
total = response.total
if (row)
tableRef?.setCurrentRow(row)
}
finally {
loading = false
}
}
/** 重置按钮操作 */
function resetQuery() {
queryRef?.resetFields()
queryParams.pageNum = 1
getList()
}
function handleSizeChange(val: number) {
if (queryParams.pageNum * val > total)
queryParams.pageNum = 1
}
function closed() {
queryRef?.resetFields()
tableData = []
queryParams.pageNum = 1
selection = undefined
}
</script>

View File

@ -1,10 +1,62 @@
/*
* @Author: zhaojinfeng 121016171@qq.com
* @Date: 2023-07-18 12:30:07
* @LastEditors: zhaojinfeng 121016171@qq.com
* @LastEditTime: 2023-07-21 21:40:46
* @FilePath: \vue3\stories\SelectTableModal.stories.ts
* @Description:
*
*/
import type { Meta, StoryObj } from '@storybook/vue3'
import ThSelectTableModal from '../packages/select-table-modal/index.vue'
// 模拟1秒钟的接口请求
function request() {
return new Promise<Page>((resolve) => {
setTimeout(() => {
resolve({
code: 200,
rows: [{ prop1: 'prop1', prop2: 'prop2', prop3: 'prop3' }],
total: 10,
message: 'ok',
})
}, 1000)
})
}
const meta = {
title: '表单组件/SelectTableModal',
component: ThSelectTableModal,
tags: ['autodocs'],
tags: ['表单组件', 'autodocs'],
args: {
value: '',
rowKey: 'prop1',
show: false,
columns: [{ label: 'label1', prop: 'prop1' }, { label: 'label2', prop: 'prop2' }, { label: 'label3', prop: 'prop3' }],
formItems: [{ label: 'label1', prop: 'prop1' }, { label: 'label2', prop: 'prop2', selectOptions: [{ label: '1', value: '1' }, { label: '2', value: '2' }] }],
defaultParams: {},
request,
layout: 'total, sizes, prev, pager, next, jumper',
pageSizes: [10, 20, 30, 50],
pagerCount: document.body.clientWidth < 992 ? 5 : 7,
hidePagination: false,
},
render: args => ({
components: { ThSelectTableModal },
setup() {
return { args }
},
template: `<th-select-table-modal
v-model:show="args.show"
:row-key="args.rowKey"
:columns="args.columns"
:form-items="args.formItems"
:default-params="args.defaultParams"
:request="args.request"
layout="args.layout"
:page-sizes="args.pageSizes"
:pager-count="args.pagerCount"
/>`,
}),
} satisfies Meta<typeof ThSelectTableModal>
export default meta

15
types/components.d.ts vendored
View File

@ -8,12 +8,25 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
DownloadLink: typeof import('./../packages/download-link/index.vue')['default']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElUpload: typeof import('element-plus/es')['ElUpload']
Header: typeof import('./../packages/header/index.vue')['default']
PreviewOffice: typeof import('./../packages/preview-office/index.vue')['default']
PreviewOfficeView: typeof import('./../packages/preview-office-view/index.vue')['default']
@ -21,6 +34,8 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
SelectTableModal: typeof import('./../packages/select-table-modal/index.vue')['default']
UploadAvatar: typeof import('./../packages/upload-avatar/index.vue')['default']
Uploadmultiplefile: typeof import('./../packages/uploadmultiplefile/index.vue')['default']
UploadMultipleFile: typeof import('./../packages/upload-multiple-file/index.vue')['default']
UploadSingleFile: typeof import('./../packages/upload-single-file/index.vue')['default']
UploadTable: typeof import('./../packages/upload-table/index.vue')['default']
}

9
types/page.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
/** 通用分页模型 */
declare interface Page<T = any[]> {
code: number
/** 分页数据 */
rows: T
/** 总页数 */
total: number
message: string
}