feat-wip(views): 数据字典相关页面代码提交

This commit is contained in:
2025-12-06 16:33:58 +08:00
parent 11747a490c
commit 755e6211c2
6 changed files with 156 additions and 214 deletions

View File

@ -1,26 +1,26 @@
import { request } from '../../../request'; import { request } from '../../../request';
export function fetchPageDictionary(pageRequest: Api.Sys.Core.DictionaryQueryPageRequest) { export function fetchDictionaryPaginate(pageRequest: Api.Common.PageRequest<Api.Sys.Core.DictionaryDTO>) {
return request<Api.Common.PageResponse<Api.Sys.Core.Dictionary>>({ return request<Api.Common.PageResponse<Api.Sys.Core.DictionaryVO>>({
url: '/dictionary/paginate', url: '/dictionary/paginate',
method: 'post', method: 'post',
data: pageRequest data: pageRequest
}); });
} }
export function fetchDictionaryAdd(dictionaryOp: Api.Sys.Core.DictionaryOp) { export function fetchDictionaryInsert(dictionaryDTO: Api.Sys.Core.DictionaryDTO) {
return request({ return request({
url: '/dictionary/insert', url: '/dictionary/insert',
method: 'post', method: 'post',
data: dictionaryOp data: dictionaryDTO
}); });
} }
export function fetchDictionaryEdit(dictionaryOp: Api.Sys.Core.DictionaryOp) { export function fetchDictionaryUpdate(dictionaryDTO: Api.Sys.Core.DictionaryDTO) {
return request({ return request({
url: '/dictionary/update', url: '/dictionary/update',
method: 'post', method: 'post',
data: dictionaryOp data: dictionaryDTO
}); });
} }
@ -44,8 +44,8 @@ export function fetchDictionaryDeleteBatch(ids: string[]) {
}); });
} }
export function fetchTreeDictionaryItem(dictionaryId: string) { export function fetchDictionaryItemTreeList(dictionaryId: string) {
return request<Api.Sys.Core.DictionaryItem[]>({ return request<Api.Sys.Core.DictionaryItemVO[]>({
url: '/dictionaryItem/tree', url: '/dictionaryItem/tree',
method: 'post', method: 'post',
data: { data: {
@ -54,19 +54,19 @@ export function fetchTreeDictionaryItem(dictionaryId: string) {
}); });
} }
export function fetchDictionaryItemAdd(dictionaryOp: Api.Sys.Core.DictionaryItemOp) { export function fetchDictionaryItemInsert(dictionaryItemDTO: Api.Sys.Core.DictionaryItemDTO) {
return request({ return request({
url: '/dictionaryItem/insert', url: '/dictionaryItem/insert',
method: 'post', method: 'post',
data: dictionaryOp data: dictionaryItemDTO
}); });
} }
export function fetchDictionaryItemEdit(dictionaryOp: Api.Sys.Core.DictionaryItemOp) { export function fetchDictionaryItemUpdate(dictionaryItemDTO: Api.Sys.Core.DictionaryItemDTO) {
return request({ return request({
url: '/dictionaryItem/update', url: '/dictionaryItem/update',
method: 'post', method: 'post',
data: dictionaryOp data: dictionaryItemDTO
}); });
} }

View File

@ -3,7 +3,7 @@ declare namespace Api {
namespace Core { namespace Core {
// ******************** sys_core_dictionary ******************** // ******************** sys_core_dictionary ********************
type DictionaryType = 'enum' | 'tree'; type DictionaryType = 'enum' | 'tree';
interface Dictionary { interface DictionaryVO {
id: string; id: string;
name: string; name: string;
code: string; code: string;
@ -12,30 +12,25 @@ declare namespace Api {
createTime: string; createTime: string;
updateTime: string; updateTime: string;
} }
interface DictionaryQuery { interface DictionaryDTO {
id: string | null;
name: string | null; name: string | null;
code: string | null; code: string | null;
type: string | null; type: DictionaryType | null;
}
type DictionaryQueryPageRequest = Api.Common.PageRequest<DictionaryQuery>;
interface DictionaryOp {
id: string | null;
name: string;
code: string;
type: DictionaryType;
description: string | null; description: string | null;
} }
interface DictionaryItem { interface DictionaryItemVO {
id: string; id: string;
dictionaryId: string | null;
name: string; name: string;
code: string; code: string;
sort: number; sort: number;
description: string | null; description: string | null;
createTime: string | null; createTime: string;
updateTime: string | null; updateTime: string;
children: DictionaryItem[]; children: DictionaryItemVO[];
} }
interface DictionaryItemOp { interface DictionaryItemDTO {
id: string | null; id: string | null;
dictionaryId: string; dictionaryId: string;
name: string; name: string;

View File

@ -2,27 +2,34 @@
import { reactive } from 'vue'; import { reactive } from 'vue';
import { NButton, NPopconfirm, NTag } from 'naive-ui'; import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { dictionaryTypeRecord } from '@/constants/sys/core/dictionary'; import { dictionaryTypeRecord } from '@/constants/sys/core/dictionary';
import { fetchDictionaryDelete, fetchDictionaryDeleteBatch, fetchPageDictionary } from '@/service/api'; import { fetchDictionaryDelete, fetchDictionaryDeleteBatch, fetchDictionaryPaginate } from '@/service/api';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table'; import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales'; import { $t } from '@/locales';
import DictionarySearch from '@/views/sys/core/dictionary/modules/dictionary-search.vue'; import DictionarySearch from '@/views/sys/core/dictionary/modules/dictionary-search.vue';
import DictionaryOperateDrawer from '@/views/sys/core/dictionary/modules/dictionary-operate-drawer.vue'; import DictionaryOperateDrawer from '@/views/sys/core/dictionary/modules/dictionary-operate-drawer.vue';
import DictionaryDTO = Api.Sys.Core.DictionaryDTO;
// ******************** 数据定义 ********************
const appStore = useAppStore(); const appStore = useAppStore();
const pageRequest: Api.Sys.Core.DictionaryQueryPageRequest = reactive({ // 表格分页请求
const pageRequest: Api.Common.PageRequest<DictionaryDTO> = reactive({
pageIndex: 1, pageIndex: 1,
pageSize: 10, pageSize: 10,
query: { query: {
id: null,
name: null, name: null,
code: null, code: null,
type: null type: null,
description: null
} }
}); });
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useNaivePaginatedTable({ // 表格数据
api: () => fetchPageDictionary(pageRequest), const { columns, columnChecks, data, loading, getData, getDataByPage, pagination } = useNaivePaginatedTable({
api: () => fetchDictionaryPaginate(pageRequest),
transform: response => defaultTransform(response), transform: response => defaultTransform(response),
onPaginationParamsChange: params => { onPaginationParamsChange: params => {
pageRequest.pageIndex = params.page || 1; pageRequest.pageIndex = params.page || 1;
@ -100,10 +107,12 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
} }
] ]
}); });
// 表格操作
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } = const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
useTableOperate(data, 'id', getData); useTableOperate(data, 'id', getData);
// ******************** 触发事件 ********************
async function handleBatchDelete() { async function handleBatchDelete() {
fetchDictionaryDeleteBatch(checkedRowKeys.value).then(() => { fetchDictionaryDeleteBatch(checkedRowKeys.value).then(() => {
onBatchDeleted(); onBatchDeleted();
@ -151,7 +160,7 @@ function edit(id: string) {
:loading="loading" :loading="loading"
remote remote
:row-key="row => row.id" :row-key="row => row.id"
:pagination="mobilePagination" :pagination="pagination"
class="sm:h-full" class="sm:h-full"
/> />
<DictionaryOperateDrawer <DictionaryOperateDrawer

View File

@ -1,59 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { $t } from '@/locales';
defineOptions({
name: 'DictionaryOperateDialog'
});
const props = defineProps<{
/** the type of operation */
operateType: NaiveUI.TableOperateType;
/** the edit row data */
// rowData?: Api.Sys.Core.Dictionary | null;
}>();
const emit = defineEmits<{
(e: 'submitted'): void;
}>();
const visible = defineModel<boolean>('visible', {
default: false
});
const title = computed(() => {
const titles: Record<NaiveUI.TableOperateType, string> = {
add: $t('common.add'),
edit: $t('common.edit')
};
return titles[props.operateType];
});
function closeDialog(): void {
visible.value = false;
}
function handleSubmit() {
window.$message?.success($t('common.updateSuccess'));
closeDialog();
emit('submitted');
}
</script>
<template>
<NModal
v-model:show="visible"
preset="dialog"
:title="title"
@positive-click="handleSubmit"
@negative-click="visible = false"
>
<div class="dialog-wrapper">111</div>
</NModal>
</template>
<style scoped>
.dialog-wrapper {
width: 1000px;
}
</style>

View File

@ -4,43 +4,56 @@ import type { DataTableColumns } from 'naive-ui';
import { jsonClone } from '@sa/utils'; import { jsonClone } from '@sa/utils';
import { dictionaryTypeOptions } from '@/constants/sys/core/dictionary'; import { dictionaryTypeOptions } from '@/constants/sys/core/dictionary';
import { import {
fetchDictionaryAdd, fetchDictionaryInsert,
fetchDictionaryEdit,
fetchDictionaryItemAdd,
fetchDictionaryItemDelete, fetchDictionaryItemDelete,
fetchDictionaryItemEdit, fetchDictionaryItemInsert,
fetchTreeDictionaryItem fetchDictionaryItemTreeList,
fetchDictionaryItemUpdate,
fetchDictionaryUpdate
} from '@/service/api'; } from '@/service/api';
import { useFormRules, useNaiveForm } from '@/hooks/common/form'; import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales'; import { $t } from '@/locales';
import DictionaryItem = Api.Sys.Core.DictionaryItem;
// ******************** 数据定义 ********************
// 组件定义
defineOptions({ defineOptions({
name: 'DictionaryOperateDrawer' name: 'DictionaryOperateDrawer'
}); });
// 属性定义
interface Props { interface Props {
/** the type of operation */ /** the type of operation */
operateType: NaiveUI.TableOperateType; operateType: NaiveUI.TableOperateType;
/** the edit row data */ /** the edit row data */
rowData?: Api.Sys.Core.Dictionary | null; rowData?: Api.Sys.Core.DictionaryVO | null;
} }
const props = defineProps<Props>(); const props = defineProps<Props>();
// 事件定义
interface Emits { interface Emits {
(e: 'submitted'): void; (e: 'submitted'): void;
} }
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
// 可见性定义
const visible = defineModel<boolean>('visible', { const visible = defineModel<boolean>('visible', {
default: false default: false
}); });
const { formRef, validate, restoreValidation } = useNaiveForm(); // Model
const { defaultRequiredRule } = useFormRules(); const model = ref(createDefaultModel());
function createDefaultModel(): Api.Sys.Core.DictionaryDTO {
return {
id: null,
name: null,
code: null,
type: null,
description: null
};
}
// 抽屉标题
const title = computed(() => { const title = computed(() => {
const titles: Record<NaiveUI.TableOperateType, string> = { const titles: Record<NaiveUI.TableOperateType, string> = {
add: $t('common.add'), add: $t('common.add'),
@ -49,41 +62,54 @@ const title = computed(() => {
return titles[props.operateType]; return titles[props.operateType];
}); });
interface Model { // Form校验规则
name: string; const { formRef, validate, restoreValidation } = useNaiveForm();
code: string; const { defaultRequiredRule } = useFormRules();
type: Api.Sys.Core.DictionaryType; type RuleKey = 'name' | 'code' | 'type';
description: string | null; const rules: Record<RuleKey, App.Global.FormRule> = {
children: Api.Sys.Core.DictionaryItem[]; name: defaultRequiredRule,
} code: defaultRequiredRule,
type: defaultRequiredRule
};
interface ChildrenOperateModel { // ******************** 事件定义 ********************
id: string | null;
name: string;
code: string;
sort: number;
description: string | null;
}
const model = ref(createDefaultModel()); function handleInitModel() {
model.value = createDefaultModel();
const childrenOperateModel = ref(createDefaultModel2()); if (props.operateType === 'edit' && props.rowData) {
Object.assign(model.value, jsonClone(props.rowData));
const childrenModelVisible = ref(false);
const childrenTitle = ref('');
const childrenModelOperateType = ref('');
function showChildrenModel(operateType: NaiveUI.TableOperateType, row: DictionaryItem | null = null) {
childrenModelOperateType.value = operateType;
childrenTitle.value = operateType === 'edit' ? $t('common.edit') : $t('common.add');
childrenModelVisible.value = true;
childrenOperateModel.value = createDefaultModel2();
if (row) {
Object.assign(childrenOperateModel.value, jsonClone(row));
} }
} }
const childrenColumns: DataTableColumns<DictionaryItem> = [ async function handleSubmit() {
await validate();
const isEdit = props.operateType === 'edit';
const opFunc = isEdit ? fetchDictionaryUpdate : fetchDictionaryInsert;
opFunc(model.value).then(() => {
window.$message?.success($t('common.updateSuccess'));
visible.value = false;
emit('submitted');
});
}
// ******************** 子项数据定义 ********************
// Model
const childrenModel = ref(createChildrenModel());
function createChildrenModel(): Api.Sys.Core.DictionaryItemDTO {
return {
id: null,
dictionaryId: '',
name: '',
code: '',
sort: 0,
description: null
};
}
// Column
const childrenColumns: DataTableColumns<Api.Sys.Core.DictionaryItemVO> = [
{ {
key: 'name', key: 'name',
title: $t('page.sys.core.dictionary.item.fields.name'), title: $t('page.sys.core.dictionary.item.fields.name'),
@ -143,113 +169,80 @@ const childrenColumns: DataTableColumns<DictionaryItem> = [
} }
} }
]; ];
// Data
function createDefaultModel(): Model { const childrenData = ref(createChildrenData());
interface ChildrenData {
records: Api.Sys.Core.DictionaryItemVO[];
}
function createChildrenData(): ChildrenData {
return { return {
name: '', records: []
code: '',
type: 'enum',
description: null,
children: []
}; };
} }
// 标题
const childrenModelTitle = ref('');
const childrenModelVisible = ref(false);
const childrenModelOperateType = ref('');
function createDefaultModel2(): ChildrenOperateModel { // ******************** 子项事件定义 ********************
return {
id: null,
name: '',
code: '',
sort: 0,
description: null
};
}
type RuleKey = 'name' | 'code' | 'type'; function handleInitChildrenData() {
const rules: Record<RuleKey, App.Global.FormRule> = {
name: defaultRequiredRule,
code: defaultRequiredRule,
type: defaultRequiredRule
};
function handleInitModel() {
model.value = createDefaultModel();
if (props.operateType === 'edit' && props.rowData) {
Object.assign(model.value, jsonClone(props.rowData));
}
}
function handleInitChildrenModel() {
if (props.operateType !== 'edit' || !props.rowData?.id) { if (props.operateType !== 'edit' || !props.rowData?.id) {
return; return;
} }
fetchTreeDictionaryItem(props.rowData.id).then(res => { fetchDictionaryItemTreeList(props.rowData.id).then(res => {
if (res.data) { if (res.data) {
model.value.children = res.data ? res.data : []; childrenData.value.records = res.data ? res.data : [];
} }
}); });
} }
function showChildrenModel(operateType: NaiveUI.TableOperateType, row: Api.Sys.Core.DictionaryItemVO | null) {
childrenModelOperateType.value = operateType;
childrenModelTitle.value = operateType === 'edit' ? $t('common.edit') : $t('common.add');
childrenModelVisible.value = true;
childrenModel.value = createChildrenModel();
if (row) {
Object.assign(childrenModel.value, jsonClone(row));
}
}
function handleChildrenSubmit() { function handleChildrenSubmit() {
const apiParams: Api.Sys.Core.DictionaryItemOp = { const apiParams = childrenModel.value;
dictionaryId: props.rowData?.id || '',
...childrenOperateModel.value
};
if (childrenModelOperateType.value === 'edit') { if (childrenModelOperateType.value === 'edit') {
// 编辑 // 编辑
fetchDictionaryItemEdit(apiParams).then(() => { fetchDictionaryItemUpdate(apiParams).then(() => {
window.$message?.success($t('common.updateSuccess')); window.$message?.success($t('common.updateSuccess'));
childrenModelVisible.value = false; childrenModelVisible.value = false;
handleInitChildrenModel(); handleInitChildrenData();
}); });
} else { } else {
// 新增 // 新增
fetchDictionaryItemAdd(apiParams).then(() => { fetchDictionaryItemInsert(apiParams).then(() => {
window.$message?.success($t('common.updateSuccess')); window.$message?.success($t('common.updateSuccess'));
childrenModelVisible.value = false; childrenModelVisible.value = false;
handleInitChildrenModel(); handleInitChildrenData();
}); });
} }
} }
function handleChildrenDelete(row: DictionaryItem, index: number) { function handleChildrenDelete(row: Api.Sys.Core.DictionaryItemVO, index: number) {
if (row.id) { if (row.id) {
// 执行删除 // 执行删除
fetchDictionaryItemDelete(row.id).then(() => { fetchDictionaryItemDelete(row.id).then(() => {
window.$message?.success($t('common.deleteSuccess')); window.$message?.success($t('common.deleteSuccess'));
handleInitChildrenModel(); handleInitChildrenData();
}); });
} else { } else if (childrenData.value.records) {
model.value.children = model.value.children.splice(index); childrenData.value.records = childrenData.value.records.splice(index);
} }
} }
function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
await validate();
const isEdit = props.operateType === 'edit';
const opFunc = isEdit ? fetchDictionaryEdit : fetchDictionaryAdd;
const opData: Api.Sys.Core.DictionaryOp = {
id: props.rowData?.id || null,
...model.value
};
opFunc(opData).then(() => {
window.$message?.success($t('common.updateSuccess'));
closeDrawer();
emit('submitted');
});
}
watch(visible, () => { watch(visible, () => {
if (visible.value) { if (visible.value) {
handleInitModel(); handleInitModel();
handleInitChildrenData();
restoreValidation(); restoreValidation();
handleInitChildrenModel();
} }
}); });
</script> </script>
@ -286,43 +279,43 @@ watch(visible, () => {
<NCard v-if="props.operateType === 'edit'" :title="$t('page.sys.core.dictionary.item.title')"> <NCard v-if="props.operateType === 'edit'" :title="$t('page.sys.core.dictionary.item.title')">
<template #header-extra> <template #header-extra>
<NSpace> <NSpace>
<NButton type="primary" @click="showChildrenModel('add')"> <NButton type="primary" @click="showChildrenModel('add', null)">
{{ $t('common.add') }} {{ $t('common.add') }}
</NButton> </NButton>
</NSpace> </NSpace>
</template> </template>
<NDataTable :data="model.children" :columns="childrenColumns" :bordered="true" /> <NDataTable :data="childrenData.records" :columns="childrenColumns" :bordered="true" />
<NModal <NModal
v-model:show="childrenModelVisible" v-model:show="childrenModelVisible"
preset="dialog" preset="dialog"
:title="childrenTitle" :title="childrenModelTitle"
:positive-text="$t('common.confirm')" :positive-text="$t('common.confirm')"
:negative-text="$t('common.cancel')" :negative-text="$t('common.cancel')"
@positive-click="handleChildrenSubmit" @positive-click="handleChildrenSubmit"
@negative-click="childrenModelVisible = false" @negative-click="childrenModelVisible = false"
> >
<NForm :model="childrenOperateModel"> <NForm :model="childrenModel">
<NFormItem :label="$t('page.sys.core.dictionary.item.fields.name')" path="name"> <NFormItem :label="$t('page.sys.core.dictionary.item.fields.name')" path="name">
<NInput <NInput
v-model:value="childrenOperateModel.name" v-model:value="childrenModel.name"
:placeholder="$t('page.sys.core.dictionary.item.fields.name')" :placeholder="$t('page.sys.core.dictionary.item.fields.name')"
/> />
</NFormItem> </NFormItem>
<NFormItem :label="$t('page.sys.core.dictionary.item.fields.code')" path="code"> <NFormItem :label="$t('page.sys.core.dictionary.item.fields.code')" path="code">
<NInput <NInput
v-model:value="childrenOperateModel.code" v-model:value="childrenModel.code"
:placeholder="$t('page.sys.core.dictionary.item.fields.code')" :placeholder="$t('page.sys.core.dictionary.item.fields.code')"
/> />
</NFormItem> </NFormItem>
<NFormItem :label="$t('page.sys.core.dictionary.item.fields.sort')" path="sort"> <NFormItem :label="$t('page.sys.core.dictionary.item.fields.sort')" path="sort">
<NInputNumber <NInputNumber
v-model:value="childrenOperateModel.sort" v-model:value="childrenModel.sort"
:placeholder="$t('page.sys.core.dictionary.item.fields.sort')" :placeholder="$t('page.sys.core.dictionary.item.fields.sort')"
/> />
</NFormItem> </NFormItem>
<NFormItem :label="$t('page.sys.core.dictionary.item.fields.description')" path="description"> <NFormItem :label="$t('page.sys.core.dictionary.item.fields.description')" path="description">
<NInput <NInput
v-model:value="childrenOperateModel.description" v-model:value="childrenModel.description"
:placeholder="$t('page.sys.core.dictionary.item.fields.description')" :placeholder="$t('page.sys.core.dictionary.item.fields.description')"
type="textarea" type="textarea"
/> />
@ -333,7 +326,7 @@ watch(visible, () => {
</NScrollbar> </NScrollbar>
<template #footer> <template #footer>
<NSpace :size="16"> <NSpace :size="16">
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton> <NButton @click="visible = false">{{ $t('common.cancel') }}</NButton>
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton> <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
</NSpace> </NSpace>
</template> </template>

View File

@ -5,20 +5,24 @@ import { dictionaryTypeOptions } from '@/constants/sys/core/dictionary';
import { translateOptions } from '@/utils/common'; import { translateOptions } from '@/utils/common';
import { $t } from '@/locales'; import { $t } from '@/locales';
// ******************** 数据定义 ********************
// 定义组件
defineOptions({ defineOptions({
name: 'DictionarySearch' name: 'DictionarySearch'
}); });
// 定义触发
interface Emits { interface Emits {
(e: 'search'): void; (e: 'search'): void;
} }
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
const model = defineModel<Api.Sys.Core.DictionaryQueryPageRequest>('model', { required: true }); // 定义数据
const model = defineModel<Api.Common.PageRequest<Api.Sys.Core.DictionaryDTO>>('model', { required: true });
const defaultModel = jsonClone(toRaw(model.value)); const defaultModel = jsonClone(toRaw(model.value));
// ******************** 触发事件 ********************
function resetModel() { function resetModel() {
Object.assign(model.value, defaultModel); Object.assign(model.value, defaultModel);
} }