747 lines
23 KiB
Vue
747 lines
23 KiB
Vue
<template>
|
|
<div class="rbac-split-layout dict-split-layout">
|
|
<CustomLeftMenu
|
|
class="left-sidebar dict-left-menu"
|
|
:tree-data="dictTypeTreeData"
|
|
:selected-keys="selectedTypeKeys"
|
|
v-model:expanded-keys="expandedKeys"
|
|
v-model:search-keyword="typeSearchKeyword"
|
|
:loading="typeLoading"
|
|
:search-placeholder="t('system.dict.left.searchPlaceholder')"
|
|
:refresh-title="t('system.dict.left.refresh')"
|
|
:add-title="t('system.dict.left.addType')"
|
|
:delete-label="t('system.dict.left.deleteType')"
|
|
:loading-text="t('system.dict.left.loading')"
|
|
:show-method="false"
|
|
:show-node-index="false"
|
|
:show-map-count="true"
|
|
:show-node-code="true"
|
|
:show-node-actions="true"
|
|
:show-pagination="true"
|
|
:page-size="30"
|
|
@search="handleTypeSearch"
|
|
@refresh="() => loadDictTypes(undefined, { loadDetails: false })"
|
|
@add="handleAddType"
|
|
@select="handleTypeSelect"
|
|
@node-click="handleTypeNodeClick"
|
|
>
|
|
<template #node-menu="{ nodeData }">
|
|
<a-doption @click="handleEditType(nodeData)">
|
|
<icon-edit />
|
|
{{ t('system.dict.left.editType') }}
|
|
</a-doption>
|
|
<a-doption
|
|
v-if="nodeData?.id"
|
|
@click="handleDeleteType(nodeData)"
|
|
>
|
|
<icon-delete />
|
|
{{ t('system.dict.left.deleteType') }}
|
|
</a-doption>
|
|
</template>
|
|
</CustomLeftMenu>
|
|
|
|
<div class="right-panel">
|
|
<div class="dict-page-panel">
|
|
<div v-if="!selectedDictType" class="panel-empty">
|
|
<a-empty :description="t('system.dict.left.emptySelect')">
|
|
<template #image>
|
|
<icon-book class="panel-empty-icon" />
|
|
</template>
|
|
<p class="panel-empty-desc">{{ t('system.dict.left.emptySelectDesc') }}</p>
|
|
</a-empty>
|
|
</div>
|
|
|
|
<template v-else>
|
|
<div class="dict-type-header">
|
|
<div class="dict-type-header-main">
|
|
<h3 class="panel-title">{{ displayTypeName }}</h3>
|
|
<a-tag size="small" color="arcoblue" class="type-code-tag">{{ selectedDictType }}</a-tag>
|
|
<span class="type-item-count">
|
|
{{ t('system.dict.panel.itemCount', { count: selectedTypeMeta?.itemCount ?? pagination.total }) }}
|
|
</span>
|
|
</div>
|
|
<a-button type="text" size="small" class="edit-type-btn" @click="handleEditSelectedType">
|
|
<template #icon><icon-edit /></template>
|
|
{{ t('system.dict.panel.editType') }}
|
|
</a-button>
|
|
</div>
|
|
|
|
<div class="table-toolbar">
|
|
<div class="toolbar-left">
|
|
<a-input-search
|
|
v-model="query.keyword"
|
|
allow-clear
|
|
:placeholder="t('system.dict.search.placeholder')"
|
|
style="width: 240px"
|
|
@search="handleSearch"
|
|
@clear="handleSearch"
|
|
/>
|
|
</div>
|
|
<div class="toolbar-right">
|
|
<a-space wrap :size="8">
|
|
<a-button type="primary" @click="handleAdd">
|
|
<template #icon><icon-plus /></template>
|
|
{{ t('system.dict.actions.add') }}
|
|
</a-button>
|
|
<a-button @click="openBatchImportModal">
|
|
<template #icon><icon-upload /></template>
|
|
{{ t('system.dict.actions.batchImport') }}
|
|
</a-button>
|
|
<a-button :loading="exportLoading" @click="handleBatchExport">
|
|
<template #icon><icon-download /></template>
|
|
{{ t('system.dict.actions.batchExport') }}
|
|
</a-button>
|
|
<a-button @click="loadData">
|
|
<template #icon><icon-refresh /></template>
|
|
</a-button>
|
|
<PaginationPro
|
|
:total-count="pagination.total"
|
|
v-model:current-page="pagination.current"
|
|
v-model:page-size="pagination.pageSize"
|
|
@page-change="handlePageChange"
|
|
@page-size-change="handlePageSizeChange"
|
|
/>
|
|
</a-space>
|
|
</div>
|
|
</div>
|
|
|
|
<div ref="tableContainerRef" class="list-wrap">
|
|
<a-spin :loading="listLoading" class="list-spin">
|
|
<HandsontablePro
|
|
:columns="hotColumns"
|
|
:data="dataSource"
|
|
:total="pagination.total"
|
|
:current-page="pagination.current"
|
|
:page-size="pagination.pageSize"
|
|
:show-action-column="true"
|
|
:action-column-position="'end'"
|
|
:action-column-width="100"
|
|
:action-renderer="actionRenderer"
|
|
:custom-cell-renderer="customCellRenderer"
|
|
:stretch-columns="true"
|
|
:height="handsontableHeight"
|
|
:read-only="true"
|
|
@action-click="handleActionClick"
|
|
/>
|
|
</a-spin>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<a-modal
|
|
v-model:visible="modalVisible"
|
|
:title="isEdit ? t('system.dict.modal.editTitle') : t('system.dict.modal.addTitle')"
|
|
:confirm-loading="modalLoading"
|
|
width="560px"
|
|
@ok="handleModalOk"
|
|
>
|
|
<a-form ref="formRef" :model="formState" :rules="formRules" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 16 }">
|
|
<a-form-item :label="t('system.dict.form.dictType')" field="dictType">
|
|
<a-input v-model="formState.dictType" disabled :placeholder="t('system.dict.placeholder.input')" />
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.dict.form.dictSort')" field="dictSort">
|
|
<a-input-number v-model="formState.dictSort" :placeholder="t('system.dict.placeholder.input')" :min="0" :precision="0" style="width: 100%" />
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.dict.form.dictCode')" field="dictCode">
|
|
<a-input v-model="formState.dictCode" :placeholder="t('system.dict.placeholder.input')" />
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.dict.form.dictName')" field="dictName">
|
|
<a-input v-model="formState.dictName" :placeholder="t('system.dict.placeholder.input')" />
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-modal>
|
|
|
|
<a-modal
|
|
v-model:visible="typeModalVisible"
|
|
:title="typeIsEdit ? t('system.dict.typeModal.editTitle') : t('system.dict.typeModal.addTitle')"
|
|
:confirm-loading="typeModalLoading"
|
|
width="520px"
|
|
@ok="handleTypeModalOk"
|
|
>
|
|
<a-form ref="typeFormRef" :model="typeFormState" :rules="typeFormRules" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 16 }">
|
|
<a-form-item :label="t('system.dict.form.dictType')" field="dictType">
|
|
<a-input
|
|
v-model="typeFormState.dictType"
|
|
:disabled="typeIsEdit"
|
|
:placeholder="t('system.dict.placeholder.input')"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.dict.form.typeRemark')" field="remark">
|
|
<a-input v-model="typeFormState.remark" allow-clear :placeholder="t('system.dict.placeholder.typeRemark')" />
|
|
</a-form-item>
|
|
<a-form-item :label="t('system.dict.form.typeSort')" field="typeSort">
|
|
<a-input-number v-model="typeFormState.typeSort" :min="0" :precision="0" style="width: 100%" />
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-modal>
|
|
|
|
<DictBatchImportModal ref="batchImportModalRef" @success="handleImportSuccess" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, reactive, ref, onMounted } from 'vue'
|
|
import dayjs from 'dayjs'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { Message, Modal } from '@arco-design/web-vue'
|
|
import {
|
|
IconPlus,
|
|
IconRefresh,
|
|
IconUpload,
|
|
IconDownload,
|
|
IconEdit,
|
|
IconDelete,
|
|
IconBook,
|
|
} from '@arco-design/web-vue/es/icon'
|
|
import HandsontablePro from '@/components/HandsontablePro/index.vue'
|
|
import PaginationPro from '@/components/PaginationPro/index.vue'
|
|
import CustomLeftMenu from '@/components/CustomLeftMenu/index.vue'
|
|
import type { CustomLeftMenuTreeNode } from '@/components/CustomLeftMenu/types'
|
|
import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
|
import {
|
|
createDict,
|
|
deleteDict,
|
|
deleteDictType,
|
|
getDictList,
|
|
getDictListAll,
|
|
getDictTypeList,
|
|
saveDictType,
|
|
updateDict,
|
|
type DictDto,
|
|
type DictTypeDto,
|
|
} from '@/api/system/dict'
|
|
import { exportDictRowsToExcel } from './dict-batch-import'
|
|
import DictBatchImportModal from './DictBatchImportModal.vue'
|
|
|
|
defineOptions({ name: 'SystemDict' })
|
|
|
|
const { t } = useI18n()
|
|
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
|
const handsontableHeight = computed(() => Math.max(tableHeight.value || 0, 400))
|
|
|
|
const typeLoading = ref(false)
|
|
const listLoading = ref(false)
|
|
const dictTypeList = ref<DictTypeDto[]>([])
|
|
const typeSearchKeyword = ref('')
|
|
const selectedDictType = ref('')
|
|
const expandedKeys = ref<string[]>([])
|
|
|
|
const dataSource = ref<DictDto[]>([])
|
|
const batchImportModalRef = ref<InstanceType<typeof DictBatchImportModal> | null>(null)
|
|
const exportLoading = ref(false)
|
|
const modalVisible = ref(false)
|
|
const modalLoading = ref(false)
|
|
const isEdit = ref(false)
|
|
const editingId = ref<number | string>()
|
|
const formRef = ref()
|
|
|
|
const typeModalVisible = ref(false)
|
|
const typeModalLoading = ref(false)
|
|
const typeIsEdit = ref(false)
|
|
const typeFormRef = ref()
|
|
|
|
const query = reactive({
|
|
keyword: '',
|
|
})
|
|
|
|
const pagination = reactive({
|
|
current: 1,
|
|
pageSize: 30,
|
|
total: 0,
|
|
})
|
|
|
|
const formState = reactive({
|
|
dictType: '',
|
|
dictSort: 1,
|
|
dictCode: '',
|
|
dictName: '',
|
|
})
|
|
|
|
const typeFormState = reactive({
|
|
id: undefined as number | string | undefined,
|
|
dictType: '',
|
|
typeSort: 0,
|
|
remark: '',
|
|
})
|
|
|
|
const formRules = computed(() => ({
|
|
dictType: [{ required: true, message: t('system.dict.rule.dictTypeRequired') }],
|
|
dictSort: [{ required: true, message: t('system.dict.rule.dictSortRequired') }],
|
|
dictCode: [{ required: true, message: t('system.dict.rule.dictCodeRequired') }],
|
|
dictName: [{ required: true, message: t('system.dict.rule.dictNameRequired') }],
|
|
}))
|
|
|
|
const typeFormRules = computed(() => ({
|
|
dictType: [{ required: true, message: t('system.dict.rule.dictTypeRequired') }],
|
|
}))
|
|
|
|
const selectedTypeMeta = computed(() =>
|
|
dictTypeList.value.find((item) => item.dictType === selectedDictType.value),
|
|
)
|
|
|
|
const displayTypeName = computed(
|
|
() => selectedTypeMeta.value?.remark?.trim() || selectedDictType.value,
|
|
)
|
|
|
|
const filteredTypeList = computed(() => {
|
|
const kw = typeSearchKeyword.value.trim().toLowerCase()
|
|
if (!kw) return dictTypeList.value
|
|
return dictTypeList.value.filter((item) => {
|
|
const type = String(item.dictType ?? '').toLowerCase()
|
|
const remark = String(item.remark ?? '').toLowerCase()
|
|
return type.includes(kw) || remark.includes(kw)
|
|
})
|
|
})
|
|
|
|
function buildTypeTreeNode(item: DictTypeDto): CustomLeftMenuTreeNode {
|
|
const dictType = String(item.dictType ?? '').trim()
|
|
const remark = String(item.remark ?? '').trim()
|
|
return {
|
|
key: `type-${dictType}`,
|
|
title: remark || dictType,
|
|
code: remark ? dictType : undefined,
|
|
mapCount: item.itemCount ?? 0,
|
|
isLeaf: true,
|
|
id: item.id,
|
|
dictType,
|
|
remark,
|
|
typeSort: item.typeSort,
|
|
}
|
|
}
|
|
|
|
const dictTypeTreeData = computed<CustomLeftMenuTreeNode[]>(() =>
|
|
filteredTypeList.value.map((item) => buildTypeTreeNode(item)),
|
|
)
|
|
|
|
const selectedTypeKeys = computed(() =>
|
|
selectedDictType.value ? [`type-${selectedDictType.value}`] : [],
|
|
)
|
|
|
|
const hotColumns = computed(() => [
|
|
{ key: 'dictSort', label: t('system.dict.table.dictSort'), width: 100 },
|
|
{ key: 'dictCode', label: t('system.dict.table.dictCode'), width: 180 },
|
|
{ key: 'dictName', label: t('system.dict.table.dictName'), width: 200 },
|
|
{ key: 'createTime', label: t('system.dict.table.createTime'), width: 170 },
|
|
])
|
|
|
|
function syncSelectedTypeItemCount(count: number) {
|
|
if (!selectedDictType.value) return
|
|
const idx = dictTypeList.value.findIndex((item) => item.dictType === selectedDictType.value)
|
|
if (idx < 0) return
|
|
dictTypeList.value[idx] = { ...dictTypeList.value[idx], itemCount: count }
|
|
}
|
|
|
|
function handlePageChange(page: number) {
|
|
pagination.current = page
|
|
loadData()
|
|
}
|
|
|
|
function handlePageSizeChange(size: number) {
|
|
pagination.pageSize = size
|
|
pagination.current = 1
|
|
loadData()
|
|
}
|
|
|
|
const actionEditIcon = '<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px"><path d="m30.48 19.038 5.733-5.734a1 1 0 0 0 0-1.414l-5.586-5.586a1 1 0 0 0-1.414 0l-5.734 5.734m7 7L15.763 33.754a1 1 0 0 1-.59.286l-6.048.708a1 1 0 0 1-1.113-1.069l.477-6.31a1 1 0 0 1 .29-.631l14.7-14.7m7 7-7-7M6 42h36"/></svg>'
|
|
const actionDeleteIcon = '<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px"><path d="M5 11h5.5m0 0v29a1 1 0 0 0 1 1h25a1 1 0 0 0 1-1V11m-27 0H16m21.5 0H43m-5.5 0H32m-16 0V7h16v4m-16 0h16M20 18v15m8-15v15"/></svg>'
|
|
|
|
function customCellRenderer(_row: number, col: number, _value: any, rowData: DictDto): string | null {
|
|
const dataColIndex = col - 1
|
|
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
|
|
const key = hotColumns.value[dataColIndex].key
|
|
if (key === 'createTime') return rowData.createTime ? dayjs(rowData.createTime).format('YYYY-MM-DD HH:mm:ss') : '--'
|
|
return null
|
|
}
|
|
|
|
function actionRenderer(_row: DictDto, rowIndex: number): string {
|
|
const actions = [
|
|
`<span data-action="edit" data-row-index="${rowIndex}" style="cursor:pointer;color:var(--app-primary-color,#165dff);display:inline-flex;" title="${t('system.dict.actions.edit')}">${actionEditIcon}</span>`,
|
|
`<span data-action="delete" data-row-index="${rowIndex}" style="cursor:pointer;color:#f53f3f;display:inline-flex;" title="${t('system.dict.actions.delete')}">${actionDeleteIcon}</span>`,
|
|
]
|
|
return `<div style="display:flex;align-items:center;justify-content:center;gap:20px;width:100%;">${actions.join('')}</div>`
|
|
}
|
|
|
|
function handleActionClick(row: DictDto, _rowIndex: number, action?: string) {
|
|
if (action === 'edit') {
|
|
handleEdit(row)
|
|
return
|
|
}
|
|
if (action === 'delete') {
|
|
Modal.confirm({
|
|
content: t('system.dict.modal.deleteConfirmContentSingle', { dictName: row.dictName || '' }),
|
|
onOk: () => handleDelete(row.id),
|
|
})
|
|
}
|
|
}
|
|
|
|
function selectDictTypeFromKey(key?: string) {
|
|
if (!key || !key.startsWith('type-')) return
|
|
const dictType = key.slice(5)
|
|
if (!dictType) return
|
|
if (selectedDictType.value === dictType) return
|
|
selectedDictType.value = dictType
|
|
pagination.current = 1
|
|
query.keyword = ''
|
|
loadData()
|
|
}
|
|
|
|
function handleTypeSelect(keys: string[]) {
|
|
selectDictTypeFromKey(keys[0])
|
|
}
|
|
|
|
function handleTypeNodeClick(key: string) {
|
|
selectDictTypeFromKey(key)
|
|
}
|
|
|
|
function handleTypeSearch() {
|
|
// filteredTypeList 客户端过滤
|
|
}
|
|
|
|
function handleSearch() {
|
|
pagination.current = 1
|
|
loadData()
|
|
}
|
|
|
|
function openBatchImportModal() {
|
|
batchImportModalRef.value?.open()
|
|
}
|
|
|
|
async function handleBatchExport() {
|
|
if (!selectedDictType.value) {
|
|
Message.warning(t('system.dict.left.emptySelect'))
|
|
return
|
|
}
|
|
exportLoading.value = true
|
|
try {
|
|
const rows = await getDictListAll({
|
|
dictType: selectedDictType.value,
|
|
keyword: query.keyword || undefined,
|
|
})
|
|
if (!rows.length) {
|
|
Message.warning(t('system.dict.export.empty'))
|
|
return
|
|
}
|
|
const suffix = selectedDictType.value ? `_${selectedDictType.value}` : ''
|
|
exportDictRowsToExcel(rows, `数据字典${suffix}_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`)
|
|
Message.success(t('system.dict.export.success'))
|
|
} catch (e: any) {
|
|
Message.error(e?.message || t('system.dict.export.failed'))
|
|
} finally {
|
|
exportLoading.value = false
|
|
}
|
|
}
|
|
|
|
function handleAdd() {
|
|
if (!selectedDictType.value) {
|
|
Message.warning(t('system.dict.left.emptySelect'))
|
|
return
|
|
}
|
|
isEdit.value = false
|
|
editingId.value = undefined
|
|
Object.assign(formState, {
|
|
dictType: selectedDictType.value,
|
|
dictSort: (selectedTypeMeta.value?.itemCount ?? 0) + 1,
|
|
dictCode: '',
|
|
dictName: '',
|
|
})
|
|
modalVisible.value = true
|
|
}
|
|
|
|
function handleEdit(row: DictDto) {
|
|
isEdit.value = true
|
|
editingId.value = row.id
|
|
Object.assign(formState, {
|
|
dictType: row.dictType || selectedDictType.value,
|
|
dictSort: row.dictSort ?? 1,
|
|
dictCode: row.dictCode || '',
|
|
dictName: row.dictName || '',
|
|
})
|
|
modalVisible.value = true
|
|
}
|
|
|
|
function handleAddType() {
|
|
typeIsEdit.value = false
|
|
Object.assign(typeFormState, {
|
|
id: undefined,
|
|
dictType: '',
|
|
typeSort: dictTypeList.value.length + 1,
|
|
remark: '',
|
|
})
|
|
typeModalVisible.value = true
|
|
}
|
|
|
|
function handleEditType(nodeData: CustomLeftMenuTreeNode | null) {
|
|
if (!nodeData?.dictType) return
|
|
typeIsEdit.value = true
|
|
Object.assign(typeFormState, {
|
|
id: nodeData.id,
|
|
dictType: nodeData.dictType,
|
|
typeSort: nodeData.typeSort ?? 0,
|
|
remark: nodeData.remark ?? '',
|
|
})
|
|
typeModalVisible.value = true
|
|
}
|
|
|
|
function handleEditSelectedType() {
|
|
handleEditType(buildTypeTreeNode(selectedTypeMeta.value || { dictType: selectedDictType.value }))
|
|
}
|
|
|
|
async function handleDeleteType(nodeData: CustomLeftMenuTreeNode | null) {
|
|
if (!nodeData?.id) return
|
|
Modal.confirm({
|
|
content: t('system.dict.typeModal.deleteConfirm', { dictType: nodeData.dictType || '' }),
|
|
onOk: async () => {
|
|
await deleteDictType(nodeData.id!)
|
|
Message.success(t('system.dict.typeModal.deleteSuccess'))
|
|
if (selectedDictType.value === nodeData.dictType) {
|
|
selectedDictType.value = ''
|
|
dataSource.value = []
|
|
pagination.total = 0
|
|
}
|
|
await loadDictTypes(undefined, { loadDetails: false })
|
|
},
|
|
})
|
|
}
|
|
|
|
async function handleDelete(id?: number | string) {
|
|
if (!id) return
|
|
await deleteDict(id)
|
|
Message.success(t('system.dict.message.deleteSuccess'))
|
|
await Promise.all([loadData(), loadDictTypes(undefined, { loadDetails: false })])
|
|
}
|
|
|
|
async function handleModalOk() {
|
|
try {
|
|
await formRef.value.validate()
|
|
modalLoading.value = true
|
|
const payload = {
|
|
dictType: formState.dictType.trim(),
|
|
dictSort: Number(formState.dictSort),
|
|
dictCode: formState.dictCode.trim(),
|
|
dictName: formState.dictName.trim(),
|
|
}
|
|
if (isEdit.value && editingId.value) {
|
|
await updateDict({ id: editingId.value, ...payload })
|
|
Message.success(t('system.dict.message.editSuccess'))
|
|
} else {
|
|
await createDict(payload)
|
|
Message.success(t('system.dict.message.addSuccess'))
|
|
}
|
|
modalVisible.value = false
|
|
await Promise.all([loadData(), loadDictTypes(undefined, { loadDetails: false })])
|
|
} finally {
|
|
modalLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function handleTypeModalOk() {
|
|
try {
|
|
await typeFormRef.value.validate()
|
|
typeModalLoading.value = true
|
|
await saveDictType({
|
|
id: typeFormState.id,
|
|
dictType: typeFormState.dictType.trim(),
|
|
remark: typeFormState.remark.trim() || undefined,
|
|
typeSort: Number(typeFormState.typeSort) || 0,
|
|
})
|
|
Message.success(typeIsEdit.value ? t('system.dict.typeModal.editSuccess') : t('system.dict.typeModal.addSuccess'))
|
|
typeModalVisible.value = false
|
|
const savedType = typeFormState.dictType.trim()
|
|
await loadDictTypes(savedType, { loadDetails: false })
|
|
} finally {
|
|
typeModalLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function loadDictTypes(
|
|
preferSelectType?: string,
|
|
options: { loadDetails?: boolean } = {},
|
|
) {
|
|
const { loadDetails = false } = options
|
|
typeLoading.value = true
|
|
try {
|
|
dictTypeList.value = await getDictTypeList()
|
|
const types = dictTypeList.value.map((item) => item.dictType).filter(Boolean) as string[]
|
|
if (preferSelectType && types.includes(preferSelectType)) {
|
|
selectedDictType.value = preferSelectType
|
|
} else if (!selectedDictType.value || !types.includes(selectedDictType.value)) {
|
|
selectedDictType.value = types[0] || ''
|
|
}
|
|
if (loadDetails && selectedDictType.value) {
|
|
await loadData()
|
|
} else if (!selectedDictType.value) {
|
|
dataSource.value = []
|
|
pagination.total = 0
|
|
}
|
|
} catch (e: any) {
|
|
dictTypeList.value = []
|
|
Message.error(e?.message || t('system.dict.message.loadTypeFailed'))
|
|
} finally {
|
|
typeLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function loadData() {
|
|
if (!selectedDictType.value) {
|
|
dataSource.value = []
|
|
pagination.total = 0
|
|
return
|
|
}
|
|
listLoading.value = true
|
|
try {
|
|
const { items, total } = await getDictList({
|
|
page: pagination.current,
|
|
pageSize: pagination.pageSize,
|
|
dictType: selectedDictType.value,
|
|
keyword: query.keyword.trim() || undefined,
|
|
})
|
|
dataSource.value = items
|
|
pagination.total = total
|
|
if (!query.keyword.trim()) {
|
|
syncSelectedTypeItemCount(total)
|
|
}
|
|
} catch (e: any) {
|
|
dataSource.value = []
|
|
pagination.total = 0
|
|
Message.error(e?.message || t('system.dict.message.loadDataFailed', { error: '' }))
|
|
} finally {
|
|
listLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function handleImportSuccess() {
|
|
await loadDictTypes(undefined, { loadDetails: false })
|
|
if (selectedDictType.value) {
|
|
await loadData()
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await loadDictTypes(undefined, { loadDetails: true })
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
@import '@/components/RbacSplit/rbac-split-layout.less';
|
|
|
|
.dict-split-layout {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.dict-page-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
min-height: 0;
|
|
background: #fff;
|
|
padding: 16px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.dict-type-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dict-type-header-main {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.panel-title {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--color-text-1);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.type-code-tag {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.type-item-count {
|
|
font-size: 13px;
|
|
color: var(--color-text-3);
|
|
}
|
|
|
|
.edit-type-btn {
|
|
flex-shrink: 0;
|
|
color: var(--app-primary-color);
|
|
}
|
|
|
|
.table-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 16px;
|
|
flex-shrink: 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.toolbar-left,
|
|
.toolbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.toolbar-left {
|
|
flex: 1;
|
|
min-width: 200px;
|
|
}
|
|
|
|
.panel-empty {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 48px 16px;
|
|
}
|
|
|
|
.panel-empty-icon {
|
|
font-size: 56px;
|
|
color: var(--color-text-4);
|
|
}
|
|
|
|
.panel-empty-desc {
|
|
margin: 8px 0 0;
|
|
font-size: 13px;
|
|
color: var(--color-text-3);
|
|
text-align: center;
|
|
}
|
|
|
|
.list-wrap {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.list-spin {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.list-spin :deep(.arco-spin-container) {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.list-spin :deep(.handsontable-pro-container) {
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
</style>
|