Newer
Older
xc-business-system / src / components / NormalTable / index.vue
<script lang="ts" setup name="NormalTable">
import type { Ref } from 'vue'
import { ElTable } from 'element-plus'
import { defineExpose, ref } from 'vue'
import type { TableColumn } from './table_interface'
import TableColumnCom from './tableColumn.vue'
// ------------------定义props、 emit-------------------
const props = defineProps({
  // 是否显示多选表格
  isShowmultiSelect: {
    type: Boolean,
    default: false,
  },
  // 是否允许多选
  isMulti: {
    type: Boolean,
    default: true,
  },
  // 查询条件,此处主要需要分页的条件
  query: {
    type: Object,
    default() {
      return {
        offset: 1,
        limit: 20,
      }
    },
  },
  // loading状态
  listLoading: {
    type: Boolean,
    default: false,
  },
  // 数据
  data: {
    type: Array,
    default() {
      return []
    },
  },
  // 表格高度
  height: {
    type: Number,
    default() {
      return null
    },
  },
  // 数据总数
  total: {
    type: Number,
    default: 0,
  },
  // 数据列配置
  columns: {
    type: Array<TableColumn>,
    default() {
      return []
    },
  },
  // 是否需要分页控件
  pagination: {
    type: Boolean,
    default: true,
  },
  // 配置项
  options: {
    type: Object,
    default() {
      return {
        needIndex: true, // 是否需要序号列
        border: true, // 是否需要上方边框
      }
    },
  },
  // 可选单页显示条数
  pageSizes: {
    type: Array,
    default() {
      return [5, 10, 20, 30, 50, 100, 150, 200]
    },
  },
  // 表格大小
  size: {
    type: String,
    default: 'default',
  }, // 表格大小,默认,small,mini等,与el-table条件相同
  // 想要展示成链接的数据

  isCheckAll: {
    type: Boolean,
    default: false,
  },
})
const emit = defineEmits(['change', 'selectionChange', 'handleSecondClickFollowLink', 'rowClick', 'rowDbClick', 'multiSelect', 'filterChange', 'rowDisabled', 'handleClickFollowLink'])
// -------定义数据--------------
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 })
  }
}

// 最终展示列
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 indexMethod(index: number) {
  return props.query.limit * (props.query.offset - 1) + index + 1
}
// 刷新
function refresh() {
  emit('change')
}
// 改变页容量
function handleSizeChange(val: number) {
  window.sessionStorage.setItem('pageSize', `${val}`)
  emit('change', { size: val })
}
// 改变当前页
function handleCurrentChange(val: number) {
  emit('change', { page: val })
}
// 多选选中结果
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 handleSelectionChange = (val: any) => {
  // console.log(val)
  emit('multiSelect', val)
}
// 单选选中
const radioChange = () => {
  const checkValue = props.data.find((item: any) => item.id == singleChecked.value)
  emit('multiSelect', [checkValue])
}
// 清除多选选中
const clearMulti = () => {
  console.log('清理选中')
  table.value!.clearSelection()
  singleChecked.value = ''
}
// elementUI的合并行方法
function mergeTableRow({ row, column, rowIndex, columnIndex }: any) {
  // console.log(row, column, rowIndex, columnIndex);
  const span = `${column.property}-span`
  if (row[span]) {
    return row[span]
  }
}

// 多选框单选功能
const selectClick = (selection: any, row: any) => {
  if (!props.isMulti) {
    console.log('selectClick')
    if (selection.length > 1) {
      const del_row = selection.shift()
      table.value?.toggleRowSelection(del_row, false) // 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
    }
  }
}
// 筛选条件发生变化
const filterChange = (val: { [key: string]: string }) => {
  emit('filterChange', val)
}
const selectable = (row: any, index: number) => {
  let result = true
  // status 接收参数  true 禁用  false 可选
  emit('rowDisabled', row, index, (status: string) => {
    if (status === 'true') {
      result = false
    }
    else if (status === 'false') {
      result = true
    }
    else {
      result = true
    }
  })
  return result
}

const handleClickFollowLink = (row: any, title: string) => {
  emit('handleClickFollowLink', row, title)
}

const handleSecondClickFollowLink = (row: any, title: string) => {
  emit('handleSecondClickFollowLink', row, title)
}

