diff --git a/eslint.config.js b/eslint.config.js index c2329f66..3762778a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -9,6 +9,13 @@ export default defineConfig( { ignores: ['index', 'App', '[id]'] } + ], + 'vue/component-name-in-template-casing': [ + 'warn', + 'PascalCase', + { + ignores: ['/^icon-/'] + } ] } } diff --git a/package.json b/package.json index 1231fbda..9a110432 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "nprogress": "0.2.0", "pinia": "2.1.7", "vue": "3.4.15", + "vue-draggable-plus": "^0.3.5", "vue-i18n": "9.9.0", "vue-router": "4.2.5" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0618fb10..3c8e7130 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: vue: specifier: 3.4.15 version: 3.4.15(typescript@5.3.3) + vue-draggable-plus: + specifier: ^0.3.5 + version: 0.3.5(@types/sortablejs@1.15.7) vue-i18n: specifier: 9.9.0 version: 9.9.0(vue@3.4.15) @@ -1869,6 +1872,10 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true + /@types/sortablejs@1.15.7: + resolution: {integrity: sha512-PvgWCx1Lbgm88FdQ6S7OGvLIjWS66mudKPlfdrWil0TjsO5zmoZmzoKiiwRShs1dwPgrlkr0N4ewuy0/+QUXYQ==} + dev: false + /@types/svgo@2.6.4: resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} dependencies: @@ -8625,6 +8632,18 @@ packages: dependencies: vue: 3.4.15(typescript@5.3.3) + /vue-draggable-plus@0.3.5(@types/sortablejs@1.15.7): + resolution: {integrity: sha512-HqIxV4Wr4U5LRPLRi2oV+EJ4g6ibyRKhuaiH4ZQo+LxK4zrk2XcBk9UyXC88OXp4SAq0XYH4Wco/T3LX5kJ79A==} + peerDependencies: + '@types/sortablejs': ^1.15.0 + '@vue/composition-api': '*' + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + '@types/sortablejs': 1.15.7 + dev: false + /vue-eslint-parser@9.4.2(eslint@8.56.0): resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} engines: {node: ^14.17.0 || >=16.0.0} diff --git a/src/components/advanced/table-column-setting.vue b/src/components/advanced/table-column-setting.vue new file mode 100644 index 00000000..15730e91 --- /dev/null +++ b/src/components/advanced/table-column-setting.vue @@ -0,0 +1,36 @@ + + + + + + + + + + {{ $t('common.columnSetting') }} + + + + + + + {{ item.title }} + + + + + + + diff --git a/src/constants/business.ts b/src/constants/business.ts new file mode 100644 index 00000000..e14aa1f5 --- /dev/null +++ b/src/constants/business.ts @@ -0,0 +1,8 @@ +import { transformRecordToOption } from '@/utils/common'; + +export const roleStatusRecord: Record = { + '1': 'page.manage.role.status.enable', + '2': 'page.manage.role.status.disable' +}; + +export const roleStatusOptions = transformRecordToOption(roleStatusRecord); diff --git a/src/hooks/common/form.ts b/src/hooks/common/form.ts index 27432554..acc93560 100644 --- a/src/hooks/common/form.ts +++ b/src/hooks/common/form.ts @@ -47,7 +47,10 @@ export function useFormRules() { ] } satisfies Record; - function createRequiredRule(message: string) { + /** the default required rule */ + const defaultRequiredRule = createRequiredRule($t('form.required')); + + function createRequiredRule(message: string): App.Global.FormRule { return { required: true, message @@ -56,6 +59,7 @@ export function useFormRules() { return { constantRules, + defaultRequiredRule, createRequiredRule }; } diff --git a/src/hooks/common/table.ts b/src/hooks/common/table.ts new file mode 100644 index 00000000..b41dcf68 --- /dev/null +++ b/src/hooks/common/table.ts @@ -0,0 +1,223 @@ +import { computed, effectScope, onScopeDispose, reactive, ref, watch } from 'vue'; +import type { Ref } from 'vue'; +import type { DataTableBaseColumn, DataTableExpandColumn, DataTableSelectionColumn, PaginationProps } from 'naive-ui'; +import type { TableColumnGroup } from 'naive-ui/es/data-table/src/interface'; +import { useBoolean, useLoading } from '@sa/hooks'; +import { useAppStore } from '@/store/modules/app'; + +type BaseData = Record; + +type ApiFn = (args: any) => Promise; + +export type TableColumn = + | (Omit, 'key'> & { key: keyof T | CustomColumnKey }) + | (Omit, 'key'> & { key: keyof T | CustomColumnKey }) + | DataTableSelectionColumn + | DataTableExpandColumn; + +export type TransformedData = { + data: TableData[]; + pageNum: number; + pageSize: number; + total: number; +}; + +/** transform api response to table data */ +type Transformer> = ( + response: Response +) => TransformedData; + +/** table config */ +export type TableConfig = { + /** api function to get table data */ + apiFn: Fn; + /** api params */ + apiParams: Parameters[0]; + /** transform api response to table data */ + transformer: Transformer>>; + /** pagination */ + pagination?: PaginationProps; + /** + * callback when pagination changed + * + * @param pagination + */ + onPaginationChanged?: (pagination: PaginationProps) => void | Promise; + /** + * whether to get data immediately + * + * @default true + */ + immediate?: boolean; + /** columns factory */ + columns: () => TableColumn[]; +}; + +/** filter columns */ +export type FilteredColumn = { + key: string; + title: string; + checked: boolean; +}; + +export function useTable( + config: TableConfig +) { + const scope = effectScope(); + const appStore = useAppStore(); + + const { loading, startLoading, endLoading } = useLoading(); + const { bool: empty, setBool: setEmpty } = useBoolean(); + + const { apiFn, apiParams, transformer, onPaginationChanged, immediate = true } = config; + + const searchParams: NonNullable[0]> = reactive(apiParams || {}); + + const { columns, filteredColumns, reloadColumns } = useTableColumn(config.columns); + + const data: Ref = ref([]); + + const pagination = reactive({ + page: 1, + pageSize: 10, + showSizePicker: true, + pageSizes: [10, 15, 20, 25, 30], + onChange: async (page: number) => { + pagination.page = page; + + await onPaginationChanged?.(pagination); + }, + onUpdatePageSize: async (pageSize: number) => { + pagination.pageSize = pageSize; + pagination.page = 1; + + await onPaginationChanged?.(pagination); + }, + ...config.pagination + }) as PaginationProps; + + function updatePagination(update: Partial) { + Object.assign(pagination, update); + } + + async function getData() { + startLoading(); + + const response = await apiFn(searchParams); + + const { data: tableData, pageNum, pageSize, total } = transformer(response as Awaited>); + + data.value = tableData; + + setEmpty(tableData.length === 0); + updatePagination({ page: pageNum, pageSize, itemCount: total }); + endLoading(); + } + + /** + * update search params + * + * @param params + */ + function updateSearchParams(params: Partial[0]>) { + Object.assign(searchParams, params); + } + + /** reset search params */ + function resetSearchParams() { + Object.keys(searchParams).forEach(key => { + searchParams[key as keyof typeof searchParams] = undefined; + }); + } + + if (immediate) { + getData(); + } + + scope.run(() => { + watch( + () => appStore.locale, + () => { + reloadColumns(); + } + ); + }); + + onScopeDispose(() => { + scope.stop(); + }); + + return { + loading, + empty, + data, + columns, + filteredColumns, + reloadColumns, + pagination, + updatePagination, + getData, + searchParams, + resetSearchParams, + updateSearchParams + }; +} + +function useTableColumn( + factory: () => TableColumn[] +) { + const SELECTION_KEY = '__selection__'; + + const allColumns = ref(factory()) as Ref[]>; + + const filteredColumns: Ref = ref(getFilteredColumns(factory())); + + const columns = computed(() => getColumns()); + + function reloadColumns() { + allColumns.value = factory(); + } + + function getFilteredColumns(aColumns: TableColumn[]) { + const cols: FilteredColumn[] = []; + + aColumns.forEach(column => { + if (column.type === undefined) { + cols.push({ + key: column.key as string, + title: column.title as string, + checked: true + }); + } + + if (column.type === 'selection') { + cols.push({ + key: SELECTION_KEY, + title: '勾选', + checked: true + }); + } + }); + + return cols; + } + + function getColumns() { + const cols = filteredColumns.value + .filter(column => column.checked) + .map(column => { + if (column.key === SELECTION_KEY) { + return allColumns.value.find(col => col.type === 'selection'); + } + return allColumns.value.find(col => (col as DataTableBaseColumn).key === column.key); + }); + + return cols as TableColumn[]; + } + + return { + columns, + reloadColumns, + filteredColumns + }; +} diff --git a/src/layouts/modules/theme-drawer/index.vue b/src/layouts/modules/theme-drawer/index.vue index 63fa5214..aa937d5a 100644 --- a/src/layouts/modules/theme-drawer/index.vue +++ b/src/layouts/modules/theme-drawer/index.vue @@ -15,7 +15,7 @@ const appStore = useAppStore(); - + diff --git a/src/layouts/modules/theme-drawer/modules/page-fun.vue b/src/layouts/modules/theme-drawer/modules/page-fun.vue index 21a13e6d..51133893 100644 --- a/src/layouts/modules/theme-drawer/modules/page-fun.vue +++ b/src/layouts/modules/theme-drawer/modules/page-fun.vue @@ -3,6 +3,7 @@ import { computed } from 'vue'; import { $t } from '@/locales'; import { useThemeStore } from '@/store/modules/theme'; import { themePageAnimationModeOptions, themeScrollModeOptions, themeTabModeOptions } from '@/constants/app'; +import { translateOptions } from '@/utils/common'; import SettingItem from '../components/setting-item.vue'; defineOptions({ @@ -16,13 +17,6 @@ const layoutMode = computed(() => themeStore.layout.mode); const isMixLayoutMode = computed(() => layoutMode.value.includes('mix')); const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wrapper'); - -function translateOptions(options: Common.Option[]) { - return options.map(option => ({ - ...option, - label: $t(option.label as App.I18n.I18nKey) - })); -} diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts index f41a0d45..0474c8b9 100644 --- a/src/locales/langs/en-us.ts +++ b/src/locales/langs/en-us.ts @@ -3,23 +3,34 @@ const local: App.I18n.Schema = { title: 'SoybeanAdmin' }, common: { - tip: 'Tip', + action: 'Action', add: 'Add', addSuccess: 'Add Success', - edit: 'Edit', - editSuccess: 'Edit Success', + backToHome: 'Back to home', + batchDelete: 'Batch Delete', + cancel: 'Cancel', + check: 'Check', + columnSetting: 'Column Setting', + confirm: 'Confirm', delete: 'Delete', deleteSuccess: 'Delete Success', - batchDelete: 'Batch Delete', - confirm: 'Confirm', - cancel: 'Cancel', - pleaseCheckValue: 'Please check whether the value is valid', - action: 'Action', - backToHome: 'Back to home', - lookForward: 'Coming soon', - userCenter: 'User Center', + confirmDelete: 'Are you sure you want to delete?', + edit: 'Edit', + index: 'Index', logout: 'Logout', - logoutConfirm: 'Are you sure you want to log out?' + logoutConfirm: 'Are you sure you want to log out?', + lookForward: 'Coming soon', + modify: 'Modify', + modifySuccess: 'Modify Success', + operate: 'Operate', + pleaseCheckValue: 'Please check whether the value is valid', + refresh: 'Refresh', + reset: 'Reset', + search: 'Search', + tip: 'Tip', + update: 'Update', + updateSuccess: 'Update Success', + userCenter: 'User Center' }, theme: { themeSchema: { @@ -240,9 +251,31 @@ const local: App.I18n.Schema = { routeParam: 'Route Param', backTab: 'Back function_tab' } + }, + manage: { + role: { + title: 'Role List', + form: { + roleName: 'Please enter role name', + roleCode: 'Please enter role code', + roleStatus: 'Please select role status', + roleDesc: 'Please enter role description' + }, + roleName: 'Role Name', + roleCode: 'Role Code', + roleStatus: 'Role Status', + roleDesc: 'Role Description', + addRole: 'Add Role', + editRole: 'Edit Role', + status: { + enable: 'Enable', + disable: 'Disable' + } + } } }, form: { + required: 'Cannot be empty', userName: { required: 'Please enter user name', invalid: 'User name format is incorrect' diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts index 137bfacc..93df9182 100644 --- a/src/locales/langs/zh-cn.ts +++ b/src/locales/langs/zh-cn.ts @@ -3,23 +3,34 @@ const local: App.I18n.Schema = { title: 'Soybean 管理系统' }, common: { - tip: '提示', + action: '操作', add: '添加', addSuccess: '添加成功', - edit: '修改', - editSuccess: '修改成功', + backToHome: '返回首页', + batchDelete: '批量删除', + cancel: '取消', + check: '勾选', + columnSetting: '列设置', + confirm: '确认', delete: '删除', deleteSuccess: '删除成功', - batchDelete: '批量删除', - confirm: '确认', - cancel: '取消', - pleaseCheckValue: '请检查输入的值是否合法', - action: '操作', - backToHome: '返回首页', - lookForward: '敬请期待', - userCenter: '个人中心', + confirmDelete: '确认删除吗?', + edit: '编辑', + index: '序号', logout: '退出登录', - logoutConfirm: '确认退出登录吗?' + logoutConfirm: '确认退出登录吗?', + lookForward: '敬请期待', + modify: '修改', + modifySuccess: '修改成功', + operate: '操作', + pleaseCheckValue: '请检查输入的值是否合法', + refresh: '刷新', + reset: '重置', + search: '搜索', + tip: '提示', + update: '更新', + updateSuccess: '更新成功', + userCenter: '个人中心' }, theme: { themeSchema: { @@ -240,9 +251,31 @@ const local: App.I18n.Schema = { routeParam: '路由参数', backTab: '返回 function_tab' } + }, + manage: { + role: { + title: '角色列表', + form: { + roleName: '请输入角色名称', + roleCode: '请输入角色编码', + roleStatus: '请选择角色状态', + roleDesc: '请输入角色描述' + }, + roleName: '角色名称', + roleCode: '角色编码', + roleStatus: '角色状态', + roleDesc: '角色描述', + addRole: '添加角色', + editRole: '编辑角色', + status: { + enable: '启用', + disable: '禁用' + } + } } }, form: { + required: '不能为空', userName: { required: '请输入用户名', invalid: '用户名格式不正确' diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts index a0919fdf..9e047f95 100644 --- a/src/router/guard/permission.ts +++ b/src/router/guard/permission.ts @@ -35,7 +35,7 @@ export function createPermissionGuard(router: Router) { authStore.userInfo.roles.includes(SUPER_ADMIN) || authStore.userInfo.roles.some(role => routeRoles.includes(role)); - const strategicPatterns: Common.StrategicPattern[] = [ + const strategicPatterns: CommonType.StrategicPattern[] = [ // 1. if it is login route when logged in, change to the root page { condition: isLogin && to.name === loginRoute, diff --git a/src/service/api/index.ts b/src/service/api/index.ts index 89f4e581..c9d31d11 100644 --- a/src/service/api/index.ts +++ b/src/service/api/index.ts @@ -1,2 +1,3 @@ export * from './auth'; export * from './route'; +export * from './system-manage'; diff --git a/src/service/api/system-manage.ts b/src/service/api/system-manage.ts new file mode 100644 index 00000000..daab6ab8 --- /dev/null +++ b/src/service/api/system-manage.ts @@ -0,0 +1,10 @@ +import { request } from '../request'; + +/** get role list */ +export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) { + return request({ + url: '/systemManage/getRoleList', + method: 'get', + params + }); +} diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index eb24a34f..965374c3 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -4,10 +4,41 @@ * All backend api type */ declare namespace Api { + namespace Common { + /** common params of paginating */ + interface PaginatingCommonParams { + /** current page number */ + current: number; + /** page size */ + size: number; + /** total count */ + total: number; + } + + /** common params of paginating query list data */ + interface PaginatingQueryRecord> extends PaginatingCommonParams { + records: T[]; + } + + /** common record */ + type CommonRecord> = { + /** record id */ + id: number; + /** record creator */ + createBy: string; + /** record create time */ + createTime: string; + /** record updater */ + updateBy: string; + /** record update time */ + updateTime: string; + } & T; + } + /** - * Namespace Auth + * namespace Auth * - * Backend api module: "auth" + * backend api module: "auth" */ namespace Auth { interface LoginToken { @@ -23,9 +54,9 @@ declare namespace Api { } /** - * Namespace Route + * namespace Route * - * Backend api module: "route" + * backend api module: "route" */ namespace Route { type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute; @@ -39,4 +70,40 @@ declare namespace Api { home: import('@elegant-router/types').LastLevelRouteKey; } } + + /** + * namespace SystemManage + * + * backend api module: "systemManage" + */ + namespace SystemManage { + /** + * role status + * + * - "1": enabled + * - "2": disabled + */ + type RoleStatus = '1' | '2'; + + /** role */ + type Role = Common.CommonRecord<{ + /** role name */ + roleName: string; + /** role code */ + roleCode: string; + /** role description */ + roleDesc: string; + /** role status */ + roleStatus: RoleStatus | null; + }>; + + /** role search params */ + type RoleSearchParams = CommonType.RecordNullable< + Pick & + Pick + >; + + /** role list */ + type RoleList = Common.PaginatingQueryRecord; + } } diff --git a/src/typings/app.d.ts b/src/typings/app.d.ts index b62ae307..03a3b2c0 100644 --- a/src/typings/app.d.ts +++ b/src/typings/app.d.ts @@ -249,23 +249,34 @@ declare namespace App { title: string; }; common: { - tip: string; + action: string; add: string; addSuccess: string; - edit: string; - editSuccess: string; + backToHome: string; + batchDelete: string; + cancel: string; + check: string; + columnSetting: string; + confirm: string; delete: string; deleteSuccess: string; - batchDelete: string; - confirm: string; - cancel: string; - pleaseCheckValue: string; - action: string; - backToHome: string; - lookForward: string; - userCenter: string; + confirmDelete: string; + edit: string; + index: string; logout: string; logoutConfirm: string; + lookForward: string; + modify: string; + modifySuccess: string; + operate: string; + pleaseCheckValue: string; + refresh: string; + reset: string; + search: string; + tip: string; + update: string; + updateSuccess: string; + userCenter: string; }; theme: { themeSchema: { title: string } & Record; @@ -428,8 +439,30 @@ declare namespace App { backTab: string; }; }; + manage: { + role: { + title: string; + form: { + roleName: string; + roleCode: string; + roleStatus: string; + roleDesc: string; + }; + roleName: string; + roleCode: string; + roleStatus: string; + roleDesc: string; + addRole: string; + editRole: string; + status: { + enable: string; + disable: string; + }; + }; + }; }; form: { + required: string; userName: FormMsg; phone: FormMsg; pwd: FormMsg; diff --git a/src/typings/common.d.ts b/src/typings/common.d.ts index feb39976..2489bb22 100644 --- a/src/typings/common.d.ts +++ b/src/typings/common.d.ts @@ -1,5 +1,5 @@ /** The common type namespace */ -declare namespace Common { +declare namespace CommonType { /** The strategic pattern */ interface StrategicPattern { /** The condition */ @@ -17,4 +17,9 @@ declare namespace Common { type Option = { value: K; label: string }; type YesOrNo = 'Y' | 'N'; + + /** add null to all properties */ + type RecordNullable = { + [K in keyof T]?: T[K] | null; + }; } diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index d44231ba..c039d3fc 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -16,10 +16,18 @@ declare module 'vue' { ExceptionBase: typeof import('./../components/common/exception-base.vue')['default'] FullScreen: typeof import('./../components/common/full-screen.vue')['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'] + IconIcRoundSettings: typeof import('~icons/ic/round-settings')['default'] IconLocalBanner: typeof import('~icons/local/banner')['default'] IconLocalLogo: typeof import('~icons/local/logo')['default'] + IconMdiDrag: typeof import('~icons/mdi/drag')['default'] + IconMdiRefresh: typeof import('~icons/mdi/refresh')['default'] LangSwitch: typeof import('./../components/common/lang-switch.vue')['default'] LookForward: typeof import('./../components/custom/look-forward.vue')['default'] MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default'] @@ -30,6 +38,7 @@ declare module 'vue' { NCard: typeof import('naive-ui')['NCard'] NCheckbox: typeof import('naive-ui')['NCheckbox'] NColorPicker: typeof import('naive-ui')['NColorPicker'] + NDataTable: typeof import('naive-ui')['NDataTable'] NDescriptions: typeof import('naive-ui')['NDescriptions'] NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem'] NDialogProvider: typeof import('naive-ui')['NDialogProvider'] @@ -39,6 +48,7 @@ declare module 'vue' { NDropdown: typeof import('naive-ui')['NDropdown'] 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'] NGridItem: typeof import('naive-ui')['NGridItem'] @@ -51,6 +61,7 @@ declare module 'vue' { NMenu: typeof import('naive-ui')['NMenu'] NMessageProvider: typeof import('naive-ui')['NMessageProvider'] NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] + NPopover: typeof import('naive-ui')['NPopover'] NSelect: typeof import('naive-ui')['NSelect'] NSpace: typeof import('naive-ui')['NSpace'] NStatistic: typeof import('naive-ui')['NStatistic'] @@ -67,6 +78,8 @@ declare module 'vue' { SoybeanAvatar: typeof import('./../components/custom/soybean-avatar.vue')['default'] SvgIcon: typeof import('./../components/custom/svg-icon.vue')['default'] SystemLogo: typeof import('./../components/common/system-logo.vue')['default'] + TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default'] + TableColumnSettings: typeof import('./../components/advanced/table-column-settings.vue')['default'] ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default'] WaveBg: typeof import('./../components/custom/wave-bg.vue')['default'] } diff --git a/src/typings/env.d.ts b/src/typings/env.d.ts index ca962d92..127d0483 100644 --- a/src/typings/env.d.ts +++ b/src/typings/env.d.ts @@ -30,7 +30,7 @@ declare namespace Env { * * Only valid in the development environment */ - readonly VITE_HTTP_PROXY?: Common.YesOrNo; + readonly VITE_HTTP_PROXY?: CommonType.YesOrNo; /** The back service env */ readonly VITE_SERVICE_ENV?: App.Service.EnvType; /** @@ -54,7 +54,7 @@ declare namespace Env { */ readonly VITE_MENU_ICON: string; /** Whether to build with sourcemap */ - readonly VITE_SOURCE_MAP?: Common.YesOrNo; + readonly VITE_SOURCE_MAP?: CommonType.YesOrNo; /** * Iconify api provider url * diff --git a/src/utils/common.ts b/src/utils/common.ts index 710952ff..ddb31254 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -1,3 +1,5 @@ +import { $t } from '@/locales'; + /** * Transform record to option * @@ -20,5 +22,17 @@ export function transformRecordToOption>(record return Object.entries(record).map(([value, label]) => ({ value, label - })) as Common.Option[]; + })) as CommonType.Option[]; +} + +/** + * Translate options + * + * @param options + */ +export function translateOptions(options: CommonType.Option[]) { + return options.map(option => ({ + ...option, + label: $t(option.label as App.I18n.I18nKey) + })); } diff --git a/src/views/manage/role/index.vue b/src/views/manage/role/index.vue index 2a654c50..dbcace47 100644 --- a/src/views/manage/role/index.vue +++ b/src/views/manage/role/index.vue @@ -1,7 +1,225 @@ - + - + + + + + + + + + + + + + + + + + + + + {{ $t('common.reset') }} + + + + + + {{ $t('common.search') }} + + + + + + + + + + + + + + {{ $t('common.add') }} + + + + + + + + {{ $t('common.batchDelete') }} + + + {{ $t('common.confirmDelete') }} + + + + + + {{ $t('common.refresh') }} + + + + + + + + diff --git a/src/views/manage/role/operate-role-drawer.vue b/src/views/manage/role/operate-role-drawer.vue new file mode 100644 index 00000000..64041d34 --- /dev/null +++ b/src/views/manage/role/operate-role-drawer.vue @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + {{ $t('common.cancel') }} + {{ $t('common.confirm') }} + + + + + + + diff --git a/src/views/manage/route/index.vue b/src/views/manage/route/index.vue index 2e0d3508..b91e74eb 100644 --- a/src/views/manage/route/index.vue +++ b/src/views/manage/route/index.vue @@ -1,10 +1,8 @@