落地报价模板 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
@@ -16,22 +16,75 @@ enum Api {
disable = '/inquiry/quoteTemplate/disable',
setDefault = '/inquiry/quoteTemplate/setDefault',
editMeta = '/inquiry/quoteTemplate/editMeta',
delete = '/inquiry/quoteTemplate/delete',
previewSample = '/inquiry/quoteTemplate/previewSample',
previewSheet = '/inquiry/quoteTemplate/previewSheet',
previewSnapshot = '/inquiry/quoteTemplate/previewSnapshot',
download = '/inquiry/quoteTemplate/download',
reparse = '/inquiry/quoteTemplate/reparse',
}
export const getTemplateList = (params) => defHttp.get({ url: Api.list, params });
/**
* 上传模板文件(新建或追加版本)。
* 禁止手写 Content-Type: multipart/form-data:缺 boundary 会被 Tomcat 直接拒收。
* FormData 交给浏览器自动带 boundary;解析可能超过默认 10 秒,单独放宽超时。
*/
export const uploadTemplate = (data: FormData) =>
defHttp.post({
url: Api.upload,
data,
headers: { 'Content-Type': 'multipart/form-data' },
});
defHttp.post(
{
url: Api.upload,
data,
timeout: 120 * 1000,
},
{ errorMessageMode: 'message' },
);
export const getTemplateVersion = (versionId: string) =>
defHttp.get({ url: Api.version, params: { versionId } });
/** 上传原表 Excel 样式快照(格子原文 + 黄区/映射;旧接口保留) */
export const getTemplateSheetPreview = (versionId: string) =>
defHttp.get({ url: Api.previewSheet, params: { versionId }, timeout: 60 * 1000 });
/**
* 原表截图预览(主账本机 LibreOffice 转 PNG/PDF)。
* 走 blob:带鉴权;首次转换可能较慢。
*/
export async function fetchTemplateSnapshotBlob(versionId: string): Promise<{ blob: Blob; contentType: string }> {
const glob = useGlobSetting();
const base = (glob.apiUrl || '').replace(/\/$/, '');
const url = `${base}${Api.previewSnapshot}?versionId=${encodeURIComponent(versionId)}`;
const token = getToken() || '';
const controller = new AbortController();
const timer = window.setTimeout(() => controller.abort(), 150 * 1000);
try {
const res = await fetch(url, {
method: 'GET',
headers: {
'X-Access-Token': String(token),
},
signal: controller.signal,
});
const ctype = (res.headers.get('content-type') || '').toLowerCase();
if (!res.ok || ctype.includes('application/json')) {
let msg = `预览失败 HTTP ${res.status}`;
try {
const j = await res.json();
if (j?.message) msg = String(j.message);
} catch {
/* ignore */
}
throw new Error(msg);
}
const blob = await res.blob();
return { blob, contentType: ctype || blob.type || 'application/octet-stream' };
} finally {
window.clearTimeout(timer);
}
}
export const listTemplateVersions = (templateId: string) =>
defHttp.get({ url: Api.versions, params: { templateId } });
@@ -44,10 +97,31 @@ export const enableTemplate = (templateId: string, versionId: string) =>
export const disableTemplate = (templateId: string) =>
defHttp.post({ url: Api.disable, params: { templateId } });
/**
* 删除模板逻辑头及全部版本;不可恢复。
* 走 POST(与 Jeecg 惯例一致):部分网关/Nginx 对 DELETE 会直接 404。
* joinParamsToUrl:非 GET 时 axios 会把 params 挪进 JSON 体,必须拼到查询串给 @RequestParam。
*/
export const deleteTemplate = (templateId: string) =>
defHttp.post(
{ url: Api.delete, params: { templateId } },
{ joinParamsToUrl: true },
);
export const setTemplateDefault = (templateId: string, isDefault = true) =>
defHttp.post({ url: Api.setDefault, params: { templateId, isDefault } });
export const editTemplateMeta = (params) => defHttp.put({ url: Api.editMeta, params });
/** 只改名称/业务/关键词/默认标记,不换文件;走 JSON 体对接主账 @RequestBody */
export const editTemplateMeta = (data) => defHttp.put({ url: Api.editMeta, data });
/**
* 对最新 Excel 版重新提交 AI 异步解析(已解析也可点;解析中跳过)。
*/
export const reparseTemplate = (templateId: string) =>
defHttp.post(
{ url: Api.reparse, params: { id: templateId }, timeout: 60 * 1000 },
{ joinParamsToUrl: true },
);
/**
* 带鉴权下载样例预览 / 原文件。