落地报价模板 AI 解析与对象存储,并增加回复超时系统设置页。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-18 17:42:46 +08:00
co-authored by Cursor
parent 982b3a2317
commit 527050bbf9
41 changed files with 4965 additions and 426 deletions
@@ -24,6 +24,121 @@ import { getPermCode } from '/@/api/sys/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { PageEnum } from '/@/enums/pageEnum';
/**
* 询价原型侧栏路径是写死的;Jeecg BACK 模式只注册后台菜单里的路由。
* 菜单种子缺失/组件路径不对时,点侧栏会进 404(本机与线上相同)。
* 这里按侧栏清单补挂手写页,不依赖种子是否已导入。
*/
type InquiryLeaf = {
path: string;
name: string;
component: string;
title: string;
};
const INQUIRY_LEAVES: InquiryLeaf[] = [
{ path: '/inquiry/dashboard', name: 'inquiry-dashboard', component: 'inquiry/dashboard/index', title: '分析看板' },
{ path: '/inquiry/work-order', name: 'inquiry-work-order', component: 'inquiry/workOrder/index', title: '询价工单' },
{ path: '/inquiry/staff-role', name: 'inquiry-staff-role', component: 'inquiry/staffRole/index', title: '员工管理' },
{ path: '/inquiry/template', name: 'inquiry-template', component: 'inquiry/template/index', title: '报价模板' },
{ path: '/inquiry/bot-reply', name: 'inquiry-bot-reply', component: 'inquiry/botReply/index', title: '机器人回复语' },
{
path: '/inquiry-sys/system-setting',
name: 'inquiry-system-setting',
component: 'inquiry/systemSetting/index',
title: '系统设置',
},
{ path: '/inquiry-sys/user', name: 'inquiry-sys-user', component: 'inquiry/account/index', title: '账号管理' },
{ path: '/inquiry-sys/role', name: 'inquiry-sys-role', component: 'inquiry/role/index', title: '角色管理' },
];
function findRouteByPath(list: AppRouteRecordRaw[] = [], targetPath: string): AppRouteRecordRaw | null {
for (const item of list) {
const p = item.path || '';
if (p === targetPath || p === targetPath.replace(/^\//, '') || p.endsWith(targetPath)) {
return item;
}
const nested = findRouteByPath(item.children as AppRouteRecordRaw[] | undefined, targetPath);
if (nested) {
return nested;
}
}
return null;
}
function findParent(list: AppRouteRecordRaw[] = [], parentPath: string): AppRouteRecordRaw | null {
for (const item of list) {
const p = item.path || '';
if (p === parentPath || p.endsWith(parentPath)) {
return item;
}
const nested = findParent(item.children as AppRouteRecordRaw[] | undefined, parentPath);
if (nested) {
return nested;
}
}
return null;
}
function ensureInquiryHandwrittenRoutes(routeList: AppRouteRecordRaw[]) {
const groups: Record<string, { title: string; redirect: string; leaves: InquiryLeaf[] }> = {
'/inquiry': {
title: '询价',
redirect: '/inquiry/dashboard',
leaves: INQUIRY_LEAVES.filter((l) => l.path.startsWith('/inquiry/') && !l.path.startsWith('/inquiry-sys')),
},
'/inquiry-sys': {
title: '询价系统配置',
redirect: '/inquiry-sys/system-setting',
leaves: INQUIRY_LEAVES.filter((l) => l.path.startsWith('/inquiry-sys/')),
},
};
for (const [parentPath, group] of Object.entries(groups)) {
for (const leaf of group.leaves) {
const existing = findRouteByPath(routeList, leaf.path);
if (existing) {
// 菜单有路径但组件指错(常见:空、旧 dashboard/Analysis)→ 纠正为手写页
const comp = String((existing as any).component || '');
if (!comp.includes(leaf.component) && !comp.includes(leaf.component.replace(/\/index$/, ''))) {
(existing as any).component = leaf.component;
existing.meta = { ...(existing.meta || {}), title: leaf.title, ignoreKeepAlive: true };
}
continue;
}
const node: AppRouteRecordRaw = {
path: leaf.path,
name: leaf.name,
component: leaf.component as any,
meta: {
title: leaf.title,
ignoreKeepAlive: true,
},
};
let parent = findParent(routeList, parentPath);
if (!parent) {
parent = {
path: parentPath,
name: parentPath.replace(/\//g, '-').replace(/^-/, '') || 'inquiry-root',
component: 'LAYOUT' as any,
redirect: group.redirect,
meta: { title: group.title },
children: [],
};
routeList.push(parent);
}
parent.children = parent.children || [];
if (leaf.path === group.redirect) {
parent.children.unshift(node);
} else {
parent.children.push(node);
}
}
}
return routeList;
}
// 系统权限
interface AuthItem {
// 菜单权限编码,例如:“sys:schedule:list,sys:schedule:info”,多个逗号隔开
@@ -219,6 +334,7 @@ export const usePermissionStore = defineStore({
try {
this.changePermissionCode();
routeList = (await getMenuList()) as AppRouteRecordRaw[];
routeList = ensureInquiryHandwrittenRoutes(routeList);
// update-begin----author:sunjianlei---date:20220315------for: 判断是否是 vue3 版本的菜单 ---
let hasIndex: boolean = false;
let hasIcon: boolean = false;