diff --git a/frontend/src/views/jd/JdStrategyBuildView.vue b/frontend/src/views/jd/JdStrategyBuildView.vue index e660cb2..fdd5975 100644 --- a/frontend/src/views/jd/JdStrategyBuildView.vue +++ b/frontend/src/views/jd/JdStrategyBuildView.vue @@ -36,6 +36,12 @@ const strategyGeneratingOtherTask = computed( /** 勾选则本次仅规则稿(不调用大模型);默认不勾选即走大模型 */ const rulesOnlyThisRun = ref(false) +/** 与竞品矩阵细类一致;空字符串表示不收窄(全关键词样本) */ +const strategyMatrixScope = ref('') +const matrixGroups = ref([]) +const briefMatrixLoading = ref(false) +const briefMatrixErr = ref('') + const decisions = reactive({ product_role: '', time_horizon: '', @@ -102,6 +108,9 @@ function buildPayload() { ack_risk_keywords: decisions.ack_risk_keywords, ack_risk_price: decisions.ack_risk_price, ack_risk_concentration: decisions.ack_risk_concentration, + ...(strategyMatrixScope.value + ? { strategy_matrix_group: strategyMatrixScope.value } + : {}), } } @@ -121,6 +130,37 @@ async function loadList() { } } +async function loadMatrixGroupsForJob(id) { + matrixGroups.value = [] + strategyMatrixScope.value = '' + briefMatrixErr.value = '' + if (!id) return + briefMatrixLoading.value = true + try { + const r = await api(`/api/jobs/${id}/competitor-brief/`) + const text = await r.text() + if (!r.ok) { + try { + briefMatrixErr.value = JSON.parse(text).detail || text + } catch { + briefMatrixErr.value = text || `HTTP ${r.status}` + } + return + } + const data = JSON.parse(text) + const mg = data.matrix_groups + matrixGroups.value = Array.isArray(mg) ? mg : [] + const saved = sessionStorage.getItem(`ma_strategy_scope_${id}`) + if (saved && matrixGroups.value.some((g) => g.group === saved)) { + strategyMatrixScope.value = saved + } + } catch (e) { + briefMatrixErr.value = String(e) + } finally { + briefMatrixLoading.value = false + } +} + async function generateAndGoPreview() { const id = selectedId.value if (!id) return @@ -160,6 +200,17 @@ async function generateAndGoPreview() { onMounted(loadList) +watch(selectedId, (id) => { + loadMatrixGroupsForJob(id) +}) + +watch(strategyMatrixScope, (v) => { + const jid = selectedId.value + if (!jid) return + if (v) sessionStorage.setItem(`ma_strategy_scope_${jid}`, v) + else sessionStorage.removeItem(`ma_strategy_scope_${jid}`) +}) + watch( () => route.query.job, (j) => { @@ -184,7 +235,7 @@ watch(

策略生成

- 选择已成功任务,在下方填空与勾选。默认使用大模型在规则底稿与同任务数据摘要基础上成稿(需服务端已配置网关)。若需更快、不调用智能服务,可勾选「本次仅生成规则稿」。策略配置与「分析报告生成」页的报告配置相互独立。策略稿与宿主报告第九章的归纳默认对齐。提交后跳转到 + 选择已成功任务,在下方选择策略类目(与竞品矩阵细类一致;选「全部分类」则与全关键词样本一致)。其余填空与勾选。默认使用大模型在规则底稿与同任务数据摘要基础上成稿(需服务端已配置网关)。若需更快、不调用智能服务,可勾选「本次仅生成规则稿」。策略配置与「分析报告生成」页的报告配置相互独立。策略稿与宿主报告第九章的归纳默认对齐;收窄类目时另并入该细类在报告中的大模型归纳节选。提交后跳转到 策略稿预览决策在本页完成:已填项会写入底稿并由大模型落实为执行句;未填项由大模型结合数据补全。成稿不再重复「请再选」式表述。

@@ -209,12 +260,27 @@ watch( +
+ + + 正在加载矩阵分组… +
+

{{ briefMatrixErr }}

任务 #{{ strategyDraftPendingJobId }} 的策略稿正在生成中,请稍候再切换任务或重复提交。

@@ -397,6 +463,13 @@ watch( gap: 0.75rem; margin-bottom: 0.75rem; } +.toolbar-stack { + flex-direction: column; + align-items: stretch; +} +.toolbar-stack .sel-label { + margin-bottom: -0.25rem; +} .sel-label { font-size: 0.85rem; font-weight: 500;