feat-wip(components): 数据字典相关页面代码提交
This commit is contained in:
@ -8,6 +8,7 @@ defineOptions({
|
||||
interface Props {
|
||||
itemAlign?: NaiveUI.Align;
|
||||
disabledDelete?: boolean;
|
||||
disabledAdd?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ function refresh() {
|
||||
<NSpace :align="itemAlign" wrap justify="end" class="lt-sm:w-200px">
|
||||
<slot name="prefix"></slot>
|
||||
<slot name="default">
|
||||
<NButton size="small" ghost type="primary" @click="add">
|
||||
<NButton size="small" ghost type="primary" :disabled="disabledAdd" @click="add">
|
||||
<template #icon>
|
||||
<icon-ic-round-plus class="text-icon" />
|
||||
</template>
|
||||
|
||||
8
src/constants/sys/core/dictionary.ts
Normal file
8
src/constants/sys/core/dictionary.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { transformRecordToOption } from '@/utils/common';
|
||||
|
||||
export const dictionaryTypeRecord: Record<Api.Sys.Core.DictionaryType, App.I18n.I18nKey> = {
|
||||
enum: 'page.sys.core.dictionary.options.type.enum',
|
||||
tree: 'page.sys.core.dictionary.options.type.tree'
|
||||
};
|
||||
|
||||
export const dictionaryTypeOptions = transformRecordToOption(dictionaryTypeRecord);
|
||||
@ -230,17 +230,17 @@ export function useTableOperate<TableData>(
|
||||
}
|
||||
|
||||
export function defaultTransform<ApiData>(
|
||||
response: FlatResponseData<any, Api.Common.PaginatingQueryRecord<ApiData>>
|
||||
response: FlatResponseData<any, Api.Common.PageResponse<ApiData>>
|
||||
): PaginationData<ApiData> {
|
||||
const { data, error } = response;
|
||||
|
||||
if (!error) {
|
||||
const { records, current, size, total } = data;
|
||||
const { records, pageNumber, pageSize, total } = data;
|
||||
|
||||
return {
|
||||
data: records,
|
||||
pageNum: current,
|
||||
pageSize: size,
|
||||
pageNum: pageNumber,
|
||||
pageSize,
|
||||
total
|
||||
};
|
||||
}
|
||||
|
||||
@ -320,6 +320,15 @@ const local: App.I18n.Schema = {
|
||||
description: 'Description',
|
||||
createTime: 'Create Time',
|
||||
updateTime: 'Update Time'
|
||||
},
|
||||
options: {
|
||||
type: {
|
||||
enum: 'Enum',
|
||||
tree: 'Tree'
|
||||
}
|
||||
},
|
||||
item: {
|
||||
title: 'Item'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -317,6 +317,15 @@ const local: App.I18n.Schema = {
|
||||
description: '描述',
|
||||
createTime: '创建时间',
|
||||
updateTime: '修改时间'
|
||||
},
|
||||
options: {
|
||||
type: {
|
||||
enum: '枚举',
|
||||
tree: '数型'
|
||||
}
|
||||
},
|
||||
item: {
|
||||
title: '字典项'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export * from './auth';
|
||||
export * from './route';
|
||||
export * from './sys/core/dictionary';
|
||||
|
||||
45
src/service/api/sys/core/dictionary.ts
Normal file
45
src/service/api/sys/core/dictionary.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { request } from '../../../request';
|
||||
|
||||
export function fetchPageDictionary(pageRequest: Api.Sys.Core.DictionaryQueryPageRequest) {
|
||||
return request<Api.Common.PageResponse<Api.Sys.Core.Dictionary>>({
|
||||
url: '/dictionary/page',
|
||||
method: 'post',
|
||||
data: pageRequest
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchDictionaryAdd(dictionaryOp: Api.Sys.Core.DictionaryOp) {
|
||||
return request({
|
||||
url: '/dictionary/add',
|
||||
method: 'post',
|
||||
data: dictionaryOp
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchDictionaryEdit(dictionaryOp: Api.Sys.Core.DictionaryOp) {
|
||||
return request({
|
||||
url: '/dictionary/edit',
|
||||
method: 'post',
|
||||
data: dictionaryOp
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchDictionaryDelete(id: string) {
|
||||
return request({
|
||||
url: '/dictionary/delete',
|
||||
method: 'post',
|
||||
data: {
|
||||
id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchDictionaryDeleteBatch(ids: string[]) {
|
||||
return request({
|
||||
url: '/dictionary/deleteBatch',
|
||||
method: 'post',
|
||||
data: {
|
||||
ids
|
||||
}
|
||||
});
|
||||
}
|
||||
51
src/typings/api/common.d.ts
vendored
51
src/typings/api/common.d.ts
vendored
@ -5,46 +5,23 @@
|
||||
*/
|
||||
declare namespace Api {
|
||||
namespace Common {
|
||||
/** common params of paginating */
|
||||
interface PaginatingCommonParams {
|
||||
/** current page number */
|
||||
current: number;
|
||||
/** page size */
|
||||
size: number;
|
||||
/** total count */
|
||||
total: number;
|
||||
/** 分页请求 */
|
||||
interface PageRequest {
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
/** common params of paginating query list data */
|
||||
interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams {
|
||||
/** 带查询参数的分页请求 */
|
||||
interface QueryPageRequest<T> extends PageRequest {
|
||||
query: T;
|
||||
}
|
||||
|
||||
/** 分页响应 */
|
||||
interface PageResponse<T> {
|
||||
total: number;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
records: T[];
|
||||
}
|
||||
|
||||
/** common search params of table */
|
||||
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>;
|
||||
|
||||
/**
|
||||
* enable status
|
||||
*
|
||||
* - "1": enabled
|
||||
* - "2": disabled
|
||||
*/
|
||||
type EnableStatus = '1' | '2';
|
||||
|
||||
/** common record */
|
||||
type CommonRecord<T = any> = {
|
||||
/** record id */
|
||||
id: number;
|
||||
/** record creator */
|
||||
createBy: string;
|
||||
/** record create time */
|
||||
createTime: string;
|
||||
/** record updater */
|
||||
updateBy: string;
|
||||
/** record update time */
|
||||
updateTime: string;
|
||||
/** record status */
|
||||
status: EnableStatus | null;
|
||||
} & T;
|
||||
}
|
||||
}
|
||||
|
||||
40
src/typings/api/sys/core.d.ts
vendored
Normal file
40
src/typings/api/sys/core.d.ts
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
declare namespace Api {
|
||||
namespace Sys {
|
||||
namespace Core {
|
||||
// ******************** sys_core_dictionary ********************
|
||||
type DictionaryType = 'enum' | 'tree';
|
||||
interface Dictionary {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
type: DictionaryType;
|
||||
description: string | null;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
}
|
||||
interface DictionaryQuery {
|
||||
name: string | null;
|
||||
code: string | null;
|
||||
type: string | null;
|
||||
}
|
||||
type DictionaryQueryPageRequest = Api.Common.QueryPageRequest<DictionaryQuery>;
|
||||
interface DictionaryOp {
|
||||
id: string | null;
|
||||
name: string;
|
||||
code: string;
|
||||
type: DictionaryType;
|
||||
description: string | null;
|
||||
}
|
||||
interface DictionaryItem {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
sort: number;
|
||||
description: string | null;
|
||||
createTime: string | null;
|
||||
updateTime: string | null;
|
||||
children: DictionaryItem[];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/typings/app.d.ts
vendored
9
src/typings/app.d.ts
vendored
@ -559,6 +559,15 @@ declare namespace App {
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
};
|
||||
options: {
|
||||
type: {
|
||||
enum: string;
|
||||
tree: string;
|
||||
};
|
||||
};
|
||||
item: {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
rbac: {
|
||||
|
||||
28
src/typings/components.d.ts
vendored
28
src/typings/components.d.ts
vendored
@ -21,14 +21,21 @@ declare module 'vue' {
|
||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
@ -41,7 +48,10 @@ declare module 'vue' {
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
@ -50,6 +60,7 @@ declare module 'vue' {
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
@ -62,7 +73,10 @@ declare module 'vue' {
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
@ -98,14 +112,21 @@ declare global {
|
||||
const FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||
const IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||
const IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||
const IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||
const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
const IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||
const IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||
const IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
const IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
const IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
const IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||
const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
const IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||
const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||
const IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||
const IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
@ -118,7 +139,10 @@ declare global {
|
||||
const NButton: typeof import('naive-ui')['NButton']
|
||||
const NCard: typeof import('naive-ui')['NCard']
|
||||
const NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
const NCollapse: typeof import('naive-ui')['NCollapse']
|
||||
const NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||
const NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
const NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
const NDivider: typeof import('naive-ui')['NDivider']
|
||||
const NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
@ -127,6 +151,7 @@ declare global {
|
||||
const NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
const NForm: typeof import('naive-ui')['NForm']
|
||||
const NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
const NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
const NGi: typeof import('naive-ui')['NGi']
|
||||
const NGrid: typeof import('naive-ui')['NGrid']
|
||||
const NInput: typeof import('naive-ui')['NInput']
|
||||
@ -139,7 +164,10 @@ declare global {
|
||||
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
const NModal: typeof import('naive-ui')['NModal']
|
||||
const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||
const NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
const NPopover: typeof import('naive-ui')['NPopover']
|
||||
const NRadio: typeof import('naive-ui')['NRadio']
|
||||
const NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
const NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
const NSelect: typeof import('naive-ui')['NSelect']
|
||||
const NSpace: typeof import('naive-ui')['NSpace']
|
||||
|
||||
@ -1,15 +1,166 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="tsx">
|
||||
import { reactive } from 'vue';
|
||||
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||
import { dictionaryTypeRecord } from '@/constants/sys/core/dictionary';
|
||||
import { fetchDictionaryDelete, fetchDictionaryDeleteBatch, fetchPageDictionary } from '@/service/api';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import DictionarySearch from '@/views/sys/core/dictionary/modules/dictionary-search.vue';
|
||||
import DictionaryOperateDrawer from '@/views/sys/core/dictionary/modules/dictionary-operate-drawer.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const pageRequest: Api.Sys.Core.DictionaryQueryPageRequest = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
query: {
|
||||
name: null,
|
||||
code: null,
|
||||
type: null
|
||||
}
|
||||
});
|
||||
|
||||
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useNaivePaginatedTable({
|
||||
api: () => fetchPageDictionary(pageRequest),
|
||||
transform: response => defaultTransform(response),
|
||||
onPaginationParamsChange: params => {
|
||||
pageRequest.pageNumber = params.page || 1;
|
||||
pageRequest.pageSize = params.pageSize || 10;
|
||||
},
|
||||
columns: () => [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center',
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
title: $t('common.index'),
|
||||
width: 64,
|
||||
align: 'center',
|
||||
render: (_, index) => index + 1
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: $t('page.sys.core.dictionary.fields.name'),
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'code',
|
||||
title: $t('page.sys.core.dictionary.fields.code'),
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
title: $t('page.sys.core.dictionary.fields.type'),
|
||||
width: 100,
|
||||
render: row => {
|
||||
if (row.type === null) {
|
||||
return null;
|
||||
}
|
||||
return <NTag type="success">{$t(dictionaryTypeRecord[row.type])}</NTag>;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'createTime',
|
||||
title: $t('page.sys.core.dictionary.fields.createTime'),
|
||||
align: 'center',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
key: 'updateTime',
|
||||
title: $t('page.sys.core.dictionary.fields.updateTime'),
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'operate',
|
||||
title: $t('common.operate'),
|
||||
align: 'center',
|
||||
minWidth: 130,
|
||||
render: row => (
|
||||
<div class="flex-center gap-8px">
|
||||
<NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
|
||||
{$t('common.edit')}
|
||||
</NButton>
|
||||
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||
{{
|
||||
default: () => $t('common.confirmDelete'),
|
||||
trigger: () => (
|
||||
<NButton type="error" ghost size="small">
|
||||
{$t('common.delete')}
|
||||
</NButton>
|
||||
)
|
||||
}}
|
||||
</NPopconfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
||||
useTableOperate(data, 'id', getData);
|
||||
|
||||
async function handleBatchDelete() {
|
||||
fetchDictionaryDeleteBatch(checkedRowKeys.value).then(() => {
|
||||
onBatchDeleted();
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(id: string) {
|
||||
fetchDictionaryDelete(id).then(() => {
|
||||
onDeleted();
|
||||
});
|
||||
}
|
||||
|
||||
function edit(id: string) {
|
||||
handleEdit(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||
<DictionarySearch v-model:model="pageRequest" @search="getDataByPage" />
|
||||
<NCard
|
||||
:title="$t('page.sys.core.dictionary.title')"
|
||||
:bordered="false"
|
||||
size="small"
|
||||
class="card-wrapper sm:flex-1-hidden"
|
||||
></NCard>
|
||||
>
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
v-model:columns="columnChecks"
|
||||
:disabled-delete="checkedRowKeys.length === 0"
|
||||
:loading="loading"
|
||||
@add="handleAdd"
|
||||
@delete="handleBatchDelete"
|
||||
@refresh="getData"
|
||||
/>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
size="small"
|
||||
:flex-height="!appStore.isMobile"
|
||||
:scroll-x="702"
|
||||
:loading="loading"
|
||||
remote
|
||||
:row-key="row => row.id"
|
||||
:pagination="mobilePagination"
|
||||
class="sm:h-full"
|
||||
/>
|
||||
<DictionaryOperateDrawer
|
||||
v-model:visible="drawerVisible"
|
||||
:operate-type="operateType"
|
||||
:row-data="editingData"
|
||||
@submitted="getDataByPage"
|
||||
/>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -0,0 +1,233 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import type { DataTableColumns } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { dictionaryTypeOptions } from '@/constants/sys/core/dictionary';
|
||||
import { fetchDictionaryAdd, fetchDictionaryEdit } from '@/service/api';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'DictionaryOperateDrawer'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
/** the type of operation */
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.Sys.Core.Dictionary | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { defaultRequiredRule } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
add: $t('common.add'),
|
||||
edit: $t('common.edit')
|
||||
};
|
||||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
type Model = Pick<Api.Sys.Core.Dictionary, 'name' | 'code' | 'type' | 'description'>;
|
||||
|
||||
const model = ref(createDefaultModel());
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
name: '',
|
||||
code: '',
|
||||
type: 'enum',
|
||||
description: null
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Exclude<keyof Model, 'description'>;
|
||||
|
||||
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 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');
|
||||
});
|
||||
}
|
||||
|
||||
function handleAddDictionaryItem() {}
|
||||
|
||||
const dictionaryItemData: Api.Sys.Core.DictionaryItem[] = [
|
||||
{
|
||||
id: '07',
|
||||
name: '07akioni',
|
||||
code: '07akioni',
|
||||
sort: 0,
|
||||
description: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
children: [
|
||||
{
|
||||
id: '08',
|
||||
name: '08akioni',
|
||||
code: '08akioni',
|
||||
sort: 0,
|
||||
description: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
children: [
|
||||
{
|
||||
id: '09',
|
||||
name: '09akioni',
|
||||
code: '09akioni',
|
||||
children: [],
|
||||
sort: 0,
|
||||
description: null,
|
||||
createTime: null,
|
||||
updateTime: null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
name: '11akioni',
|
||||
code: '11akioni',
|
||||
children: [],
|
||||
sort: 0,
|
||||
description: null,
|
||||
createTime: null,
|
||||
updateTime: null
|
||||
}
|
||||
];
|
||||
|
||||
const newDictionaryItemData = ref<Api.Sys.Core.DictionaryItem[]>([]);
|
||||
|
||||
const dictionaryItemColumns: DataTableColumns<Api.Sys.Core.DictionaryItem> = [
|
||||
{
|
||||
type: 'selection'
|
||||
},
|
||||
{
|
||||
title: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'index',
|
||||
key: 'index'
|
||||
}
|
||||
];
|
||||
|
||||
function rowKey(row: Api.Sys.Core.DictionaryItem) {
|
||||
return row.id;
|
||||
}
|
||||
|
||||
function handleUpdateChecked() {
|
||||
const dictionaryType = model.value.type;
|
||||
if (!dictionaryType) {
|
||||
return;
|
||||
}
|
||||
newDictionaryItemData.value = cloneDeep(dictionaryItemData);
|
||||
if (dictionaryType === 'enum') {
|
||||
for (const newDictionaryItemDatum of newDictionaryItemData.value) {
|
||||
newDictionaryItemDatum.children = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleInitModel();
|
||||
restoreValidation();
|
||||
handleUpdateChecked();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" display-directive="show" :default-width="600" resizable>
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem :label="$t('page.sys.core.dictionary.fields.name')" path="name">
|
||||
<NInput v-model:value="model.name" :placeholder="$t('page.sys.core.dictionary.fields.name')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('page.sys.core.dictionary.fields.code')" path="code">
|
||||
<NInput v-model:value="model.code" :placeholder="$t('page.sys.core.dictionary.fields.code')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('page.sys.core.dictionary.fields.type')" path="type">
|
||||
<NRadioGroup v-model:value="model.type" @change="handleUpdateChecked">
|
||||
<NRadio
|
||||
v-for="item in dictionaryTypeOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="$t(item.label)"
|
||||
/>
|
||||
</NRadioGroup>
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('page.sys.core.dictionary.fields.description')" path="description">
|
||||
<NInput
|
||||
v-model:value="model.description"
|
||||
:placeholder="$t('page.sys.core.dictionary.fields.description')"
|
||||
type="textarea"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<NCard :title="$t('page.sys.core.dictionary.item.title')" hoverable embedded>
|
||||
<template #header-extra>
|
||||
<NSpace wrap justify="end" class="lt-sm:w-200px">
|
||||
<NButton size="small" ghost type="primary" @click="handleAddDictionaryItem">
|
||||
<template #icon>
|
||||
<icon-ic-round-plus class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.add') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NCard>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
<NDataTable :columns="dictionaryItemColumns" :data="newDictionaryItemData" :row-key="rowKey" />
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
89
src/views/sys/core/dictionary/modules/dictionary-search.vue
Normal file
89
src/views/sys/core/dictionary/modules/dictionary-search.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { dictionaryTypeOptions } from '@/constants/sys/core/dictionary';
|
||||
import { translateOptions } from '@/utils/common';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'DictionarySearch'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const model = defineModel<Api.Sys.Core.DictionaryQueryPageRequest>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
function search() {
|
||||
emit('search');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse :default-expanded-names="['role-search']">
|
||||
<NCollapseItem :title="$t('common.search')" name="role-search">
|
||||
<NForm :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
:label="$t('page.sys.core.dictionary.fields.name')"
|
||||
path="name"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.query.name" :placeholder="$t('page.sys.core.dictionary.fields.name')" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
:label="$t('page.sys.core.dictionary.fields.code')"
|
||||
path="code"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.query.code" :placeholder="$t('page.sys.core.dictionary.fields.code')" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
:label="$t('page.sys.core.dictionary.fields.type')"
|
||||
path="type"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NSelect
|
||||
v-model:value="model.query.type"
|
||||
:placeholder="$t('page.sys.core.dictionary.fields.type')"
|
||||
:options="translateOptions(dictionaryTypeOptions)"
|
||||
clearable
|
||||
/>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="resetModel">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user