| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |