2 changed files with 521 additions and 0 deletions
@ -0,0 +1,518 @@ |
|||
<template> |
|||
<div class="p-2"> |
|||
<el-row :gutter="10"> |
|||
<!-- 左侧: 字典类型管理 --> |
|||
<el-col :span="10"> |
|||
<el-card shadow="hover" class="dict-card"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">字典类型</span> |
|||
<div class="card-header-actions"> |
|||
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" size="small" @click="handleTypeAdd">新增</el-button> |
|||
<el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Refresh" size="small" @click="handleRefreshCache">刷新缓存</el-button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<!-- 搜索区域 --> |
|||
<el-form ref="typeQueryFormRef" :model="typeQueryParams" :inline="true" class="search-form"> |
|||
<el-form-item label="字典名称" prop="dictName"> |
|||
<el-input v-model="typeQueryParams.dictName" placeholder="请输入字典名称" clearable size="small" @keyup.enter="handleTypeQuery" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="Search" size="small" @click="handleTypeQuery">搜索</el-button> |
|||
<el-button icon="Refresh" size="small" @click="resetTypeQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<!-- 字典类型表格 --> |
|||
<el-table |
|||
v-loading="typeLoading" |
|||
border |
|||
:data="typeList" |
|||
highlight-current-row |
|||
@current-change="handleTypeCurrentChange" |
|||
@selection-change="handleTypeSelectionChange" |
|||
size="small" |
|||
> |
|||
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="字典类型" align="center" prop="dictType" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width"> |
|||
<template #default="scope"> |
|||
<el-tooltip content="修改" placement="top"> |
|||
<el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleTypeUpdate(scope.row)" /> |
|||
</el-tooltip> |
|||
<el-tooltip content="删除" placement="top"> |
|||
<el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleTypeDelete(scope.row)" /> |
|||
</el-tooltip> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="typeTotal > 0" |
|||
v-model:page="typeQueryParams.pageNum" |
|||
v-model:limit="typeQueryParams.pageSize" |
|||
:total="typeTotal" |
|||
:page-sizes="[10, 20, 50]" |
|||
layout="total, prev, pager, next" |
|||
small |
|||
@pagination="getTypeList" |
|||
/> |
|||
</el-card> |
|||
</el-col> |
|||
|
|||
<!-- 右侧: 字典数据管理 --> |
|||
<el-col :span="14"> |
|||
<el-card shadow="hover" class="dict-card"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title"> |
|||
字典数据 |
|||
<el-tag v-if="currentDictType" type="primary" size="small" class="ml-2">{{ currentDictTypeName }}</el-tag> |
|||
</span> |
|||
<div class="card-header-actions"> |
|||
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" size="small" :disabled="!currentDictType" @click="handleDataAdd">新增</el-button> |
|||
<el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" size="small" :disabled="!currentDictType" @click="handleDataExport">导出</el-button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<!-- 搜索区域 --> |
|||
<el-form ref="dataQueryFormRef" :model="dataQueryParams" :inline="true" class="search-form"> |
|||
<el-form-item label="字典标签" prop="dictLabel"> |
|||
<el-input v-model="dataQueryParams.dictLabel" placeholder="请输入字典标签" clearable size="small" @keyup.enter="handleDataQuery" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="Search" size="small" @click="handleDataQuery">搜索</el-button> |
|||
<el-button icon="Refresh" size="small" @click="resetDataQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<!-- 字典数据表格 --> |
|||
<el-table v-loading="dataLoading" border :data="dataList" size="small"> |
|||
<el-table-column label="字典标签" align="center" prop="dictLabel"> |
|||
<template #default="scope"> |
|||
<span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)"> |
|||
{{ scope.row.dictLabel }} |
|||
</span> |
|||
<el-tag |
|||
v-else |
|||
:type="scope.row.listClass === 'primary' || scope.row.listClass === 'default' ? 'primary' : scope.row.listClass" |
|||
:class="scope.row.cssClass" |
|||
> |
|||
{{ scope.row.dictLabel }} |
|||
</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="字典键值" align="center" prop="dictValue" /> |
|||
<el-table-column label="字典排序" align="center" prop="dictSort" width="80" /> |
|||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width"> |
|||
<template #default="scope"> |
|||
<el-tooltip content="修改" placement="top"> |
|||
<el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleDataUpdate(scope.row)" /> |
|||
</el-tooltip> |
|||
<el-tooltip content="删除" placement="top"> |
|||
<el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDataDelete(scope.row)" /> |
|||
</el-tooltip> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<!-- 无选中提示 --> |
|||
<el-empty v-if="!currentDictType && !dataLoading" description="请在左侧选择一个字典类型" :image-size="80" /> |
|||
|
|||
<pagination |
|||
v-show="dataTotal > 0" |
|||
v-model:page="dataQueryParams.pageNum" |
|||
v-model:limit="dataQueryParams.pageSize" |
|||
:total="dataTotal" |
|||
:page-sizes="[10, 20, 50]" |
|||
layout="total, prev, pager, next" |
|||
small |
|||
@pagination="getDataList" |
|||
/> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<!-- 添加或修改字典类型对话框 --> |
|||
<el-dialog v-model="typeDialog.visible" :title="typeDialog.title" width="500px" append-to-body> |
|||
<el-form ref="typeFormRef" :model="typeForm" :rules="typeRules" label-width="80px"> |
|||
<el-form-item label="字典名称" prop="dictName"> |
|||
<el-input v-model="typeForm.dictName" placeholder="请输入字典名称" /> |
|||
</el-form-item> |
|||
<el-form-item label="字典类型" prop="dictType"> |
|||
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" /> |
|||
</el-form-item> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button type="primary" @click="submitTypeForm">确 定</el-button> |
|||
<el-button @click="cancelType">取 消</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
|
|||
<!-- 添加或修改字典数据对话框 --> |
|||
<el-dialog v-model="dataDialog.visible" :title="dataDialog.title" width="500px" append-to-body> |
|||
<el-form ref="dataFormRef" :model="dataForm" :rules="dataRules" label-width="80px"> |
|||
<el-form-item label="字典类型"> |
|||
<el-input v-model="dataForm.dictType" :disabled="true" /> |
|||
</el-form-item> |
|||
<el-form-item label="数据标签" prop="dictLabel"> |
|||
<el-input v-model="dataForm.dictLabel" placeholder="请输入数据标签" /> |
|||
</el-form-item> |
|||
<el-form-item label="数据键值" prop="dictValue"> |
|||
<el-input v-model="dataForm.dictValue" placeholder="请输入数据键值" /> |
|||
</el-form-item> |
|||
<el-form-item label="样式属性" prop="cssClass"> |
|||
<el-input v-model="dataForm.cssClass" placeholder="请输入样式属性" /> |
|||
</el-form-item> |
|||
<el-form-item label="显示排序" prop="dictSort"> |
|||
<el-input-number v-model="dataForm.dictSort" controls-position="right" :min="0" /> |
|||
</el-form-item> |
|||
<el-form-item label="回显样式" prop="listClass"> |
|||
<el-select v-model="dataForm.listClass"> |
|||
<el-option |
|||
v-for="item in listClassOptions" |
|||
:key="item.value" |
|||
:label="item.label + '(' + item.value + ')'" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="dataForm.remark" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button type="primary" @click="submitDataForm">确 定</el-button> |
|||
<el-button @click="cancelData">取 消</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup name="DictManager" lang="ts"> |
|||
import { useDictStore } from '@/store/modules/dict'; |
|||
import { listType, getType, delType, addType, updateType, refreshCache } from '@/api/system/dict/type'; |
|||
import { listData, getData, delData, addData, updateData } from '@/api/system/dict/data'; |
|||
import { DictTypeForm, DictTypeQuery, DictTypeVO } from '@/api/system/dict/type/types'; |
|||
import { DictDataForm, DictDataQuery, DictDataVO } from '@/api/system/dict/data/types'; |
|||
|
|||
const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
|||
|
|||
// ==================== 字典类型相关 ==================== |
|||
const typeList = ref<DictTypeVO[]>([]); |
|||
const typeLoading = ref(true); |
|||
const typeTotal = ref(0); |
|||
const currentDictType = ref(''); |
|||
const currentDictTypeName = ref(''); |
|||
|
|||
const typeQueryFormRef = ref<ElFormInstance>(); |
|||
const typeFormRef = ref<ElFormInstance>(); |
|||
|
|||
const typeDialog = reactive<DialogOption>({ |
|||
visible: false, |
|||
title: '' |
|||
}); |
|||
|
|||
const initTypeForm: DictTypeForm = { |
|||
dictId: undefined, |
|||
dictName: '', |
|||
dictType: '', |
|||
remark: '' |
|||
}; |
|||
|
|||
const typeQueryParams = reactive<DictTypeQuery>({ |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
dictName: '', |
|||
dictType: '' |
|||
}); |
|||
|
|||
const typeForm = ref<DictTypeForm>({ ...initTypeForm }); |
|||
|
|||
const typeRules = { |
|||
dictName: [{ required: true, message: '字典名称不能为空', trigger: 'blur' }], |
|||
dictType: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }] |
|||
}; |
|||
|
|||
/** 查询字典类型列表 */ |
|||
const getTypeList = async () => { |
|||
typeLoading.value = true; |
|||
const res = await listType(typeQueryParams); |
|||
typeList.value = res.rows; |
|||
typeTotal.value = res.total; |
|||
typeLoading.value = false; |
|||
}; |
|||
|
|||
/** 字典类型行点击 - 切换右侧数据 */ |
|||
const handleTypeCurrentChange = (row: DictTypeVO | null) => { |
|||
if (row) { |
|||
currentDictType.value = row.dictType; |
|||
currentDictTypeName.value = row.dictName; |
|||
dataQueryParams.dictType = row.dictType; |
|||
dataQueryParams.pageNum = 1; |
|||
dataQueryParams.dictLabel = ''; |
|||
getDataList(); |
|||
} |
|||
}; |
|||
|
|||
/** 字典类型搜索 */ |
|||
const handleTypeQuery = () => { |
|||
typeQueryParams.pageNum = 1; |
|||
getTypeList(); |
|||
}; |
|||
|
|||
/** 字典类型重置 */ |
|||
const resetTypeQuery = () => { |
|||
typeQueryFormRef.value?.resetFields(); |
|||
handleTypeQuery(); |
|||
}; |
|||
|
|||
/** 字典类型新增 */ |
|||
const handleTypeAdd = () => { |
|||
typeForm.value = { ...initTypeForm }; |
|||
typeFormRef.value?.resetFields(); |
|||
typeDialog.visible = true; |
|||
typeDialog.title = '添加字典类型'; |
|||
}; |
|||
|
|||
/** 字典类型修改 */ |
|||
const handleTypeUpdate = async (row: DictTypeVO) => { |
|||
typeForm.value = { ...initTypeForm }; |
|||
typeFormRef.value?.resetFields(); |
|||
const res = await getType(row.dictId); |
|||
typeForm.value = { ...res.data }; |
|||
typeDialog.visible = true; |
|||
typeDialog.title = '修改字典类型'; |
|||
}; |
|||
|
|||
/** 字典类型提交 */ |
|||
const submitTypeForm = () => { |
|||
typeFormRef.value?.validate(async (valid: boolean) => { |
|||
if (valid) { |
|||
typeForm.value.dictId ? await updateType(typeForm.value) : await addType(typeForm.value); |
|||
proxy?.$modal.msgSuccess('操作成功'); |
|||
typeDialog.visible = false; |
|||
getTypeList(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
/** 字典类型取消 */ |
|||
const cancelType = () => { |
|||
typeDialog.visible = false; |
|||
typeForm.value = { ...initTypeForm }; |
|||
typeFormRef.value?.resetFields(); |
|||
}; |
|||
|
|||
/** 字典类型删除 */ |
|||
const handleTypeDelete = async (row: DictTypeVO) => { |
|||
await proxy?.$modal.confirm('是否确认删除字典名称为"' + row.dictName + '"的数据项?'); |
|||
await delType(row.dictId); |
|||
getTypeList(); |
|||
// 如果删除的是当前选中的字典类型,清空右侧 |
|||
if (currentDictType.value === row.dictType) { |
|||
currentDictType.value = ''; |
|||
currentDictTypeName.value = ''; |
|||
dataList.value = []; |
|||
dataTotal.value = 0; |
|||
} |
|||
proxy?.$modal.msgSuccess('删除成功'); |
|||
}; |
|||
|
|||
/** 字典类型多选(预留) */ |
|||
const handleTypeSelectionChange = (_selection: DictTypeVO[]) => {}; |
|||
|
|||
/** 刷新缓存 */ |
|||
const handleRefreshCache = async () => { |
|||
await refreshCache(); |
|||
proxy?.$modal.msgSuccess('刷新成功'); |
|||
useDictStore().cleanDict(); |
|||
}; |
|||
|
|||
// ==================== 字典数据相关 ==================== |
|||
const dataList = ref<DictDataVO[]>([]); |
|||
const dataLoading = ref(false); |
|||
const dataTotal = ref(0); |
|||
|
|||
const dataQueryFormRef = ref<ElFormInstance>(); |
|||
const dataFormRef = ref<ElFormInstance>(); |
|||
|
|||
const dataDialog = reactive<DialogOption>({ |
|||
visible: false, |
|||
title: '' |
|||
}); |
|||
|
|||
const initDataForm: DictDataForm = { |
|||
dictCode: undefined, |
|||
dictLabel: '', |
|||
dictValue: '', |
|||
cssClass: '', |
|||
listClass: 'primary', |
|||
dictSort: 0, |
|||
remark: '' |
|||
}; |
|||
|
|||
const dataQueryParams = reactive<DictDataQuery>({ |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
dictName: '', |
|||
dictType: '', |
|||
dictLabel: '' |
|||
}); |
|||
|
|||
const dataForm = ref<DictDataForm>({ ...initDataForm }); |
|||
|
|||
const dataRules = { |
|||
dictLabel: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }], |
|||
dictValue: [{ required: true, message: '数据键值不能为空', trigger: 'blur' }], |
|||
dictSort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }] |
|||
}; |
|||
|
|||
// 数据标签回显样式 |
|||
const listClassOptions = ref<Array<{ value: string; label: string }>>([ |
|||
{ value: 'default', label: '默认' }, |
|||
{ value: 'primary', label: '主要' }, |
|||
{ value: 'success', label: '成功' }, |
|||
{ value: 'info', label: '信息' }, |
|||
{ value: 'warning', label: '警告' }, |
|||
{ value: 'danger', label: '危险' } |
|||
]); |
|||
|
|||
/** 查询字典数据列表 */ |
|||
const getDataList = async () => { |
|||
if (!currentDictType.value) return; |
|||
dataLoading.value = true; |
|||
const res = await listData(dataQueryParams); |
|||
dataList.value = res.rows; |
|||
dataTotal.value = res.total; |
|||
dataLoading.value = false; |
|||
}; |
|||
|
|||
/** 字典数据搜索 */ |
|||
const handleDataQuery = () => { |
|||
dataQueryParams.pageNum = 1; |
|||
getDataList(); |
|||
}; |
|||
|
|||
/** 字典数据重置 */ |
|||
const resetDataQuery = () => { |
|||
dataQueryFormRef.value?.resetFields(); |
|||
dataQueryParams.dictType = currentDictType.value; |
|||
handleDataQuery(); |
|||
}; |
|||
|
|||
/** 字典数据新增 */ |
|||
const handleDataAdd = () => { |
|||
dataForm.value = { ...initDataForm }; |
|||
dataFormRef.value?.resetFields(); |
|||
dataForm.value.dictType = currentDictType.value; |
|||
dataDialog.visible = true; |
|||
dataDialog.title = '添加字典数据'; |
|||
}; |
|||
|
|||
/** 字典数据修改 */ |
|||
const handleDataUpdate = async (row: DictDataVO) => { |
|||
dataForm.value = { ...initDataForm }; |
|||
dataFormRef.value?.resetFields(); |
|||
const res = await getData(row.dictCode); |
|||
dataForm.value = { ...res.data }; |
|||
dataDialog.visible = true; |
|||
dataDialog.title = '修改字典数据'; |
|||
}; |
|||
|
|||
/** 字典数据提交 */ |
|||
const submitDataForm = () => { |
|||
dataFormRef.value?.validate(async (valid: boolean) => { |
|||
if (valid) { |
|||
dataForm.value.dictCode ? await updateData(dataForm.value) : await addData(dataForm.value); |
|||
useDictStore().removeDict(currentDictType.value); |
|||
proxy?.$modal.msgSuccess('操作成功'); |
|||
dataDialog.visible = false; |
|||
getDataList(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
/** 字典数据取消 */ |
|||
const cancelData = () => { |
|||
dataDialog.visible = false; |
|||
dataForm.value = { ...initDataForm }; |
|||
dataFormRef.value?.resetFields(); |
|||
}; |
|||
|
|||
/** 字典数据删除 */ |
|||
const handleDataDelete = async (row: DictDataVO) => { |
|||
await proxy?.$modal.confirm('是否确认删除字典标签为"' + row.dictLabel + '"的数据项?'); |
|||
await delData(row.dictCode); |
|||
await getDataList(); |
|||
proxy?.$modal.msgSuccess('删除成功'); |
|||
useDictStore().removeDict(currentDictType.value); |
|||
}; |
|||
|
|||
/** 字典数据导出 */ |
|||
const handleDataExport = () => { |
|||
proxy?.download( |
|||
'system/dict/data/export', |
|||
{ |
|||
...dataQueryParams |
|||
}, |
|||
`dict_data_${new Date().getTime()}.xlsx` |
|||
); |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
getTypeList(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.dict-card { |
|||
min-height: calc(100vh - 120px); |
|||
|
|||
:deep(.el-card__header) { |
|||
padding: 12px 16px; |
|||
} |
|||
} |
|||
|
|||
.card-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.card-header-title { |
|||
font-weight: bold; |
|||
font-size: 14px; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.card-header-actions { |
|||
display: flex; |
|||
gap: 8px; |
|||
} |
|||
|
|||
.search-form { |
|||
margin-bottom: 10px; |
|||
|
|||
:deep(.el-form-item) { |
|||
margin-bottom: 8px; |
|||
} |
|||
} |
|||
|
|||
.ml-2 { |
|||
margin-left: 8px; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue