<script lang="ts" setup name="NonthTemplateTable"> import { ElMessage, ElTable } from 'element-plus' import type { Ref } from 'vue' import { defineExpose, ref } from 'vue' import type { TableColumn } from '@/components/NormalTable/table_interface' import { getDictByCode } from '@/api/system/dict' import type { dictType } from '@/global' import { isMerge, mergeTableRow, useMergeTableRow } from '@/commonMethods/useMergeTableRow' // ------------------定义props、 emit------------------- const props = defineProps({ pageType: { type: String, default: 'detail', }, // 数据 data: { type: Array, default() { return [] }, }, // 表格高度 height: { type: Number, default() { return null }, }, // 数据列配置 columns: { type: Array<TableColumn>, default() { return () => [] }, }, // 表格大小 size: { type: String, default: 'default', }, // 表格大小,默认,small,mini等,与el-table条件相同 type: String, // 需要合并的列 needMergeCells: { type: Array, default: () => ([]), }, }) const emit = defineEmits(['change', 'selectionChange', 'rowClick', 'rowDbClick', 'multiSelect', 'filterChange']) // ------------------------------------------字典---------------------------------------------- // ------------------------------------------字典---------------------------------------------- const standardAmplitudeUnitList = ref<dictType[]>([]) // 幅度单位 const standardFrequencyUnitList = ref<dictType[]>([]) // 频率单位-公用 const standard17TypeValueList = ref<dictType[]>([]) // 类型 const standard17DisplayFormatList = ref<dictType[]>([]) // 显示格式 const standard17VSMRList = ref<dictType[]>([]) // vsmr const standard17AmplitudeList = ref<dictType[]>([]) // 幅度 /** * 获取字典 */ function getDict() { // 幅度单位 getDictByCode('standardAmplitudeUnit').then((response) => { standardAmplitudeUnitList.value = response.data }) // 频率单位公用 getDictByCode('standardFrequencyUnit').then((response) => { standardFrequencyUnitList.value = response.data }) // 类型 getDictByCode('standard17TypeValue').then((response) => { standard17TypeValueList.value = response.data }) // 显示格式 getDictByCode('standard17DisplayFormat').then((response) => { standard17DisplayFormatList.value = response.data }) // VSMR getDictByCode('standard17VSMR').then((response) => { standard17VSMRList.value = response.data }) // 幅度 getDictByCode('standard17Amplitude').then((response) => { standard17AmplitudeList.value = response.data }) } getDict() // -------定义数据-------------- interface columnsCheckInfo { text: string show: boolean } const columnsChecked: Ref<columnsCheckInfo[]> = ref([]) const table = ref<InstanceType<typeof ElTable>>() const singleChecked = ref('') // 单选选中id // 初始化列显示状态 function initColumnsState() { columnsChecked.value = [] for (const column of props.columns) { columnsChecked.value.push({ text: column.text, show: !!column.show }) } } watch(() => props.data, (newValue, old) => { console.log('监听到表格变化', newValue) if (!props.needMergeCells.length) { return } if (newValue) { if (Array.isArray(newValue) && newValue?.length) { const data = newValue[newValue.length - 1] if (isMerge(data)) { console.log('isMerge') return } } console.log('要合并了') useMergeTableRow(newValue, props.needMergeCells) } }, { deep: true, immediate: true, }) // 最终展示列 const columnsFiltered: Ref<TableColumn[]> = ref([]) // 切换列 function changeColumns() { columnsFiltered.value = [] for (const i in props.columns) { if (columnsChecked.value[i].show === true) { columnsFiltered.value.push(props.columns[i]) } } } // 刷新 function refresh() { emit('change') } // 多选选中结果 const selectionList = ref([]) function handleSelectionChange(selection: []) { selectionList.value = selection emit('selectionChange', selection) } // 点击行 function rowClick(row: object, column?: any, event?: any) { emit('rowClick', row) } // 双击行 function rowDbClick(row: object, column?: any, event?: any) { emit('rowDbClick', row) } // 监听columns watch(props.columns, (val) => { initColumnsState() changeColumns() }) // 清除多选选中 const clearMulti = () => { console.log('清理选中') table.value!.clearSelection() singleChecked.value = '' } defineExpose({ clearMulti, initColumnsState, }) onBeforeMount(() => { initColumnsState() changeColumns() }) // ------------------------------------表格操作----------------------------------- const randomNum = `${new Date().getTime}${Math.random()}` // 右击当前行操作 const clickIndex = ref(-1) const contextmenu = (row: any, column: any, event: any, index: number) => { if (props.pageType === 'detail') { return } console.log(123) // 阻止默认的右键菜单 event.preventDefault() clickIndex.value = props.data.findIndex(item => item === row) // console.log('右击', clickIndex.value) // 获取自定义菜单元素 var menu = document.getElementById(randomNum) as HTMLElement // 设置自定义菜单的位置并显示 let positionX = event.clientX let positionY = event.clientY if (window.innerHeight - event.clientY < 268) { positionY = window.innerHeight - 268 } else { positionY = event.clientY } if (window.innerWidth - event.clientX < 146) { positionX = window.innerWidth - 146 } else if (event.clientX - 180 < 146) { positionX = 180 } else { positionX = event.clientX - 146 / 2 } menu.style.top = `${positionY}px` menu.style.left = `${positionX}px` menu.style.display = 'block' } // 点击其他位置隐藏自定义菜单 document.addEventListener('click', () => { if (props.pageType === 'detail') { return } if (document.getElementById(randomNum)) { (document.getElementById(randomNum) as HTMLElement).style.display = 'none' } }) const mouseoutTable = () => { console.log('鼠标移出') if (props.pageType === 'detail') { return } if (document.getElementById(randomNum)) { (document.getElementById(randomNum) as HTMLElement).style.display = 'none' } } // 添加行 const costomAddRow = (type: string) => { if (type === 'current-pre') { // 当前行前方插入 props.data.splice(clickIndex.value, 0, JSON.parse(JSON.stringify({ ...props.data[clickIndex.value], id: `custom-${new Date().getTime()}` }))) } else if (type === 'current-next') { // 当前行后方方插入 props.data.splice(clickIndex.value + 1, 0, JSON.parse(JSON.stringify({ ...props.data[clickIndex.value], id: `custom-${new Date().getTime()}` }))) } else if (type === 'list-head') { // 列表头行插入 props.data.splice(0, 0, JSON.parse(JSON.stringify({ ...props.data[clickIndex.value], id: `custom-${new Date().getTime()}` }))) } else if (type === 'list-tail') { // 列表尾行插入 props.data.splice(props.data.length, 0, JSON.parse(JSON.stringify({ ...props.data[clickIndex.value], id: `custom-${new Date().getTime()}` }))) } else if (type === 'select-pre') { // 选中行前方插入 if (!selectionList.value.length) { ElMessage.warning('未选择数据') return } selectionList.value.forEach((item, index) => { const dataIndex = props.data.findIndex(citem => item === citem) props.data.splice(dataIndex, 0, JSON.parse(JSON.stringify({ ...item, id: `custom-${new Date().getTime()}` }))) }) table.value!.clearSelection() } else if (type === 'select-next') { // 选中行后方插入 if (!selectionList.value.length) { ElMessage.warning('未选择数据') return } selectionList.value.forEach((item, index) => { const dataIndex = props.data.findIndex(citem => item === citem) props.data.splice(dataIndex + 1, 0, JSON.parse(JSON.stringify({ ...item, id: `custom-${new Date().getTime()}` }))) }) table.value!.clearSelection() } else if (type === 'del-current') { props.data.splice(clickIndex.value, 1) } else if (type === 'del-select') { if (!selectionList.value.length) { ElMessage.warning('未选择数据') return } selectionList.value.forEach((item, index) => { const dataIndex = props.data.findIndex(citem => item === citem) props.data.splice(dataIndex, 1) }) table.value!.clearSelection() } clickIndex.value = -1 } </script> <template> <div @mouseleave="mouseoutTable"> <el-table id="print" ref="table" :data="data" :height="data.length > 10 ? 500 : null" border stripe :size="size" style="width: 100%;" :span-method="mergeTableRow" @selection-change="handleSelectionChange" @row-click="rowClick" @row-dblclick="rowDbClick" @row-contextmenu="contextmenu" > <el-table-column v-if="pageType !== 'detail'" type="selection" width="38" /> <el-table-column align="center" label="序号" width="80" type="index" /> <el-table-column v-for="item in columns" :key="item.value" :prop="item.value" :label="item.text" :width="item.width" align="center" :show-overflow-tooltip="true" > <template #header> <span v-show="item.required" style="color: red;">*</span><span>{{ item.text }}</span> </template> <template #default="scope"> <!-- number --> <precision-input-number v-if="props.pageType !== 'detail' && (item.value === 'ifBandwidth' || item.value === 'frequency' || item.value === 'upperFrequency' || item.value === 'lowerFrequency' || item.value === 'outputPower' || item.value === 'inputPower' || item.value === 'fundamentalFrequency' || item.value === 'spectrumLevel' || item.value === 'intermediateFrequency' || item.value === 'span' || item.value === 'resolutionBandwidth' || item.value === 'fundamentalAmplitude' || item.value === 'sidebandFrequency' || item.value === 'rbwValue' || item.value === 'vbwValue' || item.value === 'randomNoiseCorrectValue' || item.value === 'standardValue')" v-model="scope.row[item.value]" :placeholder="`${item.text}`" class="full-width-input" :disabled="props.pageType === 'detail'" /> <!-- urel --> <el-input v-if="props.pageType !== 'detail' && (item.value === 'urel' || (item.value === 'flatnessUrel' && props.type !== '增益、增益平坦度') || item.value === 'maxLossUrel' || item.value === 'minLossUrel') && (props.type !== '电压驻波比(矢量网络分析仪)' && props.type !== '传输幅度' && props.type !== '传输相移')" v-model="scope.row[item.value]" :placeholder="`${item.text}`" class="full-width-input" :disabled="props.pageType === 'detail'" /> <!-- 科学计数法 --> <scientific-notation v-if="props.pageType !== 'detail' && (item.value === 'urel' && (props.type === '电压驻波比(矢量网络分析仪)' || props.type === '传输幅度' || props.type === '传输相移'))" v-model="scope.row[item.value]" :placeholder="`${item.text}`" class="full-width-input" :disabled="props.pageType === 'detail'" /> <!-- 增益、增益平坦度de增益平坦度U(k=2) --> <el-input v-if="props.pageType !== 'detail' && (item.value === 'flatnessUrel' && props.type === '增益、增益平坦度') && scope.$index === 0" v-model="scope.row[item.value]" :placeholder="`${item.text}`" class="full-width-input" :disabled="props.pageType === 'detail'" /> <!-- 频率 单位--公用 --> <el-select v-if="props.pageType !== 'detail' && (item.value === 'ifBandwidthUnit' || item.value === 'frequencyUnit' || item.value === 'lowerFrequencyUnit' || item.value === 'upperFrequencyUnit' || item.value === 'fundamentalFrequencyUnit' || item.value === 'intermediateFrequencyUnit' || item.value === 'resolutionBandwidthUnit' || item.value === 'rbwValueUnit' || item.value === 'vbwValueUnit' || item.value === 'sidebandFrequencyUnit')" v-model="scope.row[item.value]" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standardFrequencyUnitList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> <!-- 幅度单位--公用 --> <el-select v-if="props.pageType !== 'detail' && (item.value === 'outputPowerUnit' || item.value === 'inputPowerUnit' || item.value === 'spectrumLevelUnit' || item.value === 'fundamentalAmplitudeUnit' || item.value === 'randomNoiseCorrectValueUnit')" v-model="scope.row[item.value]" :placeholder="`${item.text}`" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standardAmplitudeUnitList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> <!-- 类型 --> <el-select v-if="props.pageType !== 'detail' && item.value === 'typeValue'" v-model="scope.row[item.value]" :placeholder="`${item.text}`" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standard17TypeValueList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> <!-- 显示格式 --> <el-select v-if="props.pageType !== 'detail' && item.value === 'displayFormat'" v-model="scope.row[item.value]" :placeholder="`${item.text}`" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standard17DisplayFormatList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> <!-- 谐波次数 --> <select-by-dict v-if="item.value === 'harmonicNumber'" v-model:model-value="scope.row[item.value]" placeholder="谐波次数" dict-code="standard17HarmonicNumber" :disabled="props.pageType === 'detail'" /> <!-- VSMR --> <el-select v-if="props.pageType !== 'detail' && item.value === 'vsmr'" v-model="scope.row[item.value]" :placeholder="`${item.text}`" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standard17VSMRList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> <!-- 技术指标 --> <div v-if="item.value === 'technicalIndex'" style="display: flex;align-items: center;justify-content: center;"> <span style="margin-right: 10px;">±</span> <precision-input-number v-if="props.pageType !== 'detail'" v-model="scope.row[item.value]" :placeholder="`${item.text}`" class="full-width-input" controls-position="right" :disabled="props.pageType === 'detail'" /> <span v-if="props.pageType === 'detail'">{{ scope.row[item.value] }}</span> </div> <!-- 幅度 --> <el-select v-if="props.pageType !== 'detail' && item.value === 'amplitude'" v-model="scope.row[item.value]" :placeholder="`${item.text}`" :disabled="props.pageType === 'detail'" class="full-width-input" > <el-option v-for="item of standard17AmplitudeList" :key="item.value" :label="item.name" :value="item.name" /> </el-select> </template> </el-table-column> </el-table> <!-- 自定义菜单 --> <div :id="randomNum" class="custom-menu"> <p class="menu-item" @click="costomAddRow('current-pre')"> 当前行前方插入 </p> <p class="menu-item" @click="costomAddRow('current-next')"> 当前行后方插入 </p> <p class="menu-item" @click="costomAddRow('list-head')"> 列表头行插入 </p> <p class="menu-item" @click="costomAddRow('list-tail')"> 列表尾行插入 </p> <p v-if="pageType !== 'detail'" class="menu-item" @click="costomAddRow('select-pre')"> 选中行前方插入 </p> <!-- --> <p v-if="pageType !== 'detail'" class="menu-item" @click="costomAddRow('select-next')"> 选中行后方插入 </p> <p class="menu-item" @click="costomAddRow('del-current')"> 删除当前行 </p> <p v-if="pageType !== 'detail'" class="menu-item" @click="costomAddRow('del-select')"> 删除选中行 </p> </div> </div> </template> <style lang="scss" scoped> .single-table { width: 100%; :deep(.el-table th.el-table__cell:nth-child(1) .cell) { visibility: hidden; } } .unit-class { margin-left: 0; white-space: nowrap; } .unit-class-margin-left { margin-left: 10px; white-space: nowrap; } </style> <style lang="scss"> .normal-table { .el-radio__label { display: none !important; } } </style> <style lang="scss" scoped> .custom-menu { display: none; position: fixed; background-color: #fff; border-radius: 5px; padding: 5px 0; z-index: 1000; border: 1px solid #c8c9cc; box-shadow: 0 0 12px rgb(0 0 0 / 12%); .menu-item { display: flex; align-items: center; white-space: nowrap; list-style: none; line-height: 22px; padding: 5px 16px; margin: 0; // font-size: var(--el-font-size-base); color: #606266; cursor: pointer; outline: none; &:hover { background-color: #ecf5ff; color: #409eff; } } } </style>