// 默认全部选中
watch(() => props.data, (newValue) => {
  if (props.isCheckAll) {
    newValue.forEach((row) => {
      table.value!.toggleRowSelection(row, true)
    })
    emit('multiSelect', newValue)
  }
}, { immediate: true, deep: true })

onBeforeMount(() => {
  initColumnsState()
  changeColumns()
})

defineExpose({
  clearMulti, initColumnsState, table, singleChecked,
})
</script>

<template>
  <div id="normal-table-com">
    <el-table
      id="print" ref="table" v-loading="listLoading" :data="data" :height="height" border stripe :size="size"
      style="width: 100%;" :row-key="(row: any) => { return row.id || row.index }" :span-method="mergeTableRow"
      @selection-change="handleSelectionChange" @select="selectClick" @row-click="rowClick" @row-dblclick="rowDbClick"
      @filter-change="filterChange"
    >
      <slot name="preColumns" />

      <el-table-column v-if="!isMulti" class="radio" label="" width="36" align="center" fixed>
        <template #default="scope">
          <el-radio
            v-model="singleChecked" :disabled="scope.row.radioDisabled" :label="scope.row.id" class="radio"
            @change="radioChange"
          />
        </template>
      </el-table-column>
      <el-table-column
        v-if="isShowmultiSelect && isMulti" :reserve-selection="true" type="selection" width="38"
        :selectable="selectable" fixed="left"
      />
      <template v-for="column of columns" :key="column.value">
        <el-table-column
          v-if="!column.isFilters" :label="column.text" :prop="column.value" :width="column.width"
          :align="column.align" :show-overflow-tooltip="column.showOverflow ? column.showOverflow : true"
          :fixed="column.fixed"
        >
          <template #default="scope">
            <span
              v-if="!column.filter && !column.isLink"
              :style="column.styleFilter ? column.styleFilter(scope.row) : ''"
            >{{ scope.row[column.value] }}</span>
            <span
              v-if="column.filter && !column.isLink"
              :style="column.styleFilter ? column.styleFilter(scope.row) : ''"
            >{{ column.filter(scope.row) }}</span>
            <div v-if="column.isLink">
              <show-photo v-for="(item, index) in scope.row.fileArr" :key="index" :minio-file-name="item" /><br>
            </div>
            <div v-if="column.followLink">
              <div
                v-for="(item, index) in scope.row.followLinkArr" :key="index"
                @click="handleClickFollowLink(scope.row, item)"
              >
                <span :class="item !== '/' ? 'follow-link' : ''">{{ item }}</span>
              </div>
            </div>
            <div v-if="column.secondFollowLink">
              <div
                v-for="(item, index) in scope.row.secondFollowLinkArr" :key="index"
                @click="handleSecondClickFollowLink(scope.row, item)"
              >
                <span :class="item !== '/' ? 'follow-link' : ''">{{ item }}</span>
              </div>
            </div>
          </template>
        </el-table-column>
        <el-table-column
          v-if="column.isFilters" :label="column.text" :prop="column.value" :width="column.width"
          :align="column.align" :show-overflow-tooltip="column.showOverflow ? column.showOverflow : true"
          :fixed="column.fixed" :column-key="column.value" :filters="column.filters" :filter-multiple="false"
        >
          <template #default="scope">
            <span v-if="!column.filter" :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{
              scope.row[column.value] }}</span>
            <span v-else :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{ column.filter(scope.row)
            }}</span>
          </template>
        </el-table-column>
      </template>

      <slot name="columns" />
    </el-table>
    <div v-if="props.pagination" style="width: 100%;margin-top: 10px;">
      <el-pagination
        :current-page="props.query.offset" :page-sizes="props.pageSizes as number[]"
        :page-size="props.query.limit" :total="props.total" layout="total, sizes, prev, pager, next"
        @size-change="handleSizeChange" @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

<style lang="scss" scoped>
#normal-table-com {
  width: 100%;

  .radio {
    .el-radio__label {
      display: none !important;
    }
  }
}

.single-table {
  width: 100%;

  :deep(.el-table th.el-table__cell:nth-child(1) .cell) {
    visibility: hidden;
  }
}

.follow-link {
  color: #3d7eff;
  cursor: pointer;
  text-decoration: underline;

  &:hover {
    color: #04395e;
  }
}

/* stylelint-disable-next-line no-duplicate-selectors */
// #normal-table-com {

// }
</style>

<!-- <style lang="scss">

</style> -->