feat(bigscreen): 任务展示页 + 暂时关闭语音对话页 + 导航/文档同步
- App.jsx 移除 /voice、新增 /tasks→TaskDisplay。 - pageNav PAGE_ORDER 移除 voice、加 tasks(方向键/页眉/MQTT navigate_rel 自动跟随)。 - useMqttControl:PAGE_ALIAS 加 tasks;移除 voice_*、改 task_refresh。 - PageHeader STATUS_BY_PATH 加 /tasks。 - 新增 TaskDisplay.jsx + task-display.css:轮播卡(分类/状态/标题/摘要/标签/预算/任务ID+QRCodeSVG 扫码接单)、useTasks 轮询 /park/api/tasks、接受 MQTT task_refresh。 - 新增 utils/taskData.js(useTasks, tenantFetch 带租户身份)。 - docs/mqtt-commands.md:navigate 列表/语音章节标注语音页关闭、tasks 页指令。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/* 任务展示页数据:轮询 /park/api/tasks(系统任务中心上墙任务),带租户身份。
|
||||
参考 parkData.js 的 useParkSim 轮询 + 归一化防崩模式。 */
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { tenantFetch } from './tenant';
|
||||
|
||||
const POLL_MS = 8_000;
|
||||
|
||||
function normalizeTask(raw, idx) {
|
||||
const t = raw || {};
|
||||
return {
|
||||
id: t.id || '',
|
||||
task_code: t.task_code || '',
|
||||
scan_payload: t.scan_payload || t.task_code || '',
|
||||
title: t.title || '未命名任务',
|
||||
category: t.category || '综合',
|
||||
summary: t.summary || '',
|
||||
tags: Array.isArray(t.tags) ? t.tags : [],
|
||||
budgetMin: Number(t.budget_min) || 0,
|
||||
budgetMax: Number(t.budget_max) || 0,
|
||||
budgetLabel: fmtBudget(t.budget_min, t.budget_max),
|
||||
status: t.status || 'published',
|
||||
claimed: t.status === 'claimed' || t.status === 'doing' || !!t.claimed_by,
|
||||
claimedBy: t.claimed_by || '',
|
||||
claimedAt: t.claimed_at || '',
|
||||
key: t.id || `task-${idx}`,
|
||||
};
|
||||
}
|
||||
|
||||
function fmtBudget(min, max) {
|
||||
const n = Number(min) || 0, x = Number(max) || 0;
|
||||
if (!n && !x) return '面议';
|
||||
if (!x) return `¥${n}`;
|
||||
if (n === x) return `¥${n}`;
|
||||
return `¥${n}~¥${x}`;
|
||||
}
|
||||
|
||||
export function useTasks() {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const aliveRef = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
aliveRef.current = true;
|
||||
setLoading(true);
|
||||
const load = async () => {
|
||||
try {
|
||||
const res = await tenantFetch('/api/tasks');
|
||||
if (!res.ok) throw new Error(`任务加载失败(${res.status})`);
|
||||
const body = await res.json();
|
||||
const items = Array.isArray(body?.items) ? body.items : [];
|
||||
if (aliveRef.current) {
|
||||
setTasks(items.map(normalizeTask));
|
||||
setError('');
|
||||
}
|
||||
} catch (e) {
|
||||
if (aliveRef.current) setError(e.message || '任务加载失败');
|
||||
} finally {
|
||||
if (aliveRef.current) setLoading(false);
|
||||
}
|
||||
};
|
||||
load();
|
||||
const timer = setInterval(load, POLL_MS);
|
||||
return () => { aliveRef.current = false; clearInterval(timer); };
|
||||
}, []);
|
||||
|
||||
return { tasks, loading, error };
|
||||
}
|
||||
Reference in New Issue
Block a user