1
yyb
17 小时以前 69b917fa605be8ccd0984e5c095f24d6476dce95
src/components/DynamicTable/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,402 @@
<template>
  <div class="dynamic-table-container">
    <el-table
      ref="tableRef"
      v-loading="loading"
      :data="tableData"
      :border="border"
      :height="height"
      :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      @row-click="handleRowClick"
    >
      <!-- é€‰æ‹©åˆ— -->
      <el-table-column
        v-if="showSelection"
        align="center"
        type="selection"
        width="55"
      />
      <!-- åºå·åˆ— -->
      <el-table-column
        v-if="showIndex"
        align="center"
        label="序号"
        type="index"
        width="60"
      />
      <!-- å›ºå®šåˆ—:部门 -->
      <el-table-column
        label="部门"
        prop="department"
        width="120"
        show-overflow-tooltip
        align="center"
      />
      <!-- å›ºå®šåˆ—:姓名 -->
      <el-table-column
        label="姓名"
        prop="name"
        width="100"
        show-overflow-tooltip
        align="center"
      />
      <!-- å›ºå®šåˆ—:工号 -->
      <el-table-column
        label="工号"
        prop="employeeId"
        width="100"
        show-overflow-tooltip
        align="center"
      />
      <!-- åŠ¨æ€åˆ—ï¼šæ ¹æ®å­—å…¸æ¸²æŸ“ -->
      <el-table-column
        v-for="(dictItem, index) in dynamicColumns"
        :key="dictItem.value"
        :label="dictItem.label"
        :prop="dictItem.value"
        :width="dictItem.width || 120"
        show-overflow-tooltip
        align="center"
      >
        <template #default="scope">
          <!-- æ ¹æ®å­—典类型渲染不同的显示方式 -->
          <template v-if="dictItem.renderType === 'tag'">
            <el-tag
              :type="getTagType(scope.row[dictItem.value])"
              size="small"
            >
              {{ getDictValueLabel(dictItem.dictType, scope.row[dictItem.value]) }}
            </el-tag>
          </template>
          <template v-else-if="dictItem.renderType === 'select'">
            <el-select
              v-model="scope.row[dictItem.value]"
              placeholder="请选择"
              size="small"
              @change="handleSelectChange(scope.row, dictItem.value, $event)"
            >
              <el-option
                v-for="option in dictItem.options"
                :key="option.value"
                :label="option.label"
                :value="option.value"
              />
            </el-select>
          </template>
          <template v-else-if="dictItem.renderType === 'input'">
            <el-input
              v-model="scope.row[dictItem.value]"
              size="small"
              placeholder="请输入"
              @blur="handleInputChange(scope.row, dictItem.value, $event)"
            />
          </template>
          <template v-else>
            <span>{{ getDictValueLabel(dictItem.dictType, scope.row[dictItem.value]) }}</span>
          </template>
        </template>
      </el-table-column>
      <!-- æ“ä½œåˆ— -->
      <el-table-column
        v-if="showActions"
        label="操作"
        width="150"
        align="center"
        fixed="right"
      >
        <template #default="scope">
          <el-button
            type="primary"
            link
            size="small"
            @click="handleEdit(scope.row, scope.$index)"
          >
            ç¼–辑
          </el-button>
          <el-button
            type="danger"
            link
            size="small"
            @click="handleDelete(scope.row, scope.$index)"
          >
            åˆ é™¤
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- åˆ†é¡µç»„ä»¶ -->
    <div v-if="showPagination" class="pagination-container">
      <el-pagination
        v-model:current-page="pagination.current"
        v-model:page-size="pagination.size"
        :page-sizes="[10, 20, 50, 100]"
        :total="pagination.total"
        layout="total, sizes, prev, pager, next, jumper"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useDict } from '@/utils/dict'
