<script lang="ts" setup name="NonthTemplateTable">
import type { Ref } from 'vue'
import { ElMessage, ElTable } from 'element-plus'
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 { useMergeCells } from '@/commonMethods/useMergeCells'
import { initTableRow, isMerge, mergeTableRow, useMergeTableRow } from '@/commonMethods/useMergeTableRow'
// import { } from '@/commonMethods/useMergeTableRow'
// ------------------定义props、 emit-------------------
const props = defineProps({
show: {
type: Boolean,
default: true,
},
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条件相同
// 标题
title: {
type: String,
default: '',
},
// 排序
index: {
type: String,
default: '',
},
// 按钮展示
showBtn: {
type: Boolean,
default: false,
},
// 计算机结果按钮
calcBtn: {
type: Boolean,
default: false,
},
// 自定义内容
customContent: {
type: Boolean,
default: false,
},
// 下拉框内容
selectAllList: {
type: Object,
default: () => ({}),
},
// 是否展示多选列
isMulti: {
type: Boolean,
default: true,
},
// 标题
showTitle: {
type: Boolean,
default: false,
},
// 需要合并的列
needMergeCells: {
type: Array,
default: () => ([]),
},
// 自定义下拉框内容
customSelect: {
type: Boolean,
default: false,
},
// 是否要右击菜单
needContextmenu: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['change', 'selectionChange', 'rowClick', 'rowDbClick', 'multiSelect', 'filterChange', 'addRow', 'delRow', 'changeLoadSituationa', 'calculateResult', 'disabled', 'disabledItem', 'customSelect'])
// ------------------------------------------字典----------------------------------------------
// -------定义数据--------------
interface columnsCheckInfo {
text: string
show: boolean
}
const $route = useRoute()
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 })
}
}
// 最终展示列
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')
}
// 多选选中结果
function selectionChange(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 selectList = ref<any[]>([])
// 多选
const handleSelectionChange = (val: any) => {
selectList.value = val
emit('selectionChange', val)
}
// 清除多选选中
const clearMulti = () => {
console.log('清理选中')
table.value!.clearSelection()
singleChecked.value = ''
}
// 删除行
const delRow = (selectList: any[], list: any[], title: string) => {
emit('delRow', selectList, list, title)
}
// 添加行
const addRow = (list: any[], title: string, index: string) => {
emit('addRow', list, title, index)
}
// 下拉框
const getAnySelect = (text: string, title: string, scope: any, column: any) => {
let result = [] as any
if (props.customSelect) {
emit('customSelect', { text, title, scope, column }, (value: any) => {
result = value
})
return result
}
return props.selectAllList[text]
}
// 下拉框禁用情况
const disabled = (scope: any, column: any) => {
let result = false
emit('disabled', { scope, column }, (value: boolean) => {
result = value
})
return result
}
// 下拉框某一项禁用情况
// citem 当前下拉框选项
// const disabledItem = (scope: any, column: any, citem: any) => {
// let result = false
// emit('disabledItem', { scope, column, citem }, (value: boolean) => {
// result = value
// })
// return result
// }
// 下拉框获取焦点 或者 下拉框的值发生变化
const changeLoadSituationa = (value: any, index: number, text: string, type: string, list: any[], title: string) => {
emit('changeLoadSituationa', value, index, text, type, list, title)
}
// 计算结果
const calculateResult = (title: string, index: string) => {
emit('calculateResult', title, index)
}
// 获取字典value
const getDictValue = (value1: any, value: any, index: number, text: string, type: string, list: any[], title: string) => {
console.log(value, 'value') //
// console.log(value, 'valuevaluevalue')
emit('changeLoadSituationa', value1, index, text, type, list, title)
}
// function spanMethod({ rowIndex, columnIndex }: { rowIndex: number; columnIndex: number }) {
// if (!props.needMergeCells.length) { return }
// return useMergeCells(props.data, props.needMergeCells, rowIndex, columnIndex)
// }
watch(() => props.data, (newValue, old) => {
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,
})
defineExpose({
clearMulti, initColumnsState,
})
onBeforeMount(() => {
initColumnsState()
changeColumns()
})
const randomNum = `${new Date().getTime}${Math.random()}`
// const inputBlur = (value: any, index: number, attribute: string) => {
// if (!String(value)) {
// props.data[index][attribute] = '0'
// }
// }
// 右击当前行操作
const clickIndex = ref(-1)
const contextmenu = (row: any, column: any, event: any, index: number) => {
if (props.pageType === 'detail' || !($route.path.includes('measureItem') || $route.path.includes('standardEquipmentConfig'))) { return }
// 阻止默认的右键菜单
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' || !($route.path.includes('measureItem') || $route.path.includes('standardEquipmentConfig'))) { return }
if (document.getElementById(randomNum)) {
(document.getElementById(randomNum) as HTMLElement).style.display = 'none'
}
})
const mouseoutTable = () => {
console.log('鼠标移出')
if (props.pageType === 'detail' || !($route.path.includes('measureItem') || $route.path.includes('standardEquipmentConfig'))) { 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 (!selectList.value.length) {
ElMessage.warning('未选择数据')
return
}
selectList.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 (!selectList.value.length) {
ElMessage.warning('未选择数据')
return
}
selectList.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 (!selectList.value.length) {
ElMessage.warning('未选择数据')
return
}
selectList.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>
<detail-block title="" @mouseleave="mouseoutTable">
<div style="display: flex;justify-content: space-between;">
<span v-if="props.showTitle" style="padding: 0 10px;">{{ props.title }}</span>
<!-- 是否可选等一些条件 -->
<slot name="custom-check" />
<div style="padding: 0 10px;padding-bottom: 10px;display: flex;">
<!-- 是否可选等一些条件 -->
<slot v-if="props.show" name="custom-right" />
<el-button v-if="props.showBtn && props.show" type="primary" @click="addRow(props.data, props.title, props.index)">
增加行
</el-button>
<el-button v-if="props.showBtn && props.show" type="info" @click="delRow(selectList, props.data, props.title)">
删除行
</el-button>
<el-button v-if="props.calcBtn && props.show" type="primary" @click="calculateResult(props.title, props.index)">
计算结果
</el-button>
<!-- 自定义按钮 -->
<slot name="custom-btn" />
</div>
</div>
<slot name="custom-container" />
<el-table
v-if="props.show"
id="print"
ref="table"
:data="data"
:height="data.length > 10 ? 500 : null"
border
stripe
:span-method="mergeTableRow"
:size="size"
style="width: 100%;"
@selection-change="handleSelectionChange"
@row-click="rowClick"
@row-dblclick="rowDbClick"
@row-contextmenu="contextmenu"
>
<el-table-column v-if="props.isMulti " 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"
>
<template #header>
<span v-show="item.required" style="color: red;">*</span><span>{{ item.text }}</span>
</template>
<template #default="scope">
<div style="display: flex;align-items: center;text-align: center;justify-content: center;">
<!-- 输入框/选择器等 前面插槽 -->
<slot name="pre-content" :scope="scope.row" :column="item" :index="scope.$index" />
<!-- 自定义内容 -->
<slot v-if="item.customContent" name="custom-content" :scope="scope.row" :column="item" :index="scope.$index" />
<template v-if="!item.customContent">
<!-- 文本内容 -->
<span v-if="item.type === 'text'" style="text-align: center;">
{{ scope.row[item.value] }}
</span>
<!-- 输入框 -->
<el-input
v-if="item.type === 'input'" v-model="scope.row[item.value]"
:placeholder="`${item.text}`" style="width: 100%;"
:disabled="disabled(scope, item)"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
/>
<!-- 数字输入框 -->
<precision-input-number
v-if="item.type === 'number'" v-model="scope.row[item.value]"
:placeholder="`${item.text}`" style="width: 100%;" controls-position="right"
:disabled="disabled(scope, item)" :precision="item.precision || 0"
:value-on-clear="item.required ? 0 : ''"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
/>
<!-- <el-input
v-if="item.type === 'number'" v-model="scope.row[item.value]" type="number" :placeholder="`${item.text}`" style="width: 100%;" controls-position="right"
:disabled="disabled(scope, item)" :precision="item.precision"
:value-on-clear="0"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
@blur="inputBlur(scope.row[item.value], scope.$index, item.value)"
/> -->
<!-- 下拉框 -->
<el-select
v-if="item.type === 'select'" v-model="scope.row[item.value]"
:placeholder="`${item.text}`" style="width: 100%;"
:disabled="disabled(scope, item)"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
@focus="(value) => changeLoadSituationa(value, scope.$index, item.text, 'focus', props.data, props.title)"
>
<el-option
v-for="citem in getAnySelect(item.text, title, scope, item)"
:key="citem.id"
:label="citem.name"
:disabled="citem.disabled"
:value="citem[item.bind ? item.bind : 'name']"
/>
</el-select>
<!-- 下拉框-多选 -->
<el-select
v-if="item.type === 'select-multi'" v-model="scope.row[item.value]"
multiple
:placeholder="`${item.text}`" style="width: 100%;"
:disabled="disabled(scope, item)"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
@focus="(value) => changeLoadSituationa(value, scope.$index, item.text, 'focus', props.data, props.title)"
>
<el-option
v-for="citem in getAnySelect(item.text, title, scope, item)"
:key="citem.id"
:label="citem.name"
:disabled="citem.disabled"
:value="citem[item.bind ? item.bind : 'name']"
/>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="item.type === 'radio'" v-model="scope.row[item.value]" :disabled="disabled(scope, item)" style="width: 100%;" class="ml-4 radio"
@change="(value) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
>
<el-radio label="1" size="large">
是
</el-radio>
<el-radio label="0" size="large">
否
</el-radio>
</el-radio-group>
<!-- 输入下拉框(与字典联动) -->
<select-by-dict
v-if="item.type === 'select-dict'"
v-model="scope.row[item.value]"
:placeholder="`${item.text}`"
style="width: 100%;"
:disabled="disabled(scope, item)"
:dict-code="item.code"
:dict-data="getAnySelect(item.text, title, scope, item) || []"
:is-data="item.isData"
@get-dict-value="(val) => { getDictValue(scope.row[item.value], val, scope.$index, item.text, 'change', props.data, props.title) }"
/>
<!-- 科学计数法输入框 -->
<scientific-notation
v-if="item.type === 'notation'"
v-model="scope.row[item.value]"
:placeholder="`${item.text}`"
style="width: 100%;"
:disabled="disabled(scope, item)"
:number-digit="item.numberDigit || ''"
:notation-digit="item.notationDigit || '2'"
:required="item.required"
:change="(value: any) => changeLoadSituationa(value, scope.$index, item.text, 'change', props.data, props.title)"
:focus="(value: any) => changeLoadSituationa(value, scope.$index, item.text, 'focus', props.data, props.title)"
:blur="(value: any) => changeLoadSituationa(value, scope.$index, item.text, 'blur', props.data, props.title)"
/>
</template>
<!-- 输入框/选择器等 后面插槽 -->
<slot name="next-content" :scope="scope.row" :column="item" />
</div>
</template>
</el-table-column>
<!-- <slot name="custom-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>
</detail-block>
</template>
<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;
}
}
}
.single-table {
width: 100%;
:deep(.el-table th.el-table__cell:nth-child(1) .cell) {
visibility: hidden;
}
}
.radio {
:deep(.el-radio__label) {
display: block !important;
}
}
</style>
<!-- <style lang="scss">
.normal-table {
.el-radio__label {
display: none !important;
}
}
</style> -->