<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>