|
@@ -0,0 +1,489 @@
|
|
|
|
+import {computed, nextTick, reactive, Ref, ref, watch, onMounted} from 'vue'
|
|
|
|
+import type {VxePagerProps, VxeTableProps} from "vxe-table";
|
|
|
|
+import {VxePager, VxeTable, VxeColumn} from "vxe-table";
|
|
|
|
+import {TablePublicMethods, VxeTableEventProps} from "vxe-table/types/table";
|
|
|
|
+import XEUtils from "xe-utils";
|
|
|
|
+import {stringIsBlank, stringNotBlank} from "@/utils/blank-utils";
|
|
|
|
+import {TableExportMethods} from "vxe-table/types/export";
|
|
|
|
+import {BizException, ExceptionEnum} from "@/utils/BizException";
|
|
|
|
+import {eachAndReturnList} from "@/utils/cyRefList";
|
|
|
|
+import {ElButton, ElPopover} from "element-plus";
|
|
|
|
+import setDialogToJs from "@/components/js-dialog-comp/useDialogToJs";
|
|
|
|
+import CyDialog from "@/components/cy/dialog/src/CyDialog.vue";
|
|
|
|
+import {IsCyDialog} from "@/components/cy/dialog/src/useCyDialog";
|
|
|
|
+import CyFlex from "@/components/cy/flex/src/CyFlex.vue";
|
|
|
|
+
|
|
|
|
+declare type PageQuery = {
|
|
|
|
+ currentPage?: number,
|
|
|
|
+ pageSize?: number,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+declare type SimplifiedConfiguration<D> = {
|
|
|
|
+ rowHeight?: number,
|
|
|
|
+ keyField?: string,
|
|
|
|
+ currentKey?: string | number,
|
|
|
|
+ remoteSearch?: (data?: PageQuery & any) => Promise<D[]>,
|
|
|
|
+ tableProps?: VxeTableEventProps<D> & VxeTableProps<D>,
|
|
|
|
+ showPage?: boolean,
|
|
|
|
+ result?: string,
|
|
|
|
+ total?: string,
|
|
|
|
+ pagesProps?: VxePagerProps,
|
|
|
|
+ showCheckbox?: boolean,
|
|
|
|
+ mountedQuery?: boolean,
|
|
|
|
+ dialogProps?: IsCyDialog,
|
|
|
|
+ dialogHeader?: () => any
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+function tsxVModel(modelValue: object, name: string, modelName?: string) {
|
|
|
|
+ const updateName = modelName || name
|
|
|
|
+ return {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ [updateName]: modelValue[name],
|
|
|
|
+ ['onUpdate:' + updateName]: (el: any) => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ modelValue[name] = el
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function useVxeTable<D = any>(simplifiedConfiguration: SimplifiedConfiguration<D>) {
|
|
|
|
+ const tableRef: Ref<TablePublicMethods<D> & TableExportMethods<D> | undefined> = ref()
|
|
|
|
+ const props: VxeTableProps<D> & VxeTableEventProps<D> & SimplifiedConfiguration<D> = reactive({
|
|
|
|
+ height: '100%',
|
|
|
|
+ rowConfig: {
|
|
|
|
+ isHover: true,
|
|
|
|
+ isCurrent: true,
|
|
|
|
+ height: simplifiedConfiguration?.rowHeight || 48,
|
|
|
|
+ useKey: true,
|
|
|
|
+ keyField: simplifiedConfiguration?.keyField || '',
|
|
|
|
+ },
|
|
|
|
+ scrollY: {
|
|
|
|
+ enabled: true,
|
|
|
|
+ gt: 0
|
|
|
|
+ },
|
|
|
|
+ columnConfig: {
|
|
|
|
+ resizable: true
|
|
|
|
+ },
|
|
|
|
+ scrollX: {
|
|
|
|
+ enabled: false,
|
|
|
|
+ },
|
|
|
|
+ exportConfig: {},
|
|
|
|
+ showOverflow: true,
|
|
|
|
+ currentKey: '',
|
|
|
|
+ menuConfig: {
|
|
|
|
+ enabled: true
|
|
|
|
+ },
|
|
|
|
+ loading: false,
|
|
|
|
+ loadingConfig: {
|
|
|
|
+ text: '加载中...',
|
|
|
|
+ icon: '',
|
|
|
|
+ },
|
|
|
|
+ checkboxConfig: {
|
|
|
|
+ reserve: true,
|
|
|
|
+ highlight: true,
|
|
|
|
+ range: true,
|
|
|
|
+ },
|
|
|
|
+ data: [],
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ if (simplifiedConfiguration.showPage) {
|
|
|
|
+ delete props.rowConfig?.height
|
|
|
|
+ props.scrollY!.enabled = false
|
|
|
|
+ props!.showOverflow = false
|
|
|
|
+ props!.checkboxConfig!.reserve = true
|
|
|
|
+ if (stringIsBlank(props.rowConfig?.keyField)) {
|
|
|
|
+ BizException(ExceptionEnum.MESSAGE_ERROR, '请先设置行id')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const pageVO = reactive({
|
|
|
|
+ currentPage: 1,
|
|
|
|
+ pageSize: 30,
|
|
|
|
+ total: 0
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ const pagerProps: VxePagerProps = reactive({
|
|
|
|
+ loading: false,
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ watch(() => props.currentKey, () => {
|
|
|
|
+ if (stringNotBlank(defaultProps.value.rowConfig?.keyField)) {
|
|
|
|
+ const findData = XEUtils.find(tableRef.value?.getData(), (item) => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ return item[defaultProps.value.rowConfig?.keyField] === props.currentKey;
|
|
|
|
+ })
|
|
|
|
+ if (findData) {
|
|
|
|
+ tableRef.value?.scrollToRow(findData)
|
|
|
|
+ tableRef.value?.setCurrentRow(findData)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }, {flush: 'post'})
|
|
|
|
+
|
|
|
|
+ const defaultProps = computed(() => {
|
|
|
|
+ return {
|
|
|
|
+ ...simplifiedConfiguration?.tableProps,
|
|
|
|
+ ...props
|
|
|
|
+ } as VxeTableProps<D>
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ const CyVxeTable = (props: VxeTableProps<D>, {slots}: any) => {
|
|
|
|
+
|
|
|
|
+ mutation.setTableDefaultSlots = () => {
|
|
|
|
+ return slots.default ? slots.default() : null
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const TempVxeTable = () => {
|
|
|
|
+ return <VxeTable
|
|
|
|
+ ref={tableRef}
|
|
|
|
+ {...defaultProps.value}
|
|
|
|
+ {...props}
|
|
|
|
+ >
|
|
|
|
+
|
|
|
|
+ {{
|
|
|
|
+ default: () => {
|
|
|
|
+ function renderIcon(checked: boolean, indeterminate: boolean) {
|
|
|
|
+ if (indeterminate) {
|
|
|
|
+ return <i class="vxe-icon-square-minus-fill"></i>
|
|
|
|
+ } else if (checked) {
|
|
|
|
+ return <i class="vxe-icon-square-checked-fill"></i>
|
|
|
|
+ } else {
|
|
|
|
+ return <i class="vxe-icon-checkbox-unchecked"></i>
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function checkboxHeader(checked: boolean, indeterminate: boolean) {
|
|
|
|
+ return <ElPopover placement="right">
|
|
|
|
+ {{
|
|
|
|
+ default: () => {
|
|
|
|
+ return [
|
|
|
|
+ `当前选中${pageMemory.size}条`,
|
|
|
|
+ <div style="text-align: right;margin-top: 5px;">
|
|
|
|
+ <ElButton
|
|
|
|
+ type="primary"
|
|
|
|
+ onClick={(el) => {
|
|
|
|
+ if (pageMemory.size > 0)
|
|
|
|
+ handelLookCheckBox()
|
|
|
|
+ }}
|
|
|
|
+ >{{default: () => '详情'}}
|
|
|
|
+ </ElButton>
|
|
|
|
+ <ElButton
|
|
|
|
+ type="danger"
|
|
|
|
+ onClick={(el) => {
|
|
|
|
+ mutation.clearCheckboxRow()
|
|
|
|
+ }}
|
|
|
|
+ >{{default: () => '清空'}}
|
|
|
|
+ </ElButton>
|
|
|
|
+ </div>
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ reference: () => {
|
|
|
|
+ return <span
|
|
|
|
+ style={"font-size: 1.1em;cursor: pointer;"}
|
|
|
|
+ onClick={(el) => {
|
|
|
|
+ el.preventDefault()
|
|
|
|
+ el.stopPropagation()
|
|
|
|
+ handelCheckboxAll()
|
|
|
|
+ }}>{renderIcon(checked, indeterminate)}</span>
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </ElPopover>
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return [
|
|
|
|
+ simplifiedConfiguration.showCheckbox ?
|
|
|
|
+ <VxeColumn type="checkbox" width={"max-content"}>
|
|
|
|
+ {{
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ header({checked, indeterminate}) {
|
|
|
|
+ return checkboxHeader(checked, indeterminate)
|
|
|
|
+ },
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ checkbox({row, checked, indeterminate}) {
|
|
|
|
+ return <span style={"font-size: 1.1em;cursor: pointer;"}
|
|
|
|
+ onClick={(el) => {
|
|
|
|
+ el.preventDefault()
|
|
|
|
+ el.stopPropagation()
|
|
|
|
+ handelCheckboxChange({row, checked: !checked})
|
|
|
|
+ }}>
|
|
|
|
+ {renderIcon(checked, indeterminate)}
|
|
|
|
+ </span>
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </VxeColumn> : null,
|
|
|
|
+ ...slots.default ? slots.default() : null,
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </VxeTable>
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (simplifiedConfiguration.showPage) {
|
|
|
|
+ return <div class="cy_display_flex_y">
|
|
|
|
+ <div class="cy_flex_1-y">
|
|
|
|
+ <TempVxeTable/>
|
|
|
|
+ </div>
|
|
|
|
+ <div style="height:max-content">
|
|
|
|
+ <VxePager
|
|
|
|
+ onPageChange={(data) => {
|
|
|
|
+ pageChange()
|
|
|
|
+ }}
|
|
|
|
+ size={"small"}
|
|
|
|
+ {...tsxVModel(pageVO, 'currentPage')}
|
|
|
|
+ {...tsxVModel(pageVO, 'pageSize')}
|
|
|
|
+ total={pageVO.total}
|
|
|
|
+ {...simplifiedConfiguration.pagesProps}
|
|
|
|
+ {...pagerProps}
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ return <TempVxeTable/>
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function exportExcel(filename: string = '', sheetName: string = 'sheet1') {
|
|
|
|
+ if (!XEUtils.isString(filename)) {
|
|
|
|
+ filename = ''
|
|
|
|
+ }
|
|
|
|
+ tableRef.value?.openExport({
|
|
|
|
+ filename,
|
|
|
|
+ sheetName,
|
|
|
|
+ types: ['xlsx', 'csv', 'html', 'xml', 'txt'],
|
|
|
|
+ type: 'xlsx',
|
|
|
|
+ useStyle: true,
|
|
|
|
+ original: true,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let queryParamsCache = {}
|
|
|
|
+
|
|
|
|
+ function pageChange() {
|
|
|
|
+ if (XEUtils.isEmpty(queryParamsCache)) {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ const data = {
|
|
|
|
+ ...queryParamsCache,
|
|
|
|
+ ...pageVO
|
|
|
|
+ }
|
|
|
|
+ props.loading = true
|
|
|
|
+ pageQuery(data, false)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function pageQuery(data: any, clearCurrentPage: boolean = true) {
|
|
|
|
+ pagerProps.loading = true
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ simplifiedConfiguration.remoteSearch(data).then(res => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ const data: D[] = XEUtils.get(res, simplifiedConfiguration!.result || 'result') as D[]
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ const total: number = XEUtils.get(res, simplifiedConfiguration!.total || 'total') as number
|
|
|
|
+ props.data = data
|
|
|
|
+ pageVO.total = total
|
|
|
|
+ }).catch((e) => {
|
|
|
|
+ props.data = []
|
|
|
|
+ pageVO.total = 0
|
|
|
|
+ console.error(e)
|
|
|
|
+ }).finally(() => {
|
|
|
|
+ if (clearCurrentPage) {
|
|
|
|
+ pageVO.currentPage = 1
|
|
|
|
+ pageMemory.clear()
|
|
|
|
+ }
|
|
|
|
+ props.loading = false
|
|
|
|
+ pagerProps.loading = false
|
|
|
|
+ handelPageCheckBox()
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const pageMemory = reactive(new Map<any, D>());
|
|
|
|
+
|
|
|
|
+ function getCheckboxRecords() {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ return eachAndReturnList(pageMemory, (item) => {
|
|
|
|
+ return item
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function handelCheckbox() {
|
|
|
|
+ const data = tableRef.value!.getCheckboxRecords()
|
|
|
|
+ if (data.length > 0) {
|
|
|
|
+ XEUtils.arrayEach(tableRef.value!.getCheckboxRecords(), (item) => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ pageMemory.set(item[simplifiedConfiguration.keyField], item)
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ XEUtils.arrayEach(tableRef.value!.getData(), (item) => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ pageMemory.delete(item[simplifiedConfiguration.keyField])
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function handelPageCheckBox() {
|
|
|
|
+ nextTick().then(() => {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ XEUtils.arrayEach(pageMemory, (item) => {
|
|
|
|
+ tableRef.value?.setCheckboxRow(item, true)
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function handelCheckboxChange({row, checked}: { row: any, checked: boolean }) {
|
|
|
|
+ mutation.setCheckboxRow(row, checked)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async function handelCheckboxAll() {
|
|
|
|
+ await tableRef.value?.toggleAllCheckboxRow()
|
|
|
|
+ nextTick().then(() => {
|
|
|
|
+ handelCheckbox()
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function querySearch() {
|
|
|
|
+ props.loading = true
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ if (simplifiedConfiguration.showPage) {
|
|
|
|
+ const pageData = {
|
|
|
|
+ ...pageVO,
|
|
|
|
+ total: 0,
|
|
|
|
+ currentPage: 1
|
|
|
|
+ }
|
|
|
|
+ queryParamsCache = pageData
|
|
|
|
+ pageQuery(pageData)
|
|
|
|
+ } else {
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ simplifiedConfiguration.remoteSearch({...pageVO}).then(res => {
|
|
|
|
+ props.data = res
|
|
|
|
+ }).catch(() => {
|
|
|
|
+ props.data = []
|
|
|
|
+ }).finally(() => {
|
|
|
|
+ props.loading = false
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ function handelLookCheckBox() {
|
|
|
|
+
|
|
|
|
+ const dialog = (
|
|
|
|
+ <CyDialog title="选中详情"
|
|
|
|
+ ignore-error
|
|
|
|
+ body-width="70%"
|
|
|
|
+ {...simplifiedConfiguration.dialogProps}>
|
|
|
|
+ {{
|
|
|
|
+ default: () => {
|
|
|
|
+ const data = getCheckboxRecords()
|
|
|
|
+ return <CyFlex>
|
|
|
|
+ {{
|
|
|
|
+ header: () => {
|
|
|
|
+ try {
|
|
|
|
+ if (simplifiedConfiguration.dialogHeader) {
|
|
|
|
+ const Scc = simplifiedConfiguration?.dialogHeader();
|
|
|
|
+ if (typeof Scc !== 'undefined') {
|
|
|
|
+ return <Scc/>
|
|
|
|
+ } else {
|
|
|
|
+ console.error('dialogHeader需要返回一个组件')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.error('dialogHeader需要返回一个组件', e)
|
|
|
|
+ return null
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ default: () => {
|
|
|
|
+ return <VxeTable data={data}
|
|
|
|
+ rowConfig={{
|
|
|
|
+ keyField: simplifiedConfiguration.keyField,
|
|
|
|
+ isCurrent: true,
|
|
|
|
+ isHover: true,
|
|
|
|
+ useKey: true,
|
|
|
|
+ }}
|
|
|
|
+ scrollY={{enabled: true, gt: 100}}
|
|
|
|
+ showOverflow={data.length > 100}
|
|
|
|
+ height={"100%"}>
|
|
|
|
+ {{
|
|
|
|
+ default: () => {
|
|
|
|
+ return [
|
|
|
|
+ <VxeColumn title={"删除"} width={80} fixed="left">
|
|
|
|
+ {{
|
|
|
|
+ default({row}: any) {
|
|
|
|
+ return <ElButton
|
|
|
|
+ type="danger"
|
|
|
|
+ onClick={(el) => {
|
|
|
|
+ handelCheckboxChange({row, checked: false})
|
|
|
|
+ }}
|
|
|
|
+ >{{default: () => '删除'}}
|
|
|
|
+ </ElButton>
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </VxeColumn>,
|
|
|
|
+ mutation.setTableDefaultSlots()
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </VxeTable>
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+
|
|
|
|
+ </CyFlex>
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ </CyDialog>
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ setDialogToJs(dialog, null).then(() => {
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const mutation = {
|
|
|
|
+ updateByIndex: function updateByIndex(data: D | any, index: number) {
|
|
|
|
+ const tableData = tableRef.value?.getData()
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ tableData[index] = data
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ tableRef.value?.loadData(tableData)
|
|
|
|
+ },
|
|
|
|
+ setCheckboxRow: async function (row: D, checked: boolean) {
|
|
|
|
+ await tableRef.value?.setCheckboxRow(row, checked)
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ const key = XEUtils.get(row, simplifiedConfiguration!.keyField, null) as string | null
|
|
|
|
+ if (key === null) return
|
|
|
|
+ if (checked) {
|
|
|
|
+ pageMemory.set(key, row)
|
|
|
|
+ } else {
|
|
|
|
+ pageMemory.delete(key)
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ clearCheckboxRow() {
|
|
|
|
+ tableRef.value?.clearCheckboxRow()
|
|
|
|
+ pageMemory.clear()
|
|
|
|
+ },
|
|
|
|
+ setTableDefaultSlots(): any {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ onMounted(() => {
|
|
|
|
+ if (simplifiedConfiguration.mountedQuery) {
|
|
|
|
+ nextTick().then(() => {
|
|
|
|
+ querySearch()
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ tableRef,
|
|
|
|
+ CyVxeTable: CyVxeTable,
|
|
|
|
+ tableProps: props,
|
|
|
|
+ exportExcel,
|
|
|
|
+ mutation,
|
|
|
|
+ querySearch,
|
|
|
|
+ getCheckboxRecords
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export default useVxeTable
|