// å®šä¹‰ç»„件属性
const props = defineProps({
  // è¡¨æ ¼æ•°æ®
  data: {
    type: Array,
    default: () => []
  },
  // å­—典类型数组,用于动态生成列
  dictTypes: {
    type: Array,
    default: () => []
  },
  // æ˜¯å¦æ˜¾ç¤ºé€‰æ‹©åˆ—
  showSelection: {
    type: Boolean,
    default: false
  },
  // æ˜¯å¦æ˜¾ç¤ºåºå·åˆ—
  showIndex: {
    type: Boolean,
    default: true
  },
  // æ˜¯å¦æ˜¾ç¤ºæ“ä½œåˆ—
  showActions: {
    type: Boolean,
    default: false
  },
  // æ˜¯å¦æ˜¾ç¤ºåˆ†é¡µ
  showPagination: {
    type: Boolean,
    default: false
  },
  // è¡¨æ ¼é«˜åº¦
  height: {
    type: [String, Number],
    default: 'auto'
  },
  // æ˜¯å¦æ˜¾ç¤ºè¾¹æ¡†
  border: {
    type: Boolean,
    default: true
  },
  // åŠ è½½çŠ¶æ€
  loading: {
    type: Boolean,
    default: false
  },
  // åˆ†é¡µé…ç½®
  pagination: {
    type: Object,
    default: () => ({
      current: 1,
      size: 10,
      total: 0
    })
  }
})
// å®šä¹‰äº‹ä»¶
const emit = defineEmits([
  'selection-change',
  'row-click',
  'edit',
  'delete',
  'select-change',
  'input-change',
  'size-change',
  'current-change'
])
// å“åº”式数据
const tableRef = ref(null)
const tableData = ref([])
// èŽ·å–å­—å…¸æ•°æ®
const dictData = ref({})
// åŠ¨æ€åˆ—é…ç½®
const dynamicColumns = computed(() => {
  const columns = []
  props.dictTypes.forEach(dictType => {
    const dictItems = dictData.value[dictType] || []
    // ä¸ºæ¯ä¸ªå­—典类型创建一个列,而不是为每个字典项创建列
    if (dictItems.length > 0) {
      columns.push({
        label: getDictLabel(dictType), // èŽ·å–å­—å…¸ç±»åž‹çš„æ˜¾ç¤ºåç§°
        value: dictType, // ä½¿ç”¨å­—典类型作为字段名
        width: 120,
        renderType: 'tag', // é»˜è®¤ä½¿ç”¨æ ‡ç­¾æ˜¾ç¤º
        options: dictItems, // æä¾›é€‰é¡¹
        dictType: dictType
      })
    }
  })
  return columns
})
// èŽ·å–å­—å…¸ç±»åž‹çš„æ˜¾ç¤ºåç§°
const getDictLabel = (dictType) => {
  const labelMap = {
    'sys_normal_disable': '状态',
    'sys_user_level': '级别',
    'sys_user_position': '职位',
    'sys_yes_no': '是否',
    'sys_user_sex': '性别',
    'sys_lavor_issue': '劳务问题'  // æ·»åŠ åŠ³åŠ¡é—®é¢˜å­—å…¸
  }
  return labelMap[dictType] || dictType
}
// èŽ·å–å­—å…¸æ•°æ®
const loadDictData = async () => {
  try {
    const dictPromises = props.dictTypes.map(async (dictType) => {
      const { getDicts } = await import('@/api/system/dict/data')
      const response = await getDicts(dictType)
      return {
        type: dictType,
        data: response.data.map(item => ({
          label: item.dictLabel,
          value: item.dictValue,
          elTagType: item.listClass,
          elTagClass: item.cssClass
        }))
      }
    })
    const results = await Promise.all(dictPromises)
    results.forEach(result => {
      dictData.value[result.type] = result.data
    })
  } catch (error) {
    console.error('加载字典数据失败:', error)
    // å¦‚果字典加载失败,使用默认数据
    props.dictTypes.forEach(dictType => {
      if (!dictData.value[dictType]) {
        dictData.value[dictType] = []
      }
    })
  }
}
// èŽ·å–æ ‡ç­¾ç±»åž‹
const getTagType = (value) => {
  // æ ¹æ®å€¼è¿”回不同的标签类型
  if (value === '1' || value === 'true' || value === '是') return 'success'
  if (value === '0' || value === 'false' || value === '否') return 'danger'
  if (value === '2' || value === 'warning') return 'warning'
  return 'info'
}
// èŽ·å–å­—å…¸å€¼çš„æ ‡ç­¾
const getDictValueLabel = (dictType, value) => {
  if (!value) return '-'
  const dictItems = dictData.value[dictType] || []
  const item = dictItems.find(item => item.value === value)
  return item ? item.label : value
}
// äº‹ä»¶å¤„理函数
const handleSelectionChange = (selection) => {
  emit('selection-change', selection)
}
const handleRowClick = (row, column, event) => {
  emit('row-click', row, column, event)
}
const handleEdit = (row, index) => {
  emit('edit', row, index)
}
const handleDelete = (row, index) => {
  emit('delete', row, index)
}
const handleSelectChange = (row, prop, value) => {
  emit('select-change', row, prop, value)
}
const handleInputChange = (row, prop, event) => {
  emit('input-change', row, prop, event.target.value)
}
const handleSizeChange = (size) => {
  emit('size-change', size)
}
const handleCurrentChange = (current) => {
  emit('current-change', current)
}
// ç›‘听数据变化
watch(() => props.data, (newData) => {
  tableData.value = newData
}, { immediate: true })
// ç›‘听字典类型变化
watch(() => props.dictTypes, () => {
  loadDictData()
}, { immediate: true })
// ç»„件挂载时加载字典数据
onMounted(() => {
  loadDictData()
})
// æš´éœ²æ–¹æ³•给父组件
defineExpose({
  tableRef,
  getSelection: () => tableRef.value?.getSelectionRows() || [],
  clearSelection: () => tableRef.value?.clearSelection(),
  toggleRowSelection: (row, selected) => tableRef.value?.toggleRowSelection(row, selected),
  setCurrentRow: (row) => tableRef.value?.setCurrentRow(row)
})
</script>
<style scoped>
.dynamic-table-container {
  width: 100%;
}
.pagination-container {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
}
:deep(.el-table .el-table__header-wrapper th) {
  background-color: #F0F1F5 !important;
  color: #333333;
  font-weight: 600;
}
:deep(.el-table .el-table__body-wrapper td) {
  padding: 8px 0;
}
:deep(.el-select) {
  width: 100%;
}
:deep(.el-input) {
  width: 100%;
}
</style>