按用户选定实现操作日志列表 / 详情(方案 A)与运行日志(方案 C);多条件筛选、真实游标分页、字段级 diff / 完整原始 JSON、560 导出抽屉(轮询 / 历史 / 签名下载)、只读与权限控制;侧栏「审计日志」更名「操作日志」、「客户字段」更名「字段管理」并新增「运行日志」;1024 关键列适配。
126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
/**
|
|
* 前端路由常量(工程规范 04 / 11:路由集中管理,禁止散落字符串)
|
|
*
|
|
* 约定:`/<模块>/<功能>`;模块 key、基础路径与页面目录一一对应。
|
|
* 例如:管理中心(system)→ /system/users → pages/system/users。
|
|
*/
|
|
export const ROUTES = {
|
|
home: '/',
|
|
login: '/login',
|
|
system: {
|
|
root: '/system',
|
|
users: '/system/users',
|
|
userNew: '/system/users/new',
|
|
userDetail: '/system/users/:id',
|
|
departments: '/system/departments',
|
|
roles: '/system/roles',
|
|
roleNew: '/system/roles/new',
|
|
roleDetail: '/system/roles/:id',
|
|
industries: '/system/industries',
|
|
customers: '/system/customers',
|
|
customerNew: '/system/customers/new',
|
|
customerDetail: '/system/customers/:id',
|
|
customerGroups: '/system/customer-groups',
|
|
customerFields: '/system/customer-fields/approvals',
|
|
dicts: '/system/dicts',
|
|
dictDetail: '/system/dicts/:type',
|
|
auditLogs: '/system/audit-logs',
|
|
auditLogDetail: '/system/audit-logs/:id',
|
|
runLogs: '/system/logs',
|
|
settings: '/system/settings',
|
|
},
|
|
unauthorized: '/401',
|
|
forbidden: '/403',
|
|
notFound: '/404',
|
|
serverError: '/500',
|
|
} as const;
|
|
|
|
export type RoutePath =
|
|
| typeof ROUTES.home
|
|
| typeof ROUTES.login
|
|
| typeof ROUTES.system.root
|
|
| typeof ROUTES.system.users
|
|
| typeof ROUTES.system.userNew
|
|
| typeof ROUTES.system.userDetail
|
|
| typeof ROUTES.system.departments
|
|
| typeof ROUTES.system.roles
|
|
| typeof ROUTES.system.roleNew
|
|
| typeof ROUTES.system.roleDetail
|
|
| typeof ROUTES.system.industries
|
|
| typeof ROUTES.system.customers
|
|
| typeof ROUTES.system.customerNew
|
|
| typeof ROUTES.system.customerDetail
|
|
| typeof ROUTES.system.customerGroups
|
|
| typeof ROUTES.system.customerFields
|
|
| typeof ROUTES.system.dicts
|
|
| typeof ROUTES.system.dictDetail
|
|
| typeof ROUTES.system.auditLogs
|
|
| typeof ROUTES.system.auditLogDetail
|
|
| typeof ROUTES.system.runLogs
|
|
| typeof ROUTES.system.settings
|
|
| typeof ROUTES.unauthorized
|
|
| typeof ROUTES.forbidden
|
|
| typeof ROUTES.notFound
|
|
| typeof ROUTES.serverError;
|
|
|
|
/** 用户详情深链:`/system/users/{id}` */
|
|
export function userDetailPath(userId: string): string {
|
|
return `${ROUTES.system.users}/${encodeURIComponent(userId)}`;
|
|
}
|
|
|
|
/** 角色详情 / 编辑器深链:`/system/roles/{id}` */
|
|
export function roleDetailPath(roleId: string): string {
|
|
return `${ROUTES.system.roles}/${encodeURIComponent(roleId)}`;
|
|
}
|
|
|
|
/** 客户详情深链:`/system/customers/{id}` */
|
|
export function customerDetailPath(customerId: string): string {
|
|
return `${ROUTES.system.customers}/${encodeURIComponent(customerId)}`;
|
|
}
|
|
|
|
/** 字典类型详情深链:`/system/dicts/{typeCode}` */
|
|
export function dictDetailPath(typeCode: string): string {
|
|
return `${ROUTES.system.dicts}/${encodeURIComponent(typeCode)}`;
|
|
}
|
|
|
|
/** 操作日志详情深链:`/system/audit-logs/{id}` */
|
|
export function auditLogDetailPath(logId: string): string {
|
|
return `${ROUTES.system.auditLogs}/${encodeURIComponent(logId)}`;
|
|
}
|
|
|
|
/** 新建客户(四步向导) */
|
|
export function customerNewPath(): string {
|
|
return ROUTES.system.customerNew;
|
|
}
|
|
|
|
/** 新建角色深链;带来源角色时作为复制模板预填(特殊权限不随复制带入) */
|
|
export function roleNewPath(sourceRoleId?: string): string {
|
|
return sourceRoleId === undefined
|
|
? ROUTES.system.roleNew
|
|
: `${ROUTES.system.roleNew}?from=${encodeURIComponent(sourceRoleId)}`;
|
|
}
|
|
|
|
/**
|
|
* 校验回跳地址:只允许站内绝对路径(以单个 `/` 开头),
|
|
* 拒绝协议相对地址(`//evil.com`)与外部 URL,避免开放重定向。
|
|
*/
|
|
export function sanitizeRedirect(value: string | null | undefined): string | null {
|
|
if (value === null || value === undefined) {
|
|
return null;
|
|
}
|
|
const trimmed = value.trim();
|
|
if (!trimmed.startsWith('/') || trimmed.startsWith('//')) {
|
|
return null;
|
|
}
|
|
if (trimmed.startsWith(ROUTES.login)) {
|
|
return null;
|
|
}
|
|
return trimmed;
|
|
}
|
|
|
|
/** 登录页地址;带 redirect 时回跳原路径(仅接受站内相对路径) */
|
|
export function loginPath(redirect?: string): string {
|
|
const target = sanitizeRedirect(redirect);
|
|
return target === null ? ROUTES.login : `${ROUTES.login}?redirect=${encodeURIComponent(target)}`;
|
|
}
|