hub-gif 614d631261 feat(pipeline): 竞品简报与策略管线、报告导出及 JD 前端联动
补充策略决策键、fixture 与相关单测;分析/营销包本地存储与任务状态;竞品简报 schema 与 LLM 章节生成、促销与导出调整;并纳入 Cursor 工程规则 user-dev-style。

Made-with: Cursor
2026-04-27 15:48:09 +08:00

86 lines
2.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 任务列表:与「搜索采集 / 报告 / 策略」等页共享,列表轮询只保留一路定时器。
*/
import { defineStore } from 'pinia'
function jsonFetch(path, opts = {}) {
return fetch(path, {
headers: { 'Content-Type': 'application/json', ...opts.headers },
...opts,
})
}
function isActiveJobStatus(status) {
return status === 'pending' || status === 'running'
}
/** API 与路由里 id 可能是 number表单 v-model 常为 string */
function sameJobId(a, b) {
if (a == null || b == null) return false
return a === b || String(a) === String(b)
}
let jobsListPollTimer = null
function stopJobsListPoll() {
if (jobsListPollTimer != null) {
clearInterval(jobsListPollTimer)
jobsListPollTimer = null
}
}
export const useJobStore = defineStore('ma-jobs', {
state: () => ({
jobs: [],
}),
actions: {
_syncJobsListPoll() {
const hasActive = this.jobs.some((j) => isActiveJobStatus(j.status))
if (!hasActive) {
stopJobsListPoll()
return
}
if (jobsListPollTimer != null) return
jobsListPollTimer = setInterval(() => {
useJobStore().fetchJobsListQuietly()
}, 3000)
},
setJobs(list) {
this.jobs = Array.isArray(list) ? list : []
this._syncJobsListPoll()
},
/**
* 用单条任务详情写回列表(与 PATCH 轮询、详情 GET 对齐)。
* @param {Record<string, unknown>} updated
*/
mergeJob(updated) {
if (!updated || updated.id == null) return
const idx = this.jobs.findIndex((x) => sameJobId(x.id, updated.id))
if (idx >= 0) {
this.jobs.splice(idx, 1, updated)
this._syncJobsListPoll()
}
},
async fetchJobsListQuietly() {
try {
const r = await jsonFetch('/api/jobs/')
if (r.ok) {
this.setJobs(await r.json())
}
} catch {
/* 忽略网络错误,下一轮再试 */
}
},
async refreshJobs() {
const r = await jsonFetch('/api/jobs/')
if (!r.ok) throw new Error(await r.text())
this.setJobs(await r.json())
},
},
})