From 7ecca2a5e7a9c9ae811b68dc98e05c689b18aca6 Mon Sep 17 00:00:00 2001
From: schangxiang@126.com <schangxiang@126.com>
Date: 周日, 04 5月 2025 15:41:43 +0800
Subject: [PATCH] 222
---
HIAWms/web/src/components/Table/index.vue | 680 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 680 insertions(+), 0 deletions(-)
diff --git a/HIAWms/web/src/components/Table/index.vue b/HIAWms/web/src/components/Table/index.vue
new file mode 100644
index 0000000..45435a0
--- /dev/null
+++ b/HIAWms/web/src/components/Table/index.vue
@@ -0,0 +1,680 @@
+<template>
+ <div
+ ref="informationTableRef"
+ :i="Language.triggerRenderData.i"
+ :class="{
+ 'information-table': true,
+ 'information-table-drag': props.showDarg,
+ 'information-table-border': headBorder,
+ [dragClass ? dragClass : '']: dragClass,
+ }"
+ >
+ <div
+ :style="{
+ height: props.isFooter ? 'calc(100% - 42px)' : '100%',
+ }"
+ >
+ <vxe-table
+ v-bind="$attrs"
+ show-header-overflow
+ show-overflow
+ width="100%"
+ header-row-class-name="information-table-base-header"
+ ref="tableRef"
+ min-height="45px"
+ :row-class-name="rowClassName"
+ :row-style="rowStyle"
+ :cell-style="cellStyle"
+ :empty-text="globalT(emptyText)"
+ :size="props.size || 'small'"
+ :border="border || true"
+ :height="currentHeight"
+ :max-height="currentMaxHeight"
+ :row-config="{
+ isCurrent: true,
+ isHover: true,
+ keyField: props.id || 'id',
+ ...props.rowConfig,
+ }"
+ :data="dataSource"
+ :scroll-y="{ enabled: !!isVScroll, gt: 0 }"
+ :scroll-x="{ enabled: !!isVScroll, gt: 0 }"
+ :sort-config="{
+ remote: isSort,
+ }"
+ @checkbox-all="selectChangeEvent"
+ @checkbox-change="selectChangeEvent"
+ @sort-change="onSortChange"
+ @current-change="clickRowChange"
+ >
+ <!-- 鑷畾涔夊簭鍙凤紝鎷栨嫿锛岄�夋嫨 -->
+ <CustomVxeColumn
+ v-bind="props"
+ v-model:dataSource="dataSource"
+ v-model:contextMenuConfig="contextMenuConfig"
+ :id="props.id || 'id'"
+ ref="vxeColumnRef"
+ @check="onCheck"
+ @change="onRadioChange"
+ :LanguageScopeKey="props.LanguageScopeKey"
+ >
+ <template #[name]="data" v-for="name in Object.keys(slots)">
+ <slot :name="name" v-bind="data"></slot>
+ </template>
+ </CustomVxeColumn>
+ </vxe-table>
+ </div>
+
+ <div @click="onClickFooter" v-if="isFooter" class="information-table-foot">
+ <!-- <img src="@/assets/images/+.png" style="width: 12px" /> -->
+ <Icon icon="+" :width="12"></Icon>
+ </div>
+ <paginAtion
+ v-bind="props"
+ :params="params"
+ :totalCount="totalCount"
+ :pageSize="props.MaxResultCount || props.pageSize || 50"
+ @change="onChange"
+ @currentChange="onCurrentChange"
+ v-model:pageNum="pageNum"
+ :tableRef="tableRef"
+ />
+
+ <context-menu
+ v-if="contextMenu?.length > 0"
+ v-model:show="contextMenuConfig.show"
+ :options="contextMenuConfig.options"
+ >
+ <template v-for="(item, index) in contextMenu" :key="index">
+ <context-menu-item
+ :label="item.label"
+ @click="onHandleMenuItem(item)"
+ :disabled="!!contextDisabled(item)"
+ :style="{
+ filter: contextDisabled(item) ? 'opacity(0.4)' : 'none',
+ }"
+ >
+ <div
+ :style="{
+ cursor: !contextDisabled(item) ? 'pointer' : 'not-allowed',
+ }"
+ class="table-context-menu-item-c"
+ >
+ <div style="width: 16px; margin-right: 7px">
+ <Icon :height="16" class="icon-box" :icon="item.icon" />
+ </div>
+ <div class="label-c">{{ item.label }}</div>
+ </div>
+ </context-menu-item>
+ </template>
+ </context-menu>
+ </div>
+</template>
+<script lang="ts" setup>
+// @ts-nocheck
+import {
+ computed,
+ nextTick,
+ ref,
+ useSlots,
+ onMounted,
+ onUnmounted,
+ reactive,
+ watch,
+ provide,
+ defineProps,
+ inject,
+ useAttrs,
+} from 'vue'
+import Icon from '../Icon/Icon'
+import { ContextMenu, ContextMenuItem } from '@/components/vue3-context-menu'
+import Sortable from 'sortablejs'
+import isBoolean from 'lodash/isBoolean'
+import isNil from 'lodash/isNil'
+import { getListData, adjustSort } from './api'
+import type {
+ ParamsItem,
+ TablePropsItemType,
+ contextMenuItemType,
+} from './index.d'
+import CustomVxeColumn from './components/custom-vxe-column.vue'
+import paginAtion from './components/pagination.vue'
+import { debounce, isFunction } from 'lodash'
+import VxeTable from 'vxe-table/es/table/index'
+import { getScopeT, Language, _t as globalT } from '@/libs/Language/Language'
+const props = defineProps<TablePropsItemType>()
+const LanguageScopeKey =
+ props.LanguageScopeKey || inject('LanguageScopeKey', '')
+const _t = LanguageScopeKey ? getScopeT(LanguageScopeKey) : globalT
+const attrs = useAttrs()
+const emit = defineEmits([
+ 'drag',
+ 'check',
+ 'sort',
+ 'page',
+ 'rowClick',
+ 'update:dataSource',
+ 'clickFooter',
+ 'update',
+ 'load',
+ 'beforeLoad',
+ 'reload',
+ 'update:total',
+ 'change',
+])
+
+const waitEventFns = ref<any>([])
+
+const baseParams = reactive<any>({
+ SkipCount: 0,
+ MaxResultCount: props.pageSize || 50,
+})
+
+const vxeColumnRef = ref()
+
+const rowClassName = (...arg: any) => {
+ const name = 'information-table-base-row'
+
+ const className = props.rowClassName
+ if (className) {
+ if (isFunction(className)) {
+ return `${name} ${className(...arg) || ''}`
+ }
+ return `${name} ${className || ''}`
+ } else {
+ return `${name} able`
+ }
+}
+
+const params = computed<any>(() => {
+ const p = props.params || {}
+
+ Object.assign(baseParams, p)
+
+ for (let key in baseParams) {
+ if (baseParams[key] === '') {
+ delete baseParams[key]
+ }
+ }
+ return baseParams
+})
+
+const emptyText = computed(() => {
+ return props.emptyText || '鏆傛棤鏁版嵁'
+})
+const contextMenuConfig = ref<contextMenuItemType>({
+ show: false,
+ current: null,
+ options: {
+ zIndex: 2000,
+ minWidth: 132,
+ x: 0,
+ y: 0,
+ },
+})
+const informationTableRef = ref(null)
+const pageNum = ref(1)
+const tableRef = ref()
+const slots = useSlots()
+const total = ref(props.total || 0)
+let dropR: any = null
+
+provide('tableRef', tableRef)
+
+const totalCount = computed({
+ get() {
+ if (!isNil(props.total)) {
+ return props.total
+ } else {
+ return total.value
+ }
+ },
+ set(v) {
+ if (!isNil(props.total)) {
+ emit('update:total', v)
+ } else {
+ total.value = v
+ }
+ },
+})
+
+const contextDisabled: any = computed(() => {
+ return (item: any) => {
+ if (typeof item.disabled === 'function') {
+ return item.disabled(contextMenuConfig.value.current)
+ }
+ if (item.disabled !== undefined) {
+ if (!isNil(item.disabled?.value)) {
+ return item.disabled?.value
+ } else {
+ return item.disabled
+ }
+ }
+ return false
+ }
+})
+
+const dragClass = computed(() => {
+ if (isBoolean(props.showDarg)) return false
+ return props.showDarg
+})
+
+const dataSource = computed({
+ get() {
+ return props.dataSource
+ },
+ set(v) {
+ if (v) {
+ emit('update:dataSource', v)
+ }
+ },
+})
+
+const currentHeight = computed(() => {
+ if (props.height === 'auto') {
+ return ''
+ }
+ return props.maxHeight ? '' : '100%'
+})
+
+const currentMaxHeight = computed(() => {
+ if (props.height === 'auto') {
+ return ''
+ }
+ if (props.isFooter) {
+ if (props.maxHeight) {
+ const h = parseInt(props.maxHeight) - 42
+ return h > 0 ? h : '100%'
+ }
+ return '100%'
+ } else {
+ return props.maxHeight ? props.maxHeight : '100%'
+ }
+})
+/**
+ * 鑿滃崟
+ * @param item
+ */
+const onHandleMenuItem = (item: any) => {
+ item.fn && item.fn(contextMenuConfig.value.current, pageNum.value)
+}
+
+/**
+ * 鐐瑰嚮搴曢儴娣诲姞
+ */
+const onClickFooter = () => {
+ emit('clickFooter')
+}
+
+const onChange = () => {
+ onCurrentChange(Number(pageNum.value))
+}
+/**
+ * 鍗曢�夐�変腑
+ * @param row
+ */
+const onRadioChange = (row: any) => {
+ emit('change', row)
+}
+
+const onCheck = (
+ records: Record<string, any>[],
+ selectionMap: Record<string, boolean>
+) => {
+ emit('check', records, selectionMap)
+}
+
+const selectChangeEvent = (records: any[]) => {
+ // console.log(records, 'records')
+}
+
+/**
+ * 璁剧疆閫変腑
+ * @param keys
+ * @param checked
+ */
+const setSelectRow = (keys: any[], checked = true) => {
+ const $table = tableRef.value
+ if ($table) {
+ const fn = () => {
+ const rows: any[] = []
+ dataSource.value.forEach((item: any) => {
+ if (keys.includes(item[props?.id || 'id'])) {
+ rows.push(item)
+ }
+ })
+ vxeColumnRef.value.setCheckboxRow(rows, checked)
+ emit('check', rows)
+ }
+ waitEventFns.value.push(fn)
+ }
+}
+/**
+ * rows閫変腑
+ * @param rows
+ * @param checked
+ */
+const setSelectRowByObj = (rows: any[], checked = true) => {
+ if (vxeColumnRef) {
+ const fn = () => {
+ vxeColumnRef.value.setCheckboxRow(rows, checked)
+ }
+ waitEventFns.value.push(fn)
+ }
+}
+/**
+ * 璁剧疆閫変腑
+ * @param id
+ */
+const setRadioRowKey = (id) => {
+ vxeColumnRef.value.setRadioRowKey(id)
+}
+/**
+ * 璁剧疆閫変腑
+ * @param row
+ */
+const setRadioRow = (row) => {
+ vxeColumnRef.value.setRadioRow(row)
+}
+
+/**
+ * 璁剧疆鍏ㄩ�夐�変腑鐘舵��
+ * @param checked 閫変腑鐘舵��
+ */
+const setAllCheckboxRow = (checked = false) => {
+ const $table = tableRef.value
+ if ($table) {
+ waitEventFns.value.push(() =>
+ vxeColumnRef.value.selectChangeAllEvent(checked)
+ )
+ }
+}
+
+/**
+ * 璁剧疆鍗曡楂樹寒
+ * @param key
+ */
+const setCurrentRow = (key: string) => {
+ const $table = tableRef.value
+ if ($table) {
+ const fn = () => {
+ const row = dataSource.value.find((item: any) => {
+ return key === item[props?.id || 'id']
+ })
+ if (row) {
+ tableRef.value.setCurrentRow(row)
+ }
+ }
+ waitEventFns.value.push(fn)
+ }
+}
+
+/**
+ * 娓呴櫎閫変腑
+ */
+const clearSelectEvent = () => {
+ const $vxeColumnRef = vxeColumnRef.value
+ if ($vxeColumnRef) {
+ $vxeColumnRef.clearSelection()
+ }
+}
+
+const onSortChange = (row: any) => {
+ const column = props.columns.find((item) => item.field === row.field)
+ if (column) {
+ if (row.order === 'asc') {
+ params.value[column.sortKey] = true
+ } else if (row.order === 'desc') {
+ params.value[column.sortKey] = false
+ } else {
+ delete params.value[column.sortKey]
+ }
+ getTableList()
+ }
+
+ emit('sort', row)
+}
+
+/**
+ * 璁剧疆鍗曢��
+ * @param row
+ */
+const setRow = (row: any) => {
+ const $table = tableRef.value
+ if ($table) {
+ const fn = () => {
+ tableRef.value.setCurrentRow(row)
+ }
+ waitEventFns.value.push(fn)
+ }
+}
+
+const clickRowChange = (tableData: Record<string, any>[]) => {
+ const $table = tableRef.value
+ if ($table) {
+ emit('rowClick', tableData)
+ }
+}
+
+const onCurrentChange = async (current: number) => {
+ pageNum.value = current
+
+ // @ts-ignore
+ params.value.SkipCount = (current - 1) * params.value.MaxResultCount
+ if (props.url) {
+ await getTableList()
+ }
+ emit('page', current)
+}
+
+const sortableInit = () => {
+ const moveDom = tableRef.value?.$el?.querySelector(
+ `.body--wrapper>.vxe-table--body tbody`
+ ) as HTMLElement
+ if (moveDom && Sortable) {
+ dropR = new Sortable(moveDom, {
+ handle: '.drag-move',
+ chosenClass: 'sortable-chosen',
+ swapThreshold: 1,
+ draggable: '.able',
+ animation: 150,
+ onEnd: (sortableEvent: any) => {
+ const newIndex = sortableEvent.newIndex as number
+ const oldIndex = sortableEvent.oldIndex as number
+ const data = [...dataSource.value]
+
+ const currRow = data.splice(oldIndex, 1)[0]
+ data.splice(newIndex, 0, currRow)
+ dataSource.value = []
+ nextTick(async () => {
+ dataSource.value = data
+ if (props.sortUrlTpl) {
+ const sortData = {
+ id: currRow.id,
+ sort:
+ newIndex + 1 + (pageNum.value - 1) * baseParams.MaxResultCount,
+ }
+ await adjustSort(sortData, props.sortUrlTpl)
+ }
+ emit('drag', newIndex, oldIndex, currRow)
+ })
+ },
+ })
+ }
+}
+
+const getTableList = () => {
+ return new Promise(async (r: any) => {
+ const res = await getListData(params.value, props.url || '')
+ if (props.dataTransformer && isFunction(props.dataTransformer)) {
+ dataSource.value =
+ props.dataTransformer(res?.items || res) || res?.items || res
+ } else {
+ dataSource.value = res?.items || res
+ }
+ totalCount.value = res?.totalCount || 0
+ r()
+ emit('reload')
+ })
+}
+
+const getList = (formData?: Record<string, any>) => {
+ return new Promise((resolve) => {
+ if (formData) {
+ Object.assign(params.value, formData)
+ }
+ nextTick(async () => {
+ await getTableList()
+ clearAll()
+ resolve(dataSource.value)
+ })
+ })
+}
+
+/**
+ * 鑾峰彇鍒楄〃鎻愪氦鍙傛暟
+ */
+const getParams = () => {
+ return params.value
+}
+
+/**
+ * 鑾峰彇鍒嗛〉鍙傛暟
+ */
+const getPaginationParams = () => {
+ return {
+ pageNum: pageNum.value,
+ pageSize: params.value.MaxResultCount,
+ totalCount: totalCount.value,
+ }
+}
+/**
+ * 闃熷垪澶勭悊浜嬩欢鍑芥暟
+ */
+const handleEventFns = () => {
+ if (!waitEventFns.value.length || !dataSource.value.length) return
+ nextTick(() => {
+ let fn: () => void = waitEventFns.value.pop()
+ while (fn) {
+ fn()
+ fn = waitEventFns.value.pop()
+ }
+ })
+}
+/**
+ * 璺宠浆
+ */
+const scrollToRowLine = () => {
+ const t = setTimeout(() => {
+ tableRef.value?.scrollToRow(dataSource.value[dataSource.value.length - 1])
+ clearTimeout(t)
+ })
+}
+/**
+ * 璺宠浆
+ */
+const skipRow = (row: any) => {
+ const t = setTimeout(() => {
+ tableRef.value?.scrollToRow(row)
+ tableRef.value.setCurrentRow(row)
+ clearTimeout(t)
+ })
+}
+
+/**
+ * 婊氬姩骞跺畾浣嶅埌閭d竴琛岋紝濡傛湁鍒嗛〉锛岄渶瑕佽烦杞埌鍒嗛〉
+ */
+const scrollToRow = async ({ row, skip }: any) => {
+ if (skip) {
+ if (totalCount.value > dataSource.value.length) {
+ pageNum.value = Math.ceil(totalCount.value / params.value.MaxResultCount)
+ params.value.SkipCount = (pageNum.value - 1) * params.value.MaxResultCount
+ }
+ await getTableList()
+ skipRow(dataSource.value[dataSource.value.length - 1])
+ } else {
+ skipRow(row)
+ }
+}
+
+const clearAll = () => {
+ tableRef.value?.clearAll()
+ vxeColumnRef.value?.clearSelection()
+}
+/**
+ * 娓呴櫎缂撳瓨
+ * 鎵嬪姩娓呴櫎琛ㄦ牸鎵�鏈夋潯浠讹紝杩樺師鍒板垵濮嬬姸鎬�
+ */
+const resetTable = () => {
+ tableRef.value?.clearAll()
+}
+
+Language.useChange((lang: typeof Language) => {
+ if (props.url) {
+ getList()
+ }
+})
+onUnmounted(() => {
+ dropR?.destroy()
+})
+
+onMounted(async () => {
+ emit('beforeLoad')
+ if (props.url) {
+ await getTableList()
+ }
+
+ if (props.selections?.length && dataSource.value.length) {
+ setSelectRow(props.selections)
+ }
+ sortableInit()
+ // 琛ㄦ牸鏁版嵁鍔犺浇瀹屾垚鐨勫洖璋�
+ emit('load')
+})
+
+watch(dataSource, () => {
+ if (Array.isArray(dataSource.value)) {
+ tableRef.value?.reloadData(dataSource.value)
+ if (props.autoFirstClickRow) {
+ if (dataSource.value.length) {
+ const row = dataSource.value[0]
+ setCurrentRow(row[props?.id || 'id'])
+ emit('rowClick', { row })
+ }
+ }
+ nextTick(handleEventFns)
+ }
+})
+
+watch(() => waitEventFns.value.length, debounce(handleEventFns, 100))
+
+const exposeMap = {
+ resetTable,
+ clearAll,
+ setCurrentRow,
+ setSelectRow,
+ setAllCheckboxRow,
+ setRow,
+ clearSelectEvent,
+ getList,
+ getTableList,
+ getParams,
+ getPaginationParams,
+ setSelectRowByObj,
+ scrollToRow,
+ scrollToRowLine,
+ handleEventFns,
+ setRadioRowKey,
+ setRadioRow,
+}
+
+defineExpose(exposeMap)
+
+emit('update', exposeMap)
+</script>
+<style lang="scss">
+@import url('./index.scss');
+</style>
+<style lang="scss" scoped>
+@import url('./index.module.scss');
+</style>
--
Gitblit v1.9.3