报价模板下线分类字段,匹配只靠业务类型与关键词;并补后台站点标题配置。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -34,7 +34,6 @@ class TemplateEntry:
|
|||||||
version_id: str = ""
|
version_id: str = ""
|
||||||
mapping_json: str = ""
|
mapping_json: str = ""
|
||||||
biz_type: str = ""
|
biz_type: str = ""
|
||||||
category: str = ""
|
|
||||||
keywords: tuple[str, ...] = ()
|
keywords: tuple[str, ...] = ()
|
||||||
is_default: bool = False
|
is_default: bool = False
|
||||||
|
|
||||||
@@ -94,7 +93,6 @@ def entries_from_ledger_response(resp: LedgerResponse) -> list[TemplateEntry]:
|
|||||||
version_id=str(it.get("versionId") or ""),
|
version_id=str(it.get("versionId") or ""),
|
||||||
mapping_json=str(it.get("mappingJson") or ""),
|
mapping_json=str(it.get("mappingJson") or ""),
|
||||||
biz_type=str(it.get("bizType") or ""),
|
biz_type=str(it.get("bizType") or ""),
|
||||||
category=str(it.get("category") or ""),
|
|
||||||
keywords=tuple(str(x) for x in (it.get("keywords") or []) if str(x).strip()),
|
keywords=tuple(str(x) for x in (it.get("keywords") or []) if str(x).strip()),
|
||||||
is_default=bool(it.get("isDefault")),
|
is_default=bool(it.get("isDefault")),
|
||||||
)
|
)
|
||||||
@@ -155,7 +153,6 @@ def registry_as_dicts() -> list[dict[str, Any]]:
|
|||||||
"template_id": e.template_id,
|
"template_id": e.template_id,
|
||||||
"version_no": e.version_no,
|
"version_no": e.version_no,
|
||||||
"biz_type": e.biz_type,
|
"biz_type": e.biz_type,
|
||||||
"category": e.category,
|
|
||||||
}
|
}
|
||||||
for e in list_template_registry()
|
for e in list_template_registry()
|
||||||
]
|
]
|
||||||
|
|||||||
+3
-8
@@ -50,11 +50,10 @@ public class InquiryQuoteTemplateController {
|
|||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public Result<IPage<Map<String, Object>>> list(@RequestParam(required = false) String keyword,
|
public Result<IPage<Map<String, Object>>> list(@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String bizType,
|
@RequestParam(required = false) String bizType,
|
||||||
@RequestParam(required = false) String category,
|
|
||||||
@RequestParam(required = false) String status,
|
@RequestParam(required = false) String status,
|
||||||
@RequestParam(defaultValue = "1") Integer pageNo,
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
@RequestParam(defaultValue = "20") Integer pageSize) {
|
@RequestParam(defaultValue = "20") Integer pageSize) {
|
||||||
return Result.OK(quoteTemplateService.pageWithVersion(keyword, bizType, category, status, pageNo, pageSize));
|
return Result.OK(quoteTemplateService.pageWithVersion(keyword, bizType, status, pageNo, pageSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoLog(value = "报价模板-上传版本")
|
@AutoLog(value = "报价模板-上传版本")
|
||||||
@@ -64,13 +63,12 @@ public class InquiryQuoteTemplateController {
|
|||||||
public Result<Map<String, Object>> upload(@RequestParam(required = false) String templateId,
|
public Result<Map<String, Object>> upload(@RequestParam(required = false) String templateId,
|
||||||
@RequestParam(required = false) String name,
|
@RequestParam(required = false) String name,
|
||||||
@RequestParam(required = false) String bizType,
|
@RequestParam(required = false) String bizType,
|
||||||
@RequestParam(required = false) String category,
|
|
||||||
@RequestParam(required = false) String keywordsJson,
|
@RequestParam(required = false) String keywordsJson,
|
||||||
@RequestParam(required = false, defaultValue = "auto") String mode,
|
@RequestParam(required = false, defaultValue = "auto") String mode,
|
||||||
@RequestParam(required = false, defaultValue = "false") Boolean isDefault,
|
@RequestParam(required = false, defaultValue = "false") Boolean isDefault,
|
||||||
@RequestParam("file") MultipartFile file) {
|
@RequestParam("file") MultipartFile file) {
|
||||||
Map<String, Object> row = quoteTemplateService.uploadVersion(
|
Map<String, Object> row = quoteTemplateService.uploadVersion(
|
||||||
templateId, name, bizType, category, keywordsJson, mode,
|
templateId, name, bizType, keywordsJson, mode,
|
||||||
Boolean.TRUE.equals(isDefault), file, currentUser());
|
Boolean.TRUE.equals(isDefault), file, currentUser());
|
||||||
return Result.OK(row);
|
return Result.OK(row);
|
||||||
}
|
}
|
||||||
@@ -136,7 +134,7 @@ public class InquiryQuoteTemplateController {
|
|||||||
return Result.OK(quoteTemplateService.getById(id));
|
return Result.OK(quoteTemplateService.getById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation("编辑逻辑头(名称/分类/关键词/模式,不换文件)")
|
@ApiOperation("编辑逻辑头(名称/业务/关键词/模式,不换文件)")
|
||||||
@RequiresPermissions("inquiry:template:edit")
|
@RequiresPermissions("inquiry:template:edit")
|
||||||
@PutMapping("/editMeta")
|
@PutMapping("/editMeta")
|
||||||
public Result<String> editMeta(@RequestBody InquiryQuoteTemplate body) {
|
public Result<String> editMeta(@RequestBody InquiryQuoteTemplate body) {
|
||||||
@@ -153,9 +151,6 @@ public class InquiryQuoteTemplateController {
|
|||||||
if (StringUtils.isNotBlank(body.getBizType())) {
|
if (StringUtils.isNotBlank(body.getBizType())) {
|
||||||
db.setBizType(body.getBizType());
|
db.setBizType(body.getBizType());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(body.getCategory())) {
|
|
||||||
db.setCategory(body.getCategory());
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(body.getKeywordsJson())) {
|
if (StringUtils.isNotBlank(body.getKeywordsJson())) {
|
||||||
db.setKeywordsJson(body.getKeywordsJson());
|
db.setKeywordsJson(body.getKeywordsJson());
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -17,7 +17,8 @@ import java.util.Date;
|
|||||||
/**
|
/**
|
||||||
* 报价模板逻辑头,对应 inq_quote_template。
|
* 报价模板逻辑头,对应 inq_quote_template。
|
||||||
* <p>
|
* <p>
|
||||||
* 职责:业务身份(海陆空/分类/关键词/默认/启停/auto|manual)。
|
* 职责:业务身份(海陆空/关键词/默认/启停/auto|manual)。
|
||||||
|
* 分类字段已下线:匹配只靠 bizType + keywords + isDefault。
|
||||||
* 版式字节与映射在版本表;历史报价应绑 templateId + versionNo。
|
* 版式字节与映射在版本表;历史报价应绑 templateId + versionNo。
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@@ -38,9 +39,6 @@ public class InquiryQuoteTemplate implements Serializable {
|
|||||||
@ApiModelProperty("海运/空运/陆运")
|
@ApiModelProperty("海运/空运/陆运")
|
||||||
private String bizType;
|
private String bizType;
|
||||||
|
|
||||||
@ApiModelProperty("分类")
|
|
||||||
private String category;
|
|
||||||
|
|
||||||
@ApiModelProperty("关键词 JSON 数组字符串")
|
@ApiModelProperty("关键词 JSON 数组字符串")
|
||||||
private String keywordsJson;
|
private String keywordsJson;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -15,13 +15,13 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public interface IInquiryQuoteTemplateService extends IService<InquiryQuoteTemplate> {
|
public interface IInquiryQuoteTemplateService extends IService<InquiryQuoteTemplate> {
|
||||||
|
|
||||||
IPage<Map<String, Object>> pageWithVersion(String keyword, String bizType, String category, String status,
|
IPage<Map<String, Object>> pageWithVersion(String keyword, String bizType, String status,
|
||||||
int pageNo, int pageSize);
|
int pageNo, int pageSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新建逻辑模板并写入第一版;或对已有模板追加新版本。
|
* 新建逻辑模板并写入第一版;或对已有模板追加新版本。
|
||||||
*/
|
*/
|
||||||
Map<String, Object> uploadVersion(String templateId, String name, String bizType, String category,
|
Map<String, Object> uploadVersion(String templateId, String name, String bizType,
|
||||||
String keywordsJson, String mode, boolean isDefault,
|
String keywordsJson, String mode, boolean isDefault,
|
||||||
MultipartFile file, String operator);
|
MultipartFile file, String operator);
|
||||||
|
|
||||||
|
|||||||
+3
-22
@@ -57,20 +57,17 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
private String contractPath;
|
private String contractPath;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<Map<String, Object>> pageWithVersion(String keyword, String bizType, String category, String status,
|
public IPage<Map<String, Object>> pageWithVersion(String keyword, String bizType, String status,
|
||||||
int pageNo, int pageSize) {
|
int pageNo, int pageSize) {
|
||||||
QueryWrapper<InquiryQuoteTemplate> qw = new QueryWrapper<>();
|
QueryWrapper<InquiryQuoteTemplate> qw = new QueryWrapper<>();
|
||||||
if (StringUtils.isNotBlank(bizType)) {
|
if (StringUtils.isNotBlank(bizType)) {
|
||||||
qw.eq("biz_type", bizType);
|
qw.eq("biz_type", bizType);
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(category)) {
|
|
||||||
qw.eq("category", category);
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(status)) {
|
if (StringUtils.isNotBlank(status)) {
|
||||||
qw.eq("status", status);
|
qw.eq("status", status);
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(keyword)) {
|
if (StringUtils.isNotBlank(keyword)) {
|
||||||
qw.and(w -> w.like("name", keyword).or().like("category", keyword).or().like("keywords_json", keyword));
|
qw.and(w -> w.like("name", keyword).or().like("keywords_json", keyword));
|
||||||
}
|
}
|
||||||
qw.orderByDesc("update_time").orderByDesc("create_time");
|
qw.orderByDesc("update_time").orderByDesc("create_time");
|
||||||
IPage<InquiryQuoteTemplate> page = page(new Page<>(pageNo, pageSize), qw);
|
IPage<InquiryQuoteTemplate> page = page(new Page<>(pageNo, pageSize), qw);
|
||||||
@@ -88,7 +85,6 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
m.put("id", t.getId());
|
m.put("id", t.getId());
|
||||||
m.put("name", t.getName());
|
m.put("name", t.getName());
|
||||||
m.put("bizType", t.getBizType());
|
m.put("bizType", t.getBizType());
|
||||||
m.put("category", t.getCategory());
|
|
||||||
m.put("keywords", parseKeywords(t.getKeywordsJson()));
|
m.put("keywords", parseKeywords(t.getKeywordsJson()));
|
||||||
m.put("mode", t.getMode());
|
m.put("mode", t.getMode());
|
||||||
m.put("status", t.getStatus());
|
m.put("status", t.getStatus());
|
||||||
@@ -122,7 +118,7 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Map<String, Object> uploadVersion(String templateId, String name, String bizType, String category,
|
public Map<String, Object> uploadVersion(String templateId, String name, String bizType,
|
||||||
String keywordsJson, String mode, boolean isDefault,
|
String keywordsJson, String mode, boolean isDefault,
|
||||||
MultipartFile file, String operator) {
|
MultipartFile file, String operator) {
|
||||||
if (file == null || file.isEmpty()) {
|
if (file == null || file.isEmpty()) {
|
||||||
@@ -138,7 +134,6 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
template = new InquiryQuoteTemplate();
|
template = new InquiryQuoteTemplate();
|
||||||
template.setName(StringUtils.defaultIfBlank(name, stripExt(original)));
|
template.setName(StringUtils.defaultIfBlank(name, stripExt(original)));
|
||||||
template.setBizType(StringUtils.defaultIfBlank(bizType, "海运"));
|
template.setBizType(StringUtils.defaultIfBlank(bizType, "海运"));
|
||||||
template.setCategory(StringUtils.defaultIfBlank(category, defaultCategory(template.getBizType())));
|
|
||||||
template.setKeywordsJson(StringUtils.defaultIfBlank(keywordsJson, "[]"));
|
template.setKeywordsJson(StringUtils.defaultIfBlank(keywordsJson, "[]"));
|
||||||
template.setMode(StringUtils.defaultIfBlank(mode, "auto"));
|
template.setMode(StringUtils.defaultIfBlank(mode, "auto"));
|
||||||
template.setStatus("disabled");
|
template.setStatus("disabled");
|
||||||
@@ -159,9 +154,6 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
if (StringUtils.isNotBlank(bizType)) {
|
if (StringUtils.isNotBlank(bizType)) {
|
||||||
template.setBizType(bizType);
|
template.setBizType(bizType);
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(category)) {
|
|
||||||
template.setCategory(category);
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(keywordsJson)) {
|
if (StringUtils.isNotBlank(keywordsJson)) {
|
||||||
template.setKeywordsJson(keywordsJson);
|
template.setKeywordsJson(keywordsJson);
|
||||||
}
|
}
|
||||||
@@ -445,7 +437,6 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
m.put("templateId", t.getId());
|
m.put("templateId", t.getId());
|
||||||
m.put("name", t.getName());
|
m.put("name", t.getName());
|
||||||
m.put("bizType", t.getBizType());
|
m.put("bizType", t.getBizType());
|
||||||
m.put("category", t.getCategory());
|
|
||||||
m.put("keywords", parseKeywords(t.getKeywordsJson()));
|
m.put("keywords", parseKeywords(t.getKeywordsJson()));
|
||||||
m.put("isDefault", t.getIsDefault() != null && t.getIsDefault() == 1);
|
m.put("isDefault", t.getIsDefault() != null && t.getIsDefault() == 1);
|
||||||
m.put("versionNo", ver.getVersionNo());
|
m.put("versionNo", ver.getVersionNo());
|
||||||
@@ -716,14 +707,4 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
|||||||
int i = name.lastIndexOf('.');
|
int i = name.lastIndexOf('.');
|
||||||
return i > 0 ? name.substring(0, i) : name;
|
return i > 0 ? name.substring(0, i) : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String defaultCategory(String bizType) {
|
|
||||||
if ("空运".equals(bizType)) {
|
|
||||||
return "空运到港报价单模板";
|
|
||||||
}
|
|
||||||
if ("陆运".equals(bizType)) {
|
|
||||||
return "国内拼车";
|
|
||||||
}
|
|
||||||
return "海运报价单模板";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
# 是否启用mock(生产必须 false,否则会走本地 Mock 像旧骨架)
|
# 是否启用mock(生产必须 false,否则会走本地 Mock 像旧骨架)
|
||||||
VITE_USE_MOCK = false
|
VITE_USE_MOCK = false
|
||||||
|
|
||||||
|
# 网站标题(浏览器标签后缀;缺省会显示「页面名 undefined」)
|
||||||
|
VITE_GLOB_APP_TITLE = 友通达询价系统
|
||||||
|
|
||||||
|
# 简称,用于生产 _app.config.js 变量名;勿空格/数字开头
|
||||||
|
VITE_GLOB_APP_SHORT_NAME = YtdInquiryAdmin
|
||||||
|
|
||||||
# 发布路径
|
# 发布路径
|
||||||
VITE_PUBLIC_PATH = /
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export function useTitle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tTitle = t(route?.meta?.title as string);
|
const tTitle = t(route?.meta?.title as string);
|
||||||
pageTitle.value = tTitle ? ` ${tTitle} - ${title} ` : `${title}`;
|
// 生产 _app.config.js 若漏打 VITE_GLOB_APP_TITLE,title 会是 undefined → 标签出现「报价模板 undefined」
|
||||||
|
const appTitle = title || '友通达询价系统';
|
||||||
|
pageTitle.value = tTitle ? `${tTitle} - ${appTitle}` : `${appTitle}`;
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
* placeholderCount 语义改为 mappingPointCount(映射点数)。
|
* placeholderCount 语义改为 mappingPointCount(映射点数)。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表行类型。分类字段已从运营页移除(筛选项/列/表单均不展示);
|
||||||
|
* 主账库仍可存 category,由服务端按业务默认,前端不再读写。
|
||||||
|
*/
|
||||||
export type TemplateRow = {
|
export type TemplateRow = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
bizType: "空运" | "海运" | "陆运";
|
bizType: "空运" | "海运" | "陆运";
|
||||||
category: string;
|
|
||||||
fileLabel: string;
|
fileLabel: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
format: "excel" | "pdf";
|
format: "excel" | "pdf";
|
||||||
@@ -28,34 +31,6 @@ export type TemplateRow = {
|
|||||||
objectKey?: string;
|
objectKey?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 业务 → 分类(与 contracts/quote-template-mapping-v1.json 一致) */
|
|
||||||
export const CATEGORY_BY_BIZ: Record<TemplateRow["bizType"], string[]> = {
|
|
||||||
海运: ["海运报价单模板"],
|
|
||||||
空运: ["空运到港报价单模板", "空运到门报价单模板"],
|
|
||||||
陆运: [
|
|
||||||
"双清或中越",
|
|
||||||
"国内整车",
|
|
||||||
"国内拼车",
|
|
||||||
"跨境整车",
|
|
||||||
"跨境集拼",
|
|
||||||
"中港",
|
|
||||||
"凭祥保税仓&普仓仓储",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ALL_CATEGORIES = [
|
|
||||||
"空运到港报价单模板",
|
|
||||||
"空运到门报价单模板",
|
|
||||||
"海运报价单模板",
|
|
||||||
"双清或中越",
|
|
||||||
"国内整车",
|
|
||||||
"国内拼车",
|
|
||||||
"跨境整车",
|
|
||||||
"跨境集拼",
|
|
||||||
"中港",
|
|
||||||
"凭祥保税仓&普仓仓储",
|
|
||||||
];
|
|
||||||
|
|
||||||
/** 将主账 list 记录规范为页面行 */
|
/** 将主账 list 记录规范为页面行 */
|
||||||
export function mapApiRow(raw: any): TemplateRow {
|
export function mapApiRow(raw: any): TemplateRow {
|
||||||
const created =
|
const created =
|
||||||
@@ -68,7 +43,6 @@ export function mapApiRow(raw: any): TemplateRow {
|
|||||||
id: String(raw.id || ""),
|
id: String(raw.id || ""),
|
||||||
name: raw.name || "",
|
name: raw.name || "",
|
||||||
bizType: (raw.bizType || "海运") as TemplateRow["bizType"],
|
bizType: (raw.bizType || "海运") as TemplateRow["bizType"],
|
||||||
category: raw.category || "",
|
|
||||||
fileLabel: raw.fileLabel || raw.name || "",
|
fileLabel: raw.fileLabel || raw.name || "",
|
||||||
fileName: raw.fileName || "",
|
fileName: raw.fileName || "",
|
||||||
format: raw.format === "pdf" ? "pdf" : "excel",
|
format: raw.format === "pdf" ? "pdf" : "excel",
|
||||||
|
|||||||
@@ -7,12 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import { computed, onMounted, reactive, ref } from "vue";
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import {
|
import { mapApiRow, type TemplateRow } from "/@/views/inquiry/mock/templates";
|
||||||
ALL_CATEGORIES,
|
|
||||||
CATEGORY_BY_BIZ,
|
|
||||||
mapApiRow,
|
|
||||||
type TemplateRow,
|
|
||||||
} from "/@/views/inquiry/mock/templates";
|
|
||||||
import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
|
import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
|
||||||
import {
|
import {
|
||||||
disableTemplate,
|
disableTemplate,
|
||||||
@@ -47,7 +42,6 @@ const rows = ref<TemplateRow[]>([]);
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
const bizFilter = ref("");
|
const bizFilter = ref("");
|
||||||
const categoryFilter = ref("");
|
|
||||||
const statusFilter = ref("");
|
const statusFilter = ref("");
|
||||||
|
|
||||||
const formOpen = ref(false);
|
const formOpen = ref(false);
|
||||||
@@ -59,10 +53,10 @@ const previewVm = ref<PreviewViewModel>(emptyPreviewViewModel());
|
|||||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||||
const pendingFile = ref<File | null>(null);
|
const pendingFile = ref<File | null>(null);
|
||||||
|
|
||||||
|
/** 运营表单不含「分类」:主账上传时按业务自行补默认分类 */
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: "",
|
name: "",
|
||||||
bizType: "海运" as TemplateRow["bizType"],
|
bizType: "海运" as TemplateRow["bizType"],
|
||||||
category: "",
|
|
||||||
mode: "auto" as "auto" | "manual",
|
mode: "auto" as "auto" | "manual",
|
||||||
keywordItems: [""] as string[],
|
keywordItems: [""] as string[],
|
||||||
isDefault: false,
|
isDefault: false,
|
||||||
@@ -98,10 +92,6 @@ const previewTotalText = computed(() => {
|
|||||||
return `${vm.currency || "USD"} ${amt}`;
|
return `${vm.currency || "USD"} ${amt}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
function defaultCategoryForBiz(biz: TemplateRow["bizType"]) {
|
|
||||||
return (CATEGORY_BY_BIZ[biz] || [])[0] || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const filtered = computed(() => rows.value);
|
const filtered = computed(() => rows.value);
|
||||||
|
|
||||||
function bizTag(biz: string) {
|
function bizTag(biz: string) {
|
||||||
@@ -110,13 +100,18 @@ function bizTag(biz: string) {
|
|||||||
return "tag-blue";
|
return "tag-blue";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 列表展示用:关键词用顿号拼接;空则显示占位 */
|
||||||
|
function formatKeywords(keywords: string[] | undefined) {
|
||||||
|
if (!keywords?.length) return "";
|
||||||
|
return keywords.map((k) => String(k || "").trim()).filter(Boolean).join("、");
|
||||||
|
}
|
||||||
|
|
||||||
async function loadList() {
|
async function loadList() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res: any = await getTemplateList({
|
const res: any = await getTemplateList({
|
||||||
keyword: keyword.value.trim() || undefined,
|
keyword: keyword.value.trim() || undefined,
|
||||||
bizType: bizFilter.value || undefined,
|
bizType: bizFilter.value || undefined,
|
||||||
category: categoryFilter.value || undefined,
|
|
||||||
status: statusFilter.value || undefined,
|
status: statusFilter.value || undefined,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 200,
|
pageSize: 200,
|
||||||
@@ -139,7 +134,6 @@ function resetForm() {
|
|||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
name: "",
|
name: "",
|
||||||
bizType: "海运" as TemplateRow["bizType"],
|
bizType: "海运" as TemplateRow["bizType"],
|
||||||
category: defaultCategoryForBiz("海运"),
|
|
||||||
mode: "auto" as const,
|
mode: "auto" as const,
|
||||||
keywordItems: [""],
|
keywordItems: [""],
|
||||||
isDefault: false,
|
isDefault: false,
|
||||||
@@ -165,7 +159,6 @@ function openEdit(row: TemplateRow) {
|
|||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
name: row.name,
|
name: row.name,
|
||||||
bizType: row.bizType,
|
bizType: row.bizType,
|
||||||
category: row.category || defaultCategoryForBiz(row.bizType),
|
|
||||||
mode: row.mode || "auto",
|
mode: row.mode || "auto",
|
||||||
keywordItems: row.keywords.length ? [...row.keywords] : [""],
|
keywordItems: row.keywords.length ? [...row.keywords] : [""],
|
||||||
isDefault: Boolean(row.isDefault),
|
isDefault: Boolean(row.isDefault),
|
||||||
@@ -318,8 +311,7 @@ async function submitForm() {
|
|||||||
message.warning("请至少填写一个关键词");
|
message.warning("请至少填写一个关键词");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 分类/模式不对运营展示:分类按业务默认,模式新建默认 auto,编辑保留原值
|
// 模式不对运营展示:新建默认 auto,编辑保留原值;分类交主账按业务补默认
|
||||||
const category = defaultCategoryForBiz(form.bizType);
|
|
||||||
const mode = editing.value?.mode || "auto";
|
const mode = editing.value?.mode || "auto";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -328,7 +320,6 @@ async function submitForm() {
|
|||||||
if (editing.value?.id) fd.append("templateId", editing.value.id);
|
if (editing.value?.id) fd.append("templateId", editing.value.id);
|
||||||
fd.append("name", form.name.trim());
|
fd.append("name", form.name.trim());
|
||||||
fd.append("bizType", form.bizType);
|
fd.append("bizType", form.bizType);
|
||||||
fd.append("category", category);
|
|
||||||
fd.append("keywordsJson", JSON.stringify(keywords));
|
fd.append("keywordsJson", JSON.stringify(keywords));
|
||||||
fd.append("mode", mode);
|
fd.append("mode", mode);
|
||||||
fd.append("isDefault", String(form.isDefault));
|
fd.append("isDefault", String(form.isDefault));
|
||||||
@@ -340,7 +331,6 @@ async function submitForm() {
|
|||||||
id: editing.value.id,
|
id: editing.value.id,
|
||||||
name: form.name.trim(),
|
name: form.name.trim(),
|
||||||
bizType: form.bizType,
|
bizType: form.bizType,
|
||||||
category,
|
|
||||||
keywordsJson: JSON.stringify(keywords),
|
keywordsJson: JSON.stringify(keywords),
|
||||||
mode,
|
mode,
|
||||||
isDefault: form.isDefault ? 1 : 0,
|
isDefault: form.isDefault ? 1 : 0,
|
||||||
@@ -398,10 +388,6 @@ async function downloadPreviewOrOriginal() {
|
|||||||
<option value="海运">海运</option>
|
<option value="海运">海运</option>
|
||||||
<option value="陆运">陆运</option>
|
<option value="陆运">陆运</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="categoryFilter" @change="loadList">
|
|
||||||
<option value="">全部分类</option>
|
|
||||||
<option v-for="c in ALL_CATEGORIES" :key="c" :value="c">{{ c }}</option>
|
|
||||||
</select>
|
|
||||||
<select v-model="statusFilter" @change="loadList">
|
<select v-model="statusFilter" @change="loadList">
|
||||||
<option value="">全部状态</option>
|
<option value="">全部状态</option>
|
||||||
<option value="enabled">启用</option>
|
<option value="enabled">启用</option>
|
||||||
@@ -415,8 +401,7 @@ async function downloadPreviewOrOriginal() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>模板名称</th>
|
<th>模板名称</th>
|
||||||
<th>业务</th>
|
<th>业务</th>
|
||||||
<th>分类</th>
|
<th>关键词</th>
|
||||||
<th>模板文件</th>
|
|
||||||
<th>解析状态</th>
|
<th>解析状态</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>总使用次数</th>
|
<th>总使用次数</th>
|
||||||
@@ -434,8 +419,9 @@ async function downloadPreviewOrOriginal() {
|
|||||||
<td>
|
<td>
|
||||||
<span class="tag" :class="bizTag(row.bizType)">{{ row.bizType }}</span>
|
<span class="tag" :class="bizTag(row.bizType)">{{ row.bizType }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="qt-col-muted">{{ row.category }}</td>
|
<td class="qt-col-keywords" :title="formatKeywords(row.keywords)">
|
||||||
<td class="qt-col-file">{{ row.fileLabel }}</td>
|
{{ formatKeywords(row.keywords) || "—" }}
|
||||||
|
</td>
|
||||||
<td class="qt-col-muted">{{ row.parseStatus }}</td>
|
<td class="qt-col-muted">{{ row.parseStatus }}</td>
|
||||||
<td class="qt-col-muted">{{ row.status === "enabled" ? "启用" : "禁用" }}</td>
|
<td class="qt-col-muted">{{ row.status === "enabled" ? "启用" : "禁用" }}</td>
|
||||||
<td class="qt-col-count">{{ row.useCount }} 次</td>
|
<td class="qt-col-count">{{ row.useCount }} 次</td>
|
||||||
@@ -732,10 +718,14 @@ async function downloadPreviewOrOriginal() {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
}
|
}
|
||||||
.qt-col-file {
|
.qt-col-keywords {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
|
max-width: 220px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.qt-col-count {
|
.qt-col-count {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
职责:
|
职责:
|
||||||
1) 扫描仓库「海陆空报价单模板」目录(忽略 ~$ 临时文件与嵌套重复)
|
1) 扫描仓库「海陆空报价单模板」目录(忽略 ~$ 临时文件与嵌套重复)
|
||||||
2) 按文件名判定 bizType / category / mode(auto|manual) / 关键词
|
2) 按文件名判定 bizType / mode(auto|manual) / 关键词
|
||||||
3) 用 openpyxl 做标签粗映射,写出 seed 清单 JSON(供人工核对)
|
3) 用 openpyxl 做标签粗映射,写出 seed 清单 JSON(供人工核对)
|
||||||
4) 若提供 --upload-url 与 token,则逐个 POST 到主账 /inquiry/quoteTemplate/upload
|
4) 若提供 --upload-url 与 token,则逐个 POST 到主账 /inquiry/quoteTemplate/upload
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ OUT_JSON = ROOT / "deploy" / "seed" / "quote_templates_seed_manifest_v1.json"
|
|||||||
|
|
||||||
|
|
||||||
def classify(name: str) -> dict[str, Any]:
|
def classify(name: str) -> dict[str, Any]:
|
||||||
"""按文件名归类业务/分类/模式。"""
|
"""按文件名归类业务/模式/关键词(分类字段已下线)。"""
|
||||||
n = name
|
n = name
|
||||||
mode = "auto"
|
mode = "auto"
|
||||||
if any(k in n for k in ("人工报价", "双清", "保税仓", "仓储")):
|
if any(k in n for k in ("人工报价", "双清", "保税仓", "仓储")):
|
||||||
@@ -41,82 +41,69 @@ def classify(name: str) -> dict[str, Any]:
|
|||||||
if "海运" in n:
|
if "海运" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "海运",
|
"bizType": "海运",
|
||||||
"category": "海运报价单模板",
|
|
||||||
"keywords": ["海运", "整柜", "FCL"],
|
"keywords": ["海运", "整柜", "FCL"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "空运到门" in n:
|
if "空运到门" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "空运",
|
"bizType": "空运",
|
||||||
"category": "空运到门报价单模板",
|
|
||||||
"keywords": ["空运", "到门"],
|
"keywords": ["空运", "到门"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "空运" in n:
|
if "空运" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "空运",
|
"bizType": "空运",
|
||||||
"category": "空运到港报价单模板",
|
|
||||||
"keywords": ["空运", "到港"],
|
"keywords": ["空运", "到港"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "中港" in n:
|
if "中港" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "中港",
|
|
||||||
"keywords": ["中港", "陆运"],
|
"keywords": ["中港", "陆运"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "拼车" in n:
|
if "拼车" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "国内拼车",
|
|
||||||
"keywords": ["拼车", "零担"],
|
"keywords": ["拼车", "零担"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "国内" in n and "整车" in n:
|
if "国内" in n and "整车" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "国内整车",
|
|
||||||
"keywords": ["整车", "国内"],
|
"keywords": ["整车", "国内"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "跨境整车" in n:
|
if "跨境整车" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "跨境整车",
|
|
||||||
"keywords": ["跨境", "整车"],
|
"keywords": ["跨境", "整车"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "跨境集拼" in n or "中越" in n or "集拼" in n:
|
if "跨境集拼" in n or "中越" in n or "集拼" in n:
|
||||||
cat = "双清或中越" if ("中越" in n or "双清" in n) else "跨境集拼"
|
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": cat,
|
|
||||||
"keywords": ["集拼", "跨境"] if "集拼" in n else ["中越"],
|
"keywords": ["集拼", "跨境"] if "集拼" in n else ["中越"],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
if "凭祥" in n or "仓储" in n:
|
if "凭祥" in n or "仓储" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "凭祥保税仓&普仓仓储",
|
|
||||||
"keywords": ["仓储", "凭祥"],
|
"keywords": ["仓储", "凭祥"],
|
||||||
"mode": "manual",
|
"mode": "manual",
|
||||||
}
|
}
|
||||||
if "双清" in n:
|
if "双清" in n:
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "双清或中越",
|
|
||||||
"keywords": ["双清"],
|
"keywords": ["双清"],
|
||||||
"mode": "manual",
|
"mode": "manual",
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
"bizType": "陆运",
|
"bizType": "陆运",
|
||||||
"category": "国内拼车",
|
|
||||||
"keywords": [Path(name).stem[:20]],
|
"keywords": [Path(name).stem[:20]],
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def unique_xlsx_files() -> list[Path]:
|
def unique_xlsx_files() -> list[Path]:
|
||||||
files = [p for p in TEMPLATE_DIR.rglob("*.xlsx") if not p.name.startswith("~$")]
|
files = [p for p in TEMPLATE_DIR.rglob("*.xlsx") if not p.name.startswith("~$")]
|
||||||
by_name: dict[str, Path] = {}
|
by_name: dict[str, Path] = {}
|
||||||
@@ -181,7 +168,6 @@ def build_manifest() -> list[dict[str, Any]]:
|
|||||||
"fileName": path.name,
|
"fileName": path.name,
|
||||||
"relativePath": str(path.relative_to(ROOT)).replace("\\", "/"),
|
"relativePath": str(path.relative_to(ROOT)).replace("\\", "/"),
|
||||||
"bizType": meta["bizType"],
|
"bizType": meta["bizType"],
|
||||||
"category": meta["category"],
|
|
||||||
"keywords": meta["keywords"],
|
"keywords": meta["keywords"],
|
||||||
"mode": meta["mode"],
|
"mode": meta["mode"],
|
||||||
"suggestedMappingPointCount": mapped.get("mappingPointCount", 0),
|
"suggestedMappingPointCount": mapped.get("mappingPointCount", 0),
|
||||||
@@ -201,7 +187,6 @@ def upload_one(base_url: str, token: str, item: dict[str, Any]) -> dict[str, Any
|
|||||||
fields = {
|
fields = {
|
||||||
"name": Path(item["fileName"]).stem,
|
"name": Path(item["fileName"]).stem,
|
||||||
"bizType": item["bizType"],
|
"bizType": item["bizType"],
|
||||||
"category": item["category"],
|
|
||||||
"keywordsJson": json.dumps(item["keywords"], ensure_ascii=False),
|
"keywordsJson": json.dumps(item["keywords"], ensure_ascii=False),
|
||||||
"mode": item["mode"],
|
"mode": item["mode"],
|
||||||
"isDefault": "false",
|
"isDefault": "false",
|
||||||
@@ -259,7 +244,7 @@ def main(argv: list[str]) -> int:
|
|||||||
print(f"wrote {OUT_JSON} count={len(items)}")
|
print(f"wrote {OUT_JSON} count={len(items)}")
|
||||||
for it in items:
|
for it in items:
|
||||||
print(
|
print(
|
||||||
f" [{it['mode']}] {it['bizType']}/{it['category']} "
|
f" [{it['mode']}] {it['bizType']} "
|
||||||
f"map~{it['suggestedMappingPointCount']} {it['fileName']}"
|
f"map~{it['suggestedMappingPointCount']} {it['fileName']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user