Newer
Older
xc-business-system / src / views / business / measure / item / components / second / templateTable.vue
<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> -->