feat: 模态框选择表格
This commit is contained in:
@ -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>
|
<template>
|
||||||
<div>
|
<span @click="showDialog = true">
|
||||||
SelectTableModal
|
<slot>
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="ThSelectTableModal">
|
<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>
|
</script>
|
||||||
|
@ -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 type { Meta, StoryObj } from '@storybook/vue3'
|
||||||
import ThSelectTableModal from '../packages/select-table-modal/index.vue'
|
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 = {
|
const meta = {
|
||||||
title: '表单组件/SelectTableModal',
|
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>
|
} satisfies Meta<typeof ThSelectTableModal>
|
||||||
export default meta
|
export default meta
|
||||||
|
|
||||||
|
15
types/components.d.ts
vendored
15
types/components.d.ts
vendored
@ -8,12 +8,25 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
DownloadLink: typeof import('./../packages/download-link/index.vue')['default']
|
DownloadLink: typeof import('./../packages/download-link/index.vue')['default']
|
||||||
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
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']
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
ElLink: typeof import('element-plus/es')['ElLink']
|
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']
|
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']
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
Header: typeof import('./../packages/header/index.vue')['default']
|
Header: typeof import('./../packages/header/index.vue')['default']
|
||||||
PreviewOffice: typeof import('./../packages/preview-office/index.vue')['default']
|
PreviewOffice: typeof import('./../packages/preview-office/index.vue')['default']
|
||||||
PreviewOfficeView: typeof import('./../packages/preview-office-view/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']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SelectTableModal: typeof import('./../packages/select-table-modal/index.vue')['default']
|
SelectTableModal: typeof import('./../packages/select-table-modal/index.vue')['default']
|
||||||
UploadAvatar: typeof import('./../packages/upload-avatar/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']
|
UploadSingleFile: typeof import('./../packages/upload-single-file/index.vue')['default']
|
||||||
UploadTable: typeof import('./../packages/upload-table/index.vue')['default']
|
UploadTable: typeof import('./../packages/upload-table/index.vue')['default']
|
||||||
}
|
}
|
||||||
|
9
types/page.d.ts
vendored
Normal file
9
types/page.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/** 通用分页模型 */
|
||||||
|
declare interface Page<T = any[]> {
|
||||||
|
code: number
|
||||||
|
/** 分页数据 */
|
||||||
|
rows: T
|
||||||
|
/** 总页数 */
|
||||||
|
total: number
|
||||||
|
message: string
|
||||||
|
}
|
Reference in New Issue
Block a user