<template>
|
<ElDrawer
|
:model-value="visible"
|
title="字典项管理"
|
size="1100px"
|
destroy-on-close
|
@update:model-value="handleVisibleChange"
|
>
|
<div class="dict-data-panel space-y-4">
|
<div class="text-sm text-[var(--art-text-secondary)]">
|
当前字典:{{ currentDictTypeLabel }}
|
</div>
|
|
<ArtSearchBar
|
v-model="searchForm"
|
:items="searchItems"
|
:showExpand="false"
|
@search="handleSearch"
|
@reset="handleReset"
|
/>
|
|
<ElCard class="art-table-card">
|
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
|
<template #left>
|
<ElSpace wrap>
|
<ElButton v-if="hasAuth('system:dictData:save')" @click="showDialog('add')" v-ripple>
|
新增字典项
|
</ElButton>
|
<ElButton
|
v-if="hasAuth('system:dictData:remove')"
|
type="danger"
|
:disabled="selectedRows.length === 0"
|
@click="handleBatchDelete"
|
v-ripple
|
>
|
{{ t('common.actions.batchDelete') }}
|
</ElButton>
|
<ElButton
|
v-if="hasAuth('system:dictData:list')"
|
:loading="exportLoading"
|
:disabled="loading || exportLoading"
|
@click="handleExport"
|
v-ripple
|
>
|
{{ t('common.actions.export') }}
|
</ElButton>
|
</ElSpace>
|
</template>
|
</ArtTableHeader>
|
|
<ArtTable
|
:loading="loading"
|
:data="data"
|
:columns="columns"
|
:pagination="pagination"
|
@selection-change="handleSelectionChange"
|
@pagination:size-change="handleSizeChange"
|
@pagination:current-change="handleCurrentChange"
|
/>
|
</ElCard>
|
|
<DictDataDialog
|
v-model:visible="dialogVisible"
|
:dict-type-data="dictTypeData"
|
:dict-data="currentDictData"
|
@submit="handleDialogSubmit"
|
/>
|
</div>
|
</ElDrawer>
|
</template>
|
|
<script setup>
|
import { computed, ref, watch } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import { useI18n } from 'vue-i18n'
|
import { useUserStore } from '@/store/modules/user'
|
import { useAuth } from '@/hooks/core/useAuth'
|
import { useTable } from '@/hooks/core/useTable'
|
import { useCrudPage } from '@/views/system/common/useCrudPage'
|
import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
|
import {
|
fetchDeleteDictData,
|
fetchDictDataPage,
|
fetchExportDictDataReport,
|
fetchGetDictDataDetail,
|
fetchSaveDictData,
|
fetchUpdateDictData
|
} from '@/api/system-manage'
|
import DictDataDialog from './dict-data-dialog.vue'
|
import { createDictDataTableColumns } from '../dictDataTable.columns'
|
import {
|
buildDictDataDialogModel,
|
buildDictDataPageQueryParams,
|
buildDictDataSavePayload,
|
buildDictDataSearchParams,
|
createDictDataSearchState,
|
getDictDataPaginationKey,
|
getDictDataStatusOptions,
|
normalizeDictDataListRow
|
} from '../dictDataPage.helpers'
|
|
const props = defineProps({
|
visible: { type: Boolean, default: false },
|
dictTypeData: { type: Object, default: () => ({}) }
|
})
|
|
const emit = defineEmits(['update:visible'])
|
const { t } = useI18n()
|
const { hasAuth } = useAuth()
|
const userStore = useUserStore()
|
const exportLoading = ref(false)
|
const searchForm = ref(createDictDataSearchState(props.dictTypeData))
|
let handleDeleteAction = null
|
|
const currentDictTypeLabel = computed(() => {
|
const code = props.dictTypeData?.code || '-'
|
const name = props.dictTypeData?.name || '未选择'
|
return `${name}(${code})`
|
})
|
|
const searchItems = computed(() => [
|
{
|
label: t('table.keyword'),
|
key: 'condition',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入字典值/标签/备注'
|
}
|
},
|
{
|
label: '字典值',
|
key: 'value',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入字典值'
|
}
|
},
|
{
|
label: '字典标签',
|
key: 'label',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入字典标签'
|
}
|
},
|
{
|
label: t('table.sort'),
|
key: 'sort',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入排序'
|
}
|
},
|
{
|
label: t('table.memo'),
|
key: 'memo',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入备注'
|
}
|
},
|
{
|
label: t('table.status'),
|
key: 'status',
|
type: 'select',
|
props: {
|
clearable: true,
|
options: getDictDataStatusOptions()
|
}
|
}
|
])
|
|
function buildPanelParams(extra = {}) {
|
return buildDictDataPageQueryParams({
|
...searchForm.value,
|
...extra,
|
dictTypeId: props.dictTypeData?.id,
|
dictTypeCode: props.dictTypeData?.code
|
})
|
}
|
|
const {
|
columns,
|
columnChecks,
|
data,
|
loading,
|
pagination,
|
getData,
|
replaceSearchParams,
|
handleSizeChange,
|
handleCurrentChange,
|
refreshData,
|
refreshCreate,
|
refreshUpdate,
|
refreshRemove
|
} = useTable({
|
core: {
|
apiFn: fetchDictDataPage,
|
apiParams: buildPanelParams(),
|
immediate: false,
|
paginationKey: getDictDataPaginationKey(),
|
columnsFactory: () =>
|
createDictDataTableColumns({
|
handleEdit: openEditDialog,
|
handleDelete: hasAuth('system:dictData:remove')
|
? (row) => handleDeleteAction?.(row)
|
: null,
|
t
|
})
|
},
|
transform: {
|
dataTransformer: (records) =>
|
Array.isArray(records) ? records.map((item) => normalizeDictDataListRow(item, t)) : []
|
}
|
})
|
|
const {
|
dialogVisible,
|
dialogType,
|
currentRecord: currentDictData,
|
selectedRows,
|
handleSelectionChange,
|
showDialog,
|
handleDialogSubmit,
|
handleDelete,
|
handleBatchDelete
|
} = useCrudPage({
|
createEmptyModel: () => buildDictDataDialogModel({}, props.dictTypeData),
|
buildEditModel: (record) => buildDictDataDialogModel(record, props.dictTypeData),
|
buildSavePayload: (formData) => buildDictDataSavePayload(formData, props.dictTypeData),
|
saveRequest: fetchSaveDictData,
|
updateRequest: fetchUpdateDictData,
|
deleteRequest: fetchDeleteDictData,
|
entityName: '字典项',
|
resolveRecordLabel: (record) => record?.label || record?.value || record?.id,
|
refreshCreate,
|
refreshUpdate,
|
refreshRemove
|
})
|
handleDeleteAction = handleDelete
|
|
async function openEditDialog(row) {
|
try {
|
currentDictData.value = buildDictDataDialogModel(
|
await fetchGetDictDataDetail(row.id),
|
props.dictTypeData
|
)
|
dialogVisible.value = true
|
dialogType.value = 'edit'
|
} catch (error) {
|
ElMessage.error(error?.message || '获取字典项详情失败')
|
}
|
}
|
|
async function handleExport() {
|
exportLoading.value = true
|
try {
|
const response = await guardRequestWithMessage(
|
fetchExportDictDataReport(buildDictDataSearchParams(buildPanelParams()), {
|
headers: {
|
Authorization: userStore.accessToken || ''
|
}
|
}),
|
null,
|
{
|
timeoutMessage: '字典项导出超时,已停止等待'
|
}
|
)
|
if (!response) {
|
return
|
}
|
if (!response.ok) {
|
throw new Error(`导出失败,状态码:${response.status}`)
|
}
|
|
const blob = await response.blob()
|
const downloadUrl = window.URL.createObjectURL(blob)
|
const link = document.createElement('a')
|
link.href = downloadUrl
|
link.download = 'dict-data.xlsx'
|
document.body.appendChild(link)
|
link.click()
|
link.remove()
|
window.URL.revokeObjectURL(downloadUrl)
|
ElMessage.success('导出成功')
|
} catch (error) {
|
ElMessage.error(error?.message || '导出失败')
|
} finally {
|
exportLoading.value = false
|
}
|
}
|
|
function handleSearch(params) {
|
searchForm.value = {
|
...searchForm.value,
|
...params,
|
dictTypeId: props.dictTypeData?.id,
|
dictTypeCode: props.dictTypeData?.code
|
}
|
replaceSearchParams(buildPanelParams(params))
|
getData()
|
}
|
|
function handleReset() {
|
searchForm.value = createDictDataSearchState(props.dictTypeData)
|
selectedRows.value = []
|
replaceSearchParams(buildPanelParams(searchForm.value))
|
getData()
|
}
|
|
function handleVisibleChange(visible) {
|
emit('update:visible', visible)
|
}
|
|
watch(
|
() => [props.visible, props.dictTypeData?.id],
|
([visible, dictTypeId]) => {
|
if (!visible || !dictTypeId) {
|
return
|
}
|
searchForm.value = createDictDataSearchState(props.dictTypeData)
|
selectedRows.value = []
|
replaceSearchParams(buildPanelParams(searchForm.value))
|
getData()
|
},
|
{ immediate: true }
|
)
|
</script>
|