feat(product): 新增产品管理模块与字典组件功能

- 新增产品管理相关路由和页面(dashboard、list、requirement、setting)
- 实现产品基础信息编辑弹窗组件(base-info-dialog.vue)
- 添加运行时字典功能(dict-select、dict-text、dict-tag组件)
- 集成字典管理store和API调用
- 规范ID类型定义为string避免精度丢失问题
- 完善国际化资源文件支持中英文对照
- 新增对象上下文业务域入口页导航实现说明
- 添加Vue DevTools浮动入口注释说明
- 统一权限控制支持全局和对象作用域区分
- 规范分页查询参数类型定义与使用方式
This commit is contained in:
2026-04-23 09:05:55 +08:00
parent c5911ea34b
commit 4122dfa50d
95 changed files with 9581 additions and 801 deletions

View File

@@ -1,7 +1,9 @@
import { useAuthStore } from '@/store/modules/auth';
import { useObjectContextStore } from '@/store/modules/object-context';
export function useAuth() {
const authStore = useAuthStore();
const objectContextStore = useObjectContextStore();
function hasAuth(codes: string | string[]) {
if (!authStore.isLogin) {
@@ -15,7 +17,14 @@ export function useAuth() {
return codes.some(code => authStore.userInfo.buttons.includes(code));
}
function hasObjectAuth(codes: string | string[]) {
const targetCodes = typeof codes === 'string' ? [codes] : codes;
return targetCodes.some(code => objectContextStore.buttonCodes.includes(code));
}
return {
hasAuth
hasAuth,
hasObjectAuth
};
}

View File

@@ -0,0 +1,86 @@
import { computed, toValue } from 'vue';
import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';
import { useDictStore } from '@/store/modules/dict';
type DictCode = string | Ref<string> | ComputedRef<string>;
type DictValue = string | number | null | undefined;
type DictValueList = Array<DictValue> | null | undefined;
type DictFilterOptions = {
onlyEnabled?: boolean;
};
type DictLabelOptions = string | (DictFilterOptions & { fallback?: string });
type DictLabelsOptions = string | (DictFilterOptions & { fallback?: string; separator?: string });
function normalizeLabelOptions(options?: DictLabelOptions) {
if (typeof options === 'string') {
return {
fallback: options,
onlyEnabled: false
};
}
return {
fallback: options?.fallback ?? '--',
onlyEnabled: options?.onlyEnabled ?? false
};
}
function normalizeLabelsOptions(options?: DictLabelsOptions) {
if (typeof options === 'string') {
return {
fallback: options,
separator: ' / ',
onlyEnabled: false
};
}
return {
fallback: options?.fallback ?? '--',
separator: options?.separator ?? ' / ',
onlyEnabled: options?.onlyEnabled ?? false
};
}
export function useDict(dictCode: DictCode | MaybeRefOrGetter<string>) {
const dictStore = useDictStore();
const currentDictCode = computed(() => toValue(dictCode));
const dictData = computed(() => dictStore.getDictData(currentDictCode.value));
const enabledDictData = computed(() => dictStore.getDictData(currentDictCode.value, true));
const dictOptions = computed(() => dictStore.getDictOptions(currentDictCode.value));
const dictMap = computed(() => new Map(dictData.value.map(item => [item.value, item])));
const enabledDictMap = computed(() => new Map(enabledDictData.value.map(item => [item.value, item])));
function getItem(value?: DictValue, options: DictFilterOptions = {}) {
return dictStore.getDictItem(currentDictCode.value, value, options);
}
function getLabel(value?: DictValue, options?: DictLabelOptions) {
const normalizedOptions = normalizeLabelOptions(options);
return dictStore.getDictLabel(currentDictCode.value, value, normalizedOptions);
}
function getLabels(values?: DictValueList, options?: DictLabelsOptions) {
const normalizedOptions = normalizeLabelsOptions(options);
return dictStore.getDictLabels(currentDictCode.value, values, normalizedOptions);
}
function hasValue(value?: DictValue, options: DictFilterOptions = {}) {
return dictStore.hasDictValue(currentDictCode.value, value, options);
}
return {
dictData,
enabledDictData,
dictOptions,
dictMap,
enabledDictMap,
getItem,
getLabel,
getLabels,
hasValue
};
}

View File

@@ -1,7 +1,7 @@
import { useRouter } from 'vue-router';
import type { RouteLocationRaw } from 'vue-router';
import type { RouteKey } from '@elegant-router/types';
import { router as globalRouter } from '@/router';
import { getGlobalRouter } from '@/router/instance';
/**
* Router push
@@ -11,6 +11,7 @@ import { router as globalRouter } from '@/router';
* @param inSetup Whether is in vue script setup
*/
export function useRouterPush(inSetup = true) {
const globalRouter = getGlobalRouter();
const router = inSetup ? useRouter() : globalRouter;
const route = globalRouter.currentRoute;