diff --git a/frontend-web/.env.development b/frontend-web/.env.development index e8c1e371..79771dc1 100644 --- a/frontend-web/.env.development +++ b/frontend-web/.env.development @@ -1,6 +1,9 @@ # 本地开发(pnpm dev)— 与原先 .env 保持一致 # 个人覆盖请新建 .env.development.local(已 gitignore,优先级更高) +# 任务看板数据源:true=真实接口(默认);改成 false 可让任务分析/三情分析两页走本地假数据。 +VITE_USE_REAL_DATA=true + # Gateway / DeerFlow API VITE_BACKEND_BASE_URL=http://127.0.0.1:8001 VITE_LANGGRAPH_BASE_URL=http://127.0.0.1:8001/api diff --git a/frontend-web/.env.example b/frontend-web/.env.example index f0dda406..9653a1c4 100644 --- a/frontend-web/.env.example +++ b/frontend-web/.env.example @@ -28,6 +28,12 @@ # --- 生产打包(见 .env.production)--- # 部署前修改 .env.production 中的地址,再执行 pnpm build +# --- 任务看板(任务分析 / 三情分析)数据源开关 --- +# true=走真实接口;false 或未设=这两页使用本地假数据(演示用,无需后端)。 +# .env.development / .env.production / .env.network / .env.sit 均默认 true(真实数据), +# 仅本机 .env 默认 false(假数据)。改后需重启 dev / 重新打包生效。 +# VITE_USE_REAL_DATA=true + # MaxKey 鉴权根地址(无界 onlineTicket 换 token) # VITE_AUTH_BASE_URL=https://auth.y.sit.4.cn # VITE_AIPI_CONSUMER_URL=https://ch1.b.uat.4.cn/consumer diff --git a/frontend-web/.env.network b/frontend-web/.env.network index bf2a9011..923b70c7 100644 --- a/frontend-web/.env.network +++ b/frontend-web/.env.network @@ -1,6 +1,9 @@ # 本地开发(pnpm dev)— 与原先 .env 保持一致 # 个人覆盖请新建 .env.development.local(已 gitignore,优先级更高) +# 任务看板数据源:true=真实接口(默认);改成 false 可让任务分析/三情分析两页走本地假数据。 +VITE_USE_REAL_DATA=true + # Gateway / DeerFlow API VITE_BACKEND_BASE_URL=http://47.88.25.99:7001 VITE_LANGGRAPH_BASE_URL=http://47.88.25.99:7001/api diff --git a/frontend-web/.env.production b/frontend-web/.env.production index d7dac2cd..413a18cf 100644 --- a/frontend-web/.env.production +++ b/frontend-web/.env.production @@ -1,6 +1,9 @@ # 生产打包(pnpm build)— 部署前请改成实际内网/生产地址 # 打包后变量会写入 dist,运行时不可再改 +# 任务看板数据源:true=真实接口(默认);改成 false 可让任务分析/三情分析两页走本地假数据。 +VITE_USE_REAL_DATA=true + # Gateway / DeerFlow API VITE_BACKEND_BASE_URL=https://ch1.b.uat.4.cn/deerflow VITE_LANGGRAPH_BASE_URL=https://ch1.b.uat.4.cn/deerflow/api diff --git a/frontend-web/.env.sit b/frontend-web/.env.sit index dbaa36d4..09d23e7b 100644 --- a/frontend-web/.env.sit +++ b/frontend-web/.env.sit @@ -1,6 +1,9 @@ # SIT 环境打包(pnpm build:sit / npm run build:sit)— 接口地址为 UAT 的 sit 版本 # 打包后变量会写入 dist,运行时不可再改 +# 任务看板数据源:true=真实接口(默认);改成 false 可让任务分析/三情分析两页走本地假数据。 +VITE_USE_REAL_DATA=true + # Gateway / DeerFlow API VITE_BACKEND_BASE_URL=https://ch1.b.sit.4.cn/deerflow VITE_LANGGRAPH_BASE_URL=https://ch1.b.sit.4.cn/deerflow/api diff --git a/frontend-web/src/strategy-components/components/task-components/task-mock-data.ts b/frontend-web/src/strategy-components/components/task-components/task-mock-data.ts new file mode 100644 index 00000000..dfad039b --- /dev/null +++ b/frontend-web/src/strategy-components/components/task-components/task-mock-data.ts @@ -0,0 +1,443 @@ +import { TaskListItem } from './TaskManagement'; + +/** + * 任务看板假数据 —— 给「任务分析」「三情分析」两个页面提供演示数据, + * 用于没有后端 `/task/cop-tasks` / `/three/cop-three-tasks` 接口时本地预览。 + * + * 是否启用由环境变量 `VITE_USE_REAL_DATA` 控制(见 `utils/request.ts`): + * - `VITE_USE_REAL_DATA=true` → 走真实接口(不挂本 mock) + * - 其它(含未设置) → 这两个接口走本地假数据 + * 提交的 .env.development / .env.production / .env.network / .env.sit 默认 true(真实), + * 仅本机 .env 默认 false(假数据)。 + * + * 实现方式:作为一个临时 axios adapter 挂在请求拦截器上(命中两个 url 时直接返回 mock body, + * 不发真实请求),业务组件 `SentimentAnalyzes.tsx` / `TaskAnalyzes.tsx` 一行不用改。 + * 返回结构与真实接口一致:`{ code, message, data: { records, total } }`。 + */ + +export type MockColumnType = 'unanalyzed' | 'running' | 'completed'; +export type MockDataset = 'task' | 'sentiment'; + +interface MockTaskSeed { + taskName: string; + overview: string; + taskDirection: string; + taskLevel: number; // 1 一般 / 2 紧急 / 3 加急 + taskType: number; // 1 单任务 / 2 场景 / 3 会商 / 4 多智能体 / 5 检索 +} + +/** 列 → 状态码,与组件内 taskStatusMap 对齐。 */ +const COLUMN_STATUS: Record = { + unanalyzed: 0, + running: 20, + completed: 30, +}; + +/** 生成一条完整的 TaskListItem,仅覆盖有意义的字段,其余给安全默认值。 */ +function makeTask( + id: number, + column: MockColumnType, + seed: MockTaskSeed, + createTime: string, + updateTime: string, +): TaskListItem { + return { + createBy: 1001, + createTime, + deptId: null, + id, + isTop: 0, + issuingDept: '战略规划处', + keywords: null, + overview: seed.overview, + planTaskTop: 0, + platform: null, + ptaskId: null, + recommendedBasis: null, + scene: '', + sourceType: 1, + step: null, + taskContent: seed.overview, + taskDirection: seed.taskDirection, + taskEndDate: '2026-06-30 18:00:00', + taskLevel: seed.taskLevel, + taskName: seed.taskName, + taskNo: `T-${String(id).padStart(6, '0')}`, + taskStartDate: '2026-06-01 09:00:00', + taskStatus: COLUMN_STATUS[column], + taskSuperiorFlag: 0, + taskType: seed.taskType, + column, + updateBy: 1001, + updateTime, + endCount: column === 'completed' ? 1 : 0, + sumAll: 1, + progress: column === 'completed' ? 100 : column === 'running' ? 60 : 0, + taskStatusData: null, + } as TaskListItem; +} + +/** 「任务分析」页(业务码 6BF)数据种子。 */ +const TASK_SEEDS: Record = { + unanalyzed: [ + { + taskName: '夏季文旅消费季传播任务分析', + overview: + '围绕"夏季文旅消费季"主题,梳理传播目标、核心受众与潜在策略阻力,输出可执行的任务拆解建议。', + taskDirection: '王磊', + taskLevel: 2, + taskType: 1, + }, + { + taskName: '新能源补贴政策解读专题', + overview: + '针对新一轮新能源汽车补贴政策,分析公众关注焦点与误读风险点,形成政策解读传播要点清单。', + taskDirection: '李婷', + taskLevel: 1, + taskType: 5, + }, + { + taskName: '城市更新片区舆论引导任务', + overview: + '对老城区改造涉及的拆迁、安置等敏感议题进行任务目标拆解,识别需要重点回应的群体诉求。', + taskDirection: '张伟', + taskLevel: 3, + taskType: 2, + }, + { + taskName: '高校毕业季就业服务宣传', + overview: + '梳理面向应届毕业生的就业服务政策传播任务,明确分阶段触达节奏与渠道组合。', + taskDirection: '赵敏', + taskLevel: 1, + taskType: 1, + }, + { + taskName: '防汛应急科普传播任务', + overview: + '汛期来临前,分析防汛减灾科普内容的传播目标与受众分层,制定预警信息发布预案。', + taskDirection: '陈昊', + taskLevel: 2, + taskType: 1, + }, + { + taskName: '乡村振兴成果展示任务', + overview: + '围绕本地乡村振兴典型案例,拆解成果展示传播任务,确定故事化表达与素材采集方向。', + taskDirection: '周琳', + taskLevel: 1, + taskType: 2, + }, + ], + running: [ + { + taskName: '社区养老服务满意度专题分析', + overview: + '正在对社区居家养老服务的群众反馈进行任务分析,重点判断受众诉求与服务短板。', + taskDirection: '孙强', + taskLevel: 2, + taskType: 4, + }, + { + taskName: '中小企业纾困政策落地评估', + overview: + '多智能体协同分析纾困政策落地过程中的执行阻力与企业关切,形成阶段性研判。', + taskDirection: '吴静', + taskLevel: 3, + taskType: 4, + }, + { + taskName: '城市轨道交通延误舆情研判', + overview: + '针对近期地铁延误事件,正在拆解舆论传播路径与关键节点,评估引导策略有效性。', + taskDirection: '郑凯', + taskLevel: 2, + taskType: 3, + }, + { + taskName: '食品安全抽检结果发布任务', + overview: + '正在分析食品安全抽检通报的发布口径与受众反应,优化信息透明度与安抚策略。', + taskDirection: '冯雪', + taskLevel: 1, + taskType: 1, + }, + ], + completed: [ + { + taskName: '春节文化活动传播复盘', + overview: + '已完成春节系列文化活动的传播任务分析,沉淀受众画像与高互动内容规律。', + taskDirection: '黄涛', + taskLevel: 1, + taskType: 3, + }, + { + taskName: '医保改革政策宣传任务', + overview: + '已完成医保改革政策的传播目标拆解与受众分层,输出分群体沟通话术。', + taskDirection: '宋佳', + taskLevel: 2, + taskType: 4, + }, + { + taskName: '城市营商环境优化宣传', + overview: + '已完成营商环境优化举措的任务分析,明确企业端与公众端的差异化传播重点。', + taskDirection: '高鹏', + taskLevel: 1, + taskType: 2, + }, + { + taskName: '老旧小区加装电梯议题分析', + overview: + '已完成加装电梯涉及的邻里诉求与阻力识别,形成多方协商沟通建议。', + taskDirection: '何丽', + taskLevel: 2, + taskType: 3, + }, + { + taskName: '生态环保主题宣传周复盘', + overview: + '已完成环保宣传周的传播效果分析,总结渠道贡献度与可复用素材。', + taskDirection: '罗勇', + taskLevel: 1, + taskType: 1, + }, + { + taskName: '青年人才引进政策解读', + overview: + '已完成人才引进政策的受众判断与策略阻力分析,输出落地传播节奏表。', + taskDirection: '谭芳', + taskLevel: 1, + taskType: 5, + }, + ], +}; + +/** 「三情分析」页(业务码 3Q)数据种子。 */ +const SENTIMENT_SEEDS: Record = { + unanalyzed: [ + { + taskName: '暑期旅游热点情绪监测', + overview: + '对暑期热门景区相关讨论进行情绪与倾向分析,识别游客吐槽集中点与正向口碑话题。', + taskDirection: '王磊', + taskLevel: 2, + taskType: 1, + }, + { + taskName: '电价调整公众情绪研判', + overview: + '围绕居民阶梯电价调整,分析公众情绪走向与主要担忧,判断需要重点疏导的群体。', + taskDirection: '李婷', + taskLevel: 3, + taskType: 5, + }, + { + taskName: '校园周边交通安全舆情', + overview: + '对校园周边交通安全话题进行三情分析,梳理家长群体情绪与诉求强度。', + taskDirection: '张伟', + taskLevel: 2, + taskType: 1, + }, + { + taskName: '景区门票预约制讨论分析', + overview: + '分析门票预约制相关讨论的情绪分布,识别便利性与公平性两类核心关切。', + taskDirection: '赵敏', + taskLevel: 1, + taskType: 2, + }, + { + taskName: '夜间经济噪音投诉情绪', + overview: + '对夜市、夜间演出引发的噪音投诉进行情绪分析,区分居民与商户的不同立场。', + taskDirection: '陈昊', + taskLevel: 1, + taskType: 1, + }, + ], + running: [ + { + taskName: '台风预警信息传播情绪追踪', + overview: + '正在分析台风预警发布后的公众情绪变化,评估恐慌情绪与配合度的动态走势。', + taskDirection: '孙强', + taskLevel: 3, + taskType: 4, + }, + { + taskName: '学区划分调整舆论情绪', + overview: + '多智能体协同追踪学区调整话题的情绪热度与对立观点,研判潜在升级风险。', + taskDirection: '吴静', + taskLevel: 2, + taskType: 4, + }, + { + taskName: '网络热点事件情绪态势', + overview: + '正在对近期网络热点事件进行三情分析,识别情绪峰值节点与关键意见领袖。', + taskDirection: '郑凯', + taskLevel: 2, + taskType: 3, + }, + ], + completed: [ + { + taskName: '春运出行体验情绪复盘', + overview: + '已完成春运期间出行体验的情绪与倾向分析,沉淀满意度变化与高频抱怨主题。', + taskDirection: '黄涛', + taskLevel: 1, + taskType: 3, + }, + { + taskName: '惠民政策落地情绪评估', + overview: + '已完成惠民政策落地后的公众情绪评估,输出正向反馈与改进诉求清单。', + taskDirection: '宋佳', + taskLevel: 2, + taskType: 4, + }, + { + taskName: '城市内涝应对情绪分析', + overview: + '已完成强降雨内涝事件的三情分析,总结情绪演化曲线与有效安抚措施。', + taskDirection: '高鹏', + taskLevel: 2, + taskType: 1, + }, + { + taskName: '景区限流措施满意度分析', + overview: + '已完成节假日景区限流措施的情绪分析,明确游客接受度与体验改进方向。', + taskDirection: '何丽', + taskLevel: 1, + taskType: 2, + }, + { + taskName: '公共交通票价听证情绪', + overview: + '已完成票价听证会相关讨论的情绪与立场分析,输出分群体沟通建议。', + taskDirection: '罗勇', + taskLevel: 1, + taskType: 5, + }, + ], +}; + +const SEEDS: Record> = { + task: TASK_SEEDS, + sentiment: SENTIMENT_SEEDS, +}; + +/** 给同一列里的卡片生成错落有致的时间,便于「按时间倒序」展示。 */ +function buildTimes(index: number): { createTime: string; updateTime: string } { + const day = String(28 - index).padStart(2, '0'); + const hour = String(9 + (index % 8)).padStart(2, '0'); + return { + createTime: `2026-05-${day} ${hour}:15:00`, + updateTime: `2026-06-0${(index % 9) + 1} ${hour}:40:00`, + }; +} + +/** 数据集 → 列 → 完整任务列表(按 id 区分,避免两页 key 冲突)。 */ +function buildPool(dataset: MockDataset, column: MockColumnType): TaskListItem[] { + const base = dataset === 'task' ? 10000 : 20000; + const columnOffset = column === 'unanalyzed' ? 0 : column === 'running' ? 300 : 600; + return SEEDS[dataset][column].map((seed, index) => { + const { createTime, updateTime } = buildTimes(index); + return makeTask(base + columnOffset + index, column, seed, createTime, updateTime); + }); +} + +interface MockCopTasksPayload { + content?: string; + pageNum?: number; + pageSize?: number; + taskStatus?: number[]; +} + +/** 根据请求里的 taskStatus 数组反推当前查询的是哪一列。 */ +function resolveColumn(taskStatus?: number[]): MockColumnType { + const list = Array.isArray(taskStatus) ? taskStatus : []; + if (list.includes(20)) return 'running'; + if (list.includes(0) || list.includes(10)) return 'unanalyzed'; + return 'completed'; +} + +/** + * 模拟 `/task/cop-tasks` / `/three/cop-three-tasks` 接口的 body: + * 按列返回分页后的假数据,支持按 `content` 对任务名/摘要做模糊过滤。 + */ +function buildCopTasksBody(dataset: MockDataset, payload: MockCopTasksPayload) { + const { content = '', pageNum = 1, pageSize = 10, taskStatus } = payload; + const column = resolveColumn(taskStatus); + + let pool = buildPool(dataset, column); + const keyword = content.trim(); + if (keyword) { + pool = pool.filter( + (item) => + item.taskName.includes(keyword) || item.overview.includes(keyword), + ); + } + + const total = pool.length; + const start = (pageNum - 1) * pageSize; + const records = pool.slice(start, start + pageSize); + + return { code: 0, message: 'ok', data: { records, total } }; +} + +/** 命中本 mock 的接口路径 → 对应数据集。 */ +const MOCK_ROUTES: Array<{ match: string; dataset: MockDataset }> = [ + { match: '/three/cop-three-tasks', dataset: 'sentiment' }, + { match: '/task/cop-tasks', dataset: 'task' }, +]; + +/** 判断某个请求 url 是否应走假数据。 */ +export function matchTaskBoardMock(url?: string): MockDataset | null { + if (!url) return null; + const hit = MOCK_ROUTES.find((route) => url.includes(route.match)); + return hit ? hit.dataset : null; +} + +/** + * 临时假数据 axios adapter:命中任务看板接口时直接返回 mock body,不发真实请求。 + * 仅在 `VITE_USE_REAL_DATA !== 'true'` 时由 request.ts 挂上(见那里的请求拦截器)。 + */ +export function taskBoardMockAdapter(config: { + url?: string; + data?: unknown; +}): Promise<{ data: unknown; status: number; statusText: string; headers: Record; config: unknown }> { + const dataset = matchTaskBoardMock(config.url) ?? 'task'; + + let payload: MockCopTasksPayload = {}; + try { + payload = + typeof config.data === 'string' + ? JSON.parse(config.data) + : ((config.data as MockCopTasksPayload) ?? {}); + } catch { + payload = {}; + } + + const body = buildCopTasksBody(dataset, payload); + + return new Promise((resolve) => { + // 模拟网络延迟,让 Loading 态可见。 + setTimeout(() => { + resolve({ + data: body, + status: 200, + statusText: 'OK', + headers: {}, + config, + }); + }, 280); + }); +} diff --git a/frontend-web/src/utils/request.ts b/frontend-web/src/utils/request.ts index 17c6a139..6666aa95 100644 --- a/frontend-web/src/utils/request.ts +++ b/frontend-web/src/utils/request.ts @@ -1,4 +1,12 @@ import axios from 'axios' +import { matchTaskBoardMock, taskBoardMockAdapter } from '@/strategy-components/components/task-components/task-mock-data' + +// 任务看板(任务分析 / 三情分析)数据源开关: +// VITE_USE_REAL_DATA=true → 走真实接口;其它(含未设置)→ 这两个接口走本地假数据。 +// 提交的 .env.development/.env.production/.env.network/.env.sit 默认 true(真实数据), +// 仅本机 .env 默认 false(假数据)。改 env 后需重启 dev server 生效。 +const USE_REAL_DATA = import.meta.env.VITE_USE_REAL_DATA === 'true' + // axios.interceptors const service = axios.create({ // baseURL: '', // url = base url + request url @@ -31,6 +39,10 @@ service.interceptors.response.use( ) service.interceptors.request.use( config => { + // 假数据模式:任务看板两个接口直接走本地 mock,不发真实请求(VITE_USE_REAL_DATA=true 时跳过)。 + if (!USE_REAL_DATA && matchTaskBoardMock(config.url)) { + config.adapter = taskBoardMockAdapter as any; + } // let token: any = localStorage.getItem('token'); config.headers['Authorization'] = `Bearer admin`; // if (token) {