feat(web): A4 数据字典与系统配置页

背景:用户选定方案 A 主从分栏,A4 需要落地字典与配置页。\n改动:新增 /system/dicts、/system/dicts/:type、/system/settings;主从类型列表 + 三层合并 + 树/词表切换 + 条目/类型弹层;6 组配置 + 命名预览 + 阈值二次确认 + 变更记录;客户字段上限改读 effective config;路由/样式/OpenAPI 类型同步。\n影响面:管理中心新增两个菜单页;客户页上限显示改为配置读取(fallback 共享常量)。\n回滚:回滚本提交并恢复 AppLayout / routes / main 样式 import。\n验证:web typecheck/lint/build;单测 154;浏览器自检 1440/1024 无溢出。
This commit is contained in:
Stev_Wang committed 2026-11-04 11:49:32 +08:00
1 parent e4330a43e4
commit 3b0a99d91f
36 files changed
+7691 -15

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
import type { SystemConfig, SystemConfigChange } from '@lingxi/shared';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useAuthStore } from '@/stores/auth-store';
import { setUnauthorizedHandler } from './client';
import {
fetchEffectiveSystemConfigs,
fetchSystemConfigHistory,
fetchSystemConfigs,
updateSystemConfigs,
} from './configs';
const NOW = '2026-11-04T10:00:00.000Z';
const ID = '01HZZZZZZZZZZZZZZZZZZZZZZ1';
const jsonResponse = (body: unknown, status = 200): Response =>
new Response(status === 204 ? null : JSON.stringify(body), {
status,
headers: status === 204 ? undefined : { 'Content-Type': 'application/json' },
});
const config: SystemConfig = {
key: 'limit.customer.base_field',
group: 'limits',
label: '客户基础字段上限',
description: '每客户基础字段的最大数量',
impact: '前后端共用;修改后新申请按新上限校验',
type: 'number',
value: 10,
valueType: 'number',
unit: '个',
version: 3,
updatedAt: NOW,
updatedBy: '江岚',
editable: true,
isPublic: true,
min: 1,
max: 50,
step: null,
maxLength: null,
};
const change: SystemConfigChange = {
key: 'limit.customer.base_field',
label: '客户基础字段上限',
version: 3,
oldValue: 8,
newValue: 10,
reason: '在系统配置页调整',
operator: '江岚',
changedAt: NOW,
result: '已生效',
};
function lastCall(): { url: string; init: RequestInit | undefined } {
const fetchMock = vi.mocked(globalThis.fetch);
const call = fetchMock.mock.calls.at(-1);
const input = call?.[0];
const url =
typeof input === 'string' ? input : input instanceof URL ? input.href : (input?.url ?? '');
return { url, init: call?.[1] };
}
function lastBody(): unknown {
const body = lastCall().init?.body;
return typeof body === 'string' ? (JSON.parse(body) as unknown) : undefined;
}
beforeEach(() => {
vi.restoreAllMocks();
window.localStorage.clear();
useAuthStore.getState().clearSession();
setUnauthorizedHandler(null);
useAuthStore.getState().setSession({
accessToken: 'token',
user: { id: ID, name: '江岚', account: 'jiang.lan' },
permissions: ['system:configure'],
});
});
describe('系统配置接口封装', () => {
it('全量配置 / 生效配置', async () => {
vi.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(jsonResponse({ items: [config] }))
.mockResolvedValueOnce(jsonResponse({ items: [{ key: config.key, value: 10 }] }));
const list = await fetchSystemConfigs();
expect(list.items).toHaveLength(1);
expect(lastCall().url).toContain('/system/configs');
const effective = await fetchEffectiveSystemConfigs();
expect(effective.items[0]?.value).toBe(10);
expect(lastCall().url).toContain('/system/configs/effective');
});
it('批量保存:PUT changes 带 expectedVersion(乐观锁)', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(jsonResponse({ items: [config] }));
await updateSystemConfigs({
changes: [{ key: 'limit.customer.base_field', value: 12, expectedVersion: 3 }],
});
expect(lastCall().init?.method).toBe('PUT');
expect(lastBody()).toEqual({
changes: [{ key: 'limit.customer.base_field', value: 12, expectedVersion: 3 }],
});
});
it('变更记录:query 用 snake_case(config_key / limit)', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
jsonResponse({ items: [change], next_cursor: null }),
);
const history = await fetchSystemConfigHistory({
config_key: 'limit.customer.base_field',
limit: 50,
});
expect(history.items[0]?.version).toBe(3);
expect(lastCall().url).toContain('/system/configs/history?');
expect(lastCall().url).toContain('config_key=');
expect(lastCall().url).toContain('limit=50');
});
});
+68
View File
@@ -0,0 +1,68 @@
/**
* A4 系统配置接口(唯一出处)。
* 依据:packages/shared/src/schemas/config.ts、PRD R-26、技术架构 §5.7。
*
* `system_configs` 是阈值 / 上限唯一来源:页面保存走 PUT changes + expectedVersion 乐观锁;
* 前端上限读取走 /system/configs/effective(仅登录,只读)。响应 / 入参全部 shared Zod parse。
*/
import {
type EffectiveSystemConfig,
effectiveSystemConfigsResponseSchema,
type SystemConfig,
type SystemConfigChange,
type SystemConfigHistoryQuery,
systemConfigHistoryQuerySchema,
systemConfigHistoryResponseSchema,
systemConfigListResponseSchema,
type UpdateSystemConfigsInput,
updateSystemConfigsInputSchema,
} from '@lingxi/shared';
import { request } from './client';
import { ENDPOINTS } from './endpoints';
function searchParams(entries: Record<string, string | number | undefined>): string {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(entries)) {
if (value !== undefined && value !== '') params.set(key, String(value));
}
const search = params.toString();
return search === '' ? '' : `?${search}`;
}
/** 全量配置(含非公开项,需 system:configure) */
export async function fetchSystemConfigs(): Promise<{ items: SystemConfig[] }> {
const payload: unknown = await request(ENDPOINTS.systemConfigs);
return systemConfigListResponseSchema.parse(payload);
}
/** 生效配置(仅登录;前端上限读取,无需 system:configure) */
export async function fetchEffectiveSystemConfigs(): Promise<{
items: EffectiveSystemConfig[];
}> {
const payload: unknown = await request(ENDPOINTS.systemConfigsEffective);
return effectiveSystemConfigsResponseSchema.parse(payload);
}
/** 批量保存:changes 带 expectedVersion(不一致返回 40901) */
export async function updateSystemConfigs(
input: UpdateSystemConfigsInput,
): Promise<{ items: SystemConfig[] }> {
const body = updateSystemConfigsInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.systemConfigs, { method: 'PUT', body });
return systemConfigListResponseSchema.parse(payload);
}
/** 变更记录(含前后值、操作人、时间、原因与结果) */
export async function fetchSystemConfigHistory(
query: Partial<SystemConfigHistoryQuery> = {},
): Promise<{ items: SystemConfigChange[]; next_cursor: string | null }> {
const parsed = systemConfigHistoryQuerySchema.parse(query);
const payload: unknown = await request(
`${ENDPOINTS.systemConfigsHistory}${searchParams({
config_key: parsed.config_key,
limit: parsed.limit,
})}`,
);
return systemConfigHistoryResponseSchema.parse(payload);
}
+209
View File
@@ -0,0 +1,209 @@
import type {
DictItem,
DictItemListResponse,
DictTypeDetail,
DictTypeSummary,
} from '@lingxi/shared';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useAuthStore } from '@/stores/auth-store';
import { setUnauthorizedHandler } from './client';
import {
createDictItem,
createDictType,
deleteDictType,
fetchDictItemList,
fetchDictTypeDetail,
fetchDictTypeList,
setDictTypeStatus,
updateDictItem,
updateDictType,
} from './dicts';
const NOW = '2026-11-04T10:00:00.000Z';
const ID = '01HZZZZZZZZZZZZZZZZZZZZZZ1';
const jsonResponse = (body: unknown, status = 200): Response =>
new Response(status === 204 ? null : JSON.stringify(body), {
status,
headers: status === 204 ? undefined : { 'Content-Type': 'application/json' },
});
const typeSummary: DictTypeSummary = {
code: 'script_direction',
name: '脚本方向',
group: 'content',
kind: 'list',
description: '脚本“说什么”的方向',
industryScoped: true,
builtin: true,
status: 'active',
sortOrder: 60,
itemCount: 11,
activeItemCount: 11,
industryCount: 4,
updatedAt: NOW,
updatedBy: '江岚',
};
const item: DictItem = {
id: ID,
typeCode: 'script_direction',
code: 'oral',
name: '口播',
parentCode: null,
parentName: null,
industryId: null,
industryPath: null,
sortOrder: 10,
status: 'active',
remark: '',
source: 'generic',
overridden: false,
refCount: 15,
updatedAt: NOW,
updatedBy: '江岚',
};
const typeDetail: DictTypeDetail = { ...typeSummary, items: [item] };
const itemList: DictItemListResponse = {
items: [item],
next_cursor: null,
layers: [
{ source: 'generic', industryId: null, label: '通用', count: 5 },
{ source: 'parent', industryId: ID, label: '父行业', count: 2 },
{ source: 'current', industryId: ID, label: '本行业', count: 1 },
],
effectiveCount: 7,
overriddenCount: 1,
disabledCount: 0,
};
function lastCall(): { url: string; init: RequestInit | undefined } {
const fetchMock = vi.mocked(globalThis.fetch);
const call = fetchMock.mock.calls.at(-1);
const input = call?.[0];
const url =
typeof input === 'string' ? input : input instanceof URL ? input.href : (input?.url ?? '');
return { url, init: call?.[1] };
}
function lastBody(): unknown {
const body = lastCall().init?.body;
return typeof body === 'string' ? (JSON.parse(body) as unknown) : undefined;
}
beforeEach(() => {
vi.restoreAllMocks();
window.localStorage.clear();
useAuthStore.getState().clearSession();
setUnauthorizedHandler(null);
useAuthStore.getState().setSession({
accessToken: 'token',
user: { id: ID, name: '江岚', account: 'jiang.lan' },
permissions: ['dict:manage'],
});
});
describe('数据字典接口封装', () => {
it('类型列表:query 用 snake_case(keyword / group / status / limit / cursor)', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
jsonResponse({ items: [typeSummary], next_cursor: 'cursor-2' }),
);
const result = await fetchDictTypeList({
keyword: '脚本',
group: 'content',
status: 'active',
limit: 100,
cursor: 'cursor-1',
});
expect(result.next_cursor).toBe('cursor-2');
expect(lastCall().url).toContain('/dicts?keyword=');
expect(lastCall().url).toContain('group=content');
expect(lastCall().url).toContain('status=active');
expect(lastCall().url).toContain('limit=100');
expect(lastCall().url).toContain('cursor=cursor-1');
});
it('类型详情 / 新建 / 更新 / 状态 / 删除', async () => {
vi.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(jsonResponse(typeDetail))
.mockResolvedValueOnce(jsonResponse(typeDetail, 201))
.mockResolvedValueOnce(jsonResponse(typeDetail))
.mockResolvedValueOnce(jsonResponse(typeDetail))
.mockResolvedValueOnce(jsonResponse(undefined, 204));
await fetchDictTypeDetail('script_direction');
expect(lastCall().url).toContain('/dicts/script_direction');
await createDictType({
code: 'delivery_slot',
name: '投放位置',
group: 'platform',
kind: 'list',
industryScoped: false,
description: '',
sortOrder: 10,
});
expect(lastCall().init?.method).toBe('POST');
expect(lastBody()).toMatchObject({ code: 'delivery_slot', industryScoped: false });
await updateDictType('script_direction', { name: '脚本方向2' });
expect(lastCall().init?.method).toBe('PATCH');
expect(lastBody()).toEqual({ name: '脚本方向2' });
await setDictTypeStatus('script_direction', { status: 'disabled' });
expect(lastCall().url).toContain('/dicts/script_direction/status');
expect(lastBody()).toEqual({ status: 'disabled' });
await deleteDictType('script_direction');
expect(lastCall().init?.method).toBe('DELETE');
});
it('条目列表:industry_id 只在传入行业视角时出现', async () => {
vi.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(jsonResponse(itemList))
.mockResolvedValueOnce(jsonResponse(itemList));
await fetchDictItemList('script_direction', { industry_id: ID });
expect(lastCall().url).toContain('/dicts/script_direction/items?industry_id=');
await fetchDictItemList('script_direction');
expect(lastCall().url).not.toContain('industry_id');
});
it('新建 / 编辑条目:body 用 camelCase,编辑不提交编码与行业', async () => {
vi.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(jsonResponse(item, 201))
.mockResolvedValueOnce(jsonResponse(item));
await createDictItem('script_direction', {
code: 'oral',
name: '口播',
parentCode: null,
industryId: null,
sortOrder: 10,
status: 'active',
remark: '',
});
expect(lastCall().init?.method).toBe('POST');
expect(lastBody()).toMatchObject({ code: 'oral', parentCode: null, industryId: null });
await updateDictItem('script_direction', ID, {
name: '口播(新)',
sortOrder: 20,
status: 'disabled',
remark: '说明',
});
expect(lastCall().url).toContain(`/dicts/script_direction/items/${ID}`);
expect(lastCall().init?.method).toBe('PATCH');
expect(lastBody()).toEqual({
name: '口播(新)',
sortOrder: 20,
status: 'disabled',
remark: '说明',
});
});
});
+133
View File
@@ -0,0 +1,133 @@
/**
* A4 数据字典接口(唯一出处)。
* 依据:packages/shared/src/schemas/dict.ts、PRD R-10、技术架构 §6.3.7。
*
* 约定:query 用 snake_case(行业视角 `industry_id`);写接口 body 用 camelCase(C-01)。
* 所有响应 / 入参都用 shared Zod schema parse,契约漂移在请求层直接暴露。
*/
import {
type CreateDictItemInput,
createDictItemInputSchema,
type CreateDictTypeInput,
createDictTypeInputSchema,
type DictItem,
type DictItemListQuery,
dictItemListQuerySchema,
type DictItemListResponse,
dictItemListResponseSchema,
dictItemSchema,
type DictTypeDetail,
dictTypeDetailSchema,
type DictTypeListQuery,
dictTypeListQuerySchema,
type DictTypeSummary,
dictTypeSummarySchema,
listResponseSchema,
type SetDictTypeStatusInput,
setDictTypeStatusInputSchema,
type UpdateDictItemInput,
updateDictItemInputSchema,
type UpdateDictTypeInput,
updateDictTypeInputSchema,
} from '@lingxi/shared';
import { request } from './client';
import { ENDPOINTS } from './endpoints';
const dictTypeListResponseSchema = listResponseSchema(dictTypeSummarySchema);
function searchParams(entries: Record<string, string | number | undefined>): string {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(entries)) {
if (value !== undefined && value !== '') params.set(key, String(value));
}
const search = params.toString();
return search === '' ? '' : `?${search}`;
}
/** 字典类型列表:keyword / group / status / limit / cursor(query snake_case) */
export async function fetchDictTypeList(
query: Partial<DictTypeListQuery> = {},
): Promise<{ items: DictTypeSummary[]; next_cursor: string | null }> {
const parsed = dictTypeListQuerySchema.parse(query);
const payload: unknown = await request(
`${ENDPOINTS.dicts}${searchParams({
keyword: parsed.keyword,
group: parsed.group,
status: parsed.status,
limit: parsed.limit,
cursor: parsed.cursor,
})}`,
);
return dictTypeListResponseSchema.parse(payload);
}
export async function fetchDictTypeDetail(typeCode: string): Promise<DictTypeDetail> {
const payload: unknown = await request(ENDPOINTS.dict(typeCode));
return dictTypeDetailSchema.parse(payload);
}
export async function createDictType(input: CreateDictTypeInput): Promise<DictTypeDetail> {
const body = createDictTypeInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.dicts, { method: 'POST', body });
return dictTypeDetailSchema.parse(payload);
}
export async function updateDictType(
typeCode: string,
input: UpdateDictTypeInput,
): Promise<DictTypeDetail> {
const body = updateDictTypeInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.dict(typeCode), { method: 'PATCH', body });
return dictTypeDetailSchema.parse(payload);
}
export async function setDictTypeStatus(
typeCode: string,
input: SetDictTypeStatusInput,
): Promise<DictTypeDetail> {
const body = setDictTypeStatusInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.dictStatus(typeCode), {
method: 'PATCH',
body,
});
return dictTypeDetailSchema.parse(payload);
}
export async function deleteDictType(typeCode: string): Promise<void> {
await request<unknown>(ENDPOINTS.dict(typeCode), { method: 'DELETE' });
}
/** 生效条目列表(三层合并结果):不传 industry_id 表示仅通用层 */
export async function fetchDictItemList(
typeCode: string,
query: Partial<DictItemListQuery> = {},
): Promise<DictItemListResponse> {
const parsed = dictItemListQuerySchema.parse(query);
const payload: unknown = await request(
`${ENDPOINTS.dictItems(typeCode)}${searchParams({ industry_id: parsed.industry_id })}`,
);
return dictItemListResponseSchema.parse(payload);
}
export async function createDictItem(
typeCode: string,
input: CreateDictItemInput,
): Promise<DictItem> {
const body = createDictItemInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.dictItems(typeCode), { method: 'POST', body });
return dictItemSchema.parse(payload);
}
export async function updateDictItem(
typeCode: string,
itemId: string,
input: UpdateDictItemInput,
): Promise<DictItem> {
const body = updateDictItemInputSchema.parse(input);
const payload: unknown = await request(ENDPOINTS.dictItem(typeCode, itemId), {
method: 'PATCH',
body,
});
return dictItemSchema.parse(payload);
}
+12
View File
@@ -4,6 +4,8 @@
* 后端若调整路径,只改这里;联调时以 `apps/web/src/api/generated/openapi.d.ts` 为准。
*/
const USERS = '/users';
const DICTS = '/dicts';
const SYSTEM_CONFIGS = '/system/configs';
export const ENDPOINTS = {
login: '/auth/login',
@@ -52,4 +54,14 @@ export const ENDPOINTS = {
`/customers/${encodeURIComponent(customerId)}/fields`,
customerFieldApprove: (fieldId: string): string =>
`/customer-fields/${encodeURIComponent(fieldId)}/approve`,
// A4 dict / system config
dicts: DICTS,
dict: (typeCode: string): string => `${DICTS}/${encodeURIComponent(typeCode)}`,
dictStatus: (typeCode: string): string => `${DICTS}/${encodeURIComponent(typeCode)}/status`,
dictItems: (typeCode: string): string => `${DICTS}/${encodeURIComponent(typeCode)}/items`,
dictItem: (typeCode: string, itemId: string): string =>
`${DICTS}/${encodeURIComponent(typeCode)}/items/${encodeURIComponent(itemId)}`,
systemConfigs: SYSTEM_CONFIGS,
systemConfigsEffective: `${SYSTEM_CONFIGS}/effective`,
systemConfigsHistory: `${SYSTEM_CONFIGS}/history`,
} as const;
+362
View File
@@ -627,6 +627,147 @@ export interface paths {
patch?: never;
trace?: never;
};
'/api/v1/dicts': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 字典类型列表(分组顺序 + 排序;含条目 / 行业层统计) */
get: operations['DictsController_list'];
put?: never;
/** 新建字典类型(编码唯一;builtin=false) */
post: operations['DictsController_create'];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/api/v1/dicts/{code}': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 字典类型详情(不存在 40401) */
get: operations['DictsController_detail'];
put?: never;
post?: never;
/** 删除自定义字典类型(内置 / 有子条目时不可删) */
delete: operations['DictsController_remove'];
options?: never;
head?: never;
/** 更新字典类型(编码 / 形态 / 行业作用域不可改) */
patch: operations['DictsController_update'];
trace?: never;
};
'/api/v1/dicts/{code}/status': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
/** 启用 / 停用字典类型(内置类型允许停用) */
patch: operations['DictsController_setStatus'];
trace?: never;
};
'/api/v1/dicts/{code}/items': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 生效条目列表(industry_id 三层合并;非行业类型忽略该参数) */
get: operations['DictsController_listItems'];
put?: never;
/** 新建字典条目(编码 / 父级 / 行业校验;编码创建后不可改) */
post: operations['DictsController_createItem'];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/api/v1/dicts/{code}/items/{itemId}': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
/** 更新字典条目(仅名称 / 排序 / 启停 / 备注) */
patch: operations['DictsController_updateItem'];
trace?: never;
};
'/api/v1/system/configs': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 全量系统配置(定义元数据 + 当前值 / 版本) */
get: operations['SystemConfigController_list'];
/** 批量保存配置(乐观锁 expectedVersion;写版本历史与审计) */
put: operations['SystemConfigController_update'];
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/api/v1/system/configs/effective': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 生效配置(仅登录;只返回 public 项,前端上限读取入口) */
get: operations['SystemConfigController_effective'];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/api/v1/system/configs/history': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** 配置变更记录(按变更时间倒序;操作人为用户姓名) */
get: operations['SystemConfigController_history'];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
'/health': {
parameters: {
query?: never;
@@ -1559,6 +1700,227 @@ export interface operations {
};
};
};
DictsController_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_create: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
201: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_detail: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_remove: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_update: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_setStatus: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_listItems: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_createItem: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
201: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
DictsController_updateItem: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
SystemConfigController_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
SystemConfigController_update: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
SystemConfigController_effective: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
SystemConfigController_history: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
HealthController_check: {
parameters: {
query?: never;
+9
View File
@@ -26,4 +26,13 @@ export const queryKeys = {
customerGroup: (groupId: string) => ['customer-groups', 'detail', groupId] as const,
customerFields: (query: object) => ['customer-fields', 'list', query] as const,
customerFieldsRoot: ['customer-fields'] as const,
// A4 dict / system config
dicts: ['dicts'] as const,
dictTypeList: (query: object) => ['dicts', 'list', query] as const,
dictType: (typeCode: string) => ['dicts', 'detail', typeCode] as const,
dictItems: (typeCode: string, industryId: string | null) =>
['dicts', 'items', typeCode, industryId ?? 'all'] as const,
systemConfigs: ['system-configs'] as const,
systemConfigsEffective: ['system-configs', 'effective'] as const,
systemConfigHistory: (query: object) => ['system-configs', 'history', query] as const,
};