diff --git a/jsconfig.json b/jsconfig.json
index f265a7f27..9a4ca659c 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -12,6 +12,7 @@
"allowJs": true,
"checkJs": false,
"jsx": "preserve",
+ "ignoreDeprecations": "6.0",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
diff --git a/language/original-web.txt b/language/original-web.txt
index de419698e..7476e8e67 100644
--- a/language/original-web.txt
+++ b/language/original-web.txt
@@ -2534,3 +2534,58 @@ AI任务分析
服务器返回错误(HTTP (*))
你撤回了一条消息
重新编辑
+四象限
+个人视角
+部门任务总览
+(*) 人
+项
+最久 (*) 天
+今天 (*) 项到期
+还有 (*) 项
+待完成
+我协助的
+(*)月(*)日
+有 (*) 项已超期,建议先处理
+有 (*) 项今日到期
+今日无到期任务
+团队有 (*) 项已超期,涉及 (*) 人
+团队有 (*) 项已超期
+团队本周已完成 (*) 项
+查看任务
+涉及 (*) 人
+3 天内到期
+本周完成
+较上周
+与上周持平
+成员任务分配
+(*) 项
+未分配
+查看全部 (*) 人
+优先级分布
+未完成 (*) 项
+点击任一档位可下钻到任务列表
+高优先级
+重点关注任务
+未分配负责人
+待分配
+全部显示完毕
+逾期 (*) 天
+逾期 (*) 小时
+夜深了,{username}
+早上好,{username}
+中午好,{username}
+下午好,{username}
+晚上好,{username}
+演示数据,接口对接后可打开任务详情
+服务器时间
+暂无已超期任务
+暂无今日到期任务
+暂无待完成任务
+暂无协助的任务
+没有超期任务,保持住
+今天只有这一项,处理完就轻松了
+数量较多,仅显示最近 (*) 项,展开其余 (*) 项
+太棒了,当前没有需要处理的任务
+我的部门
+待开始
+暂无待开始任务
diff --git a/resources/assets/js/pages/manage/components/DashboardTeam.vue b/resources/assets/js/pages/manage/components/DashboardTeam.vue
new file mode 100644
index 000000000..6f42d88fa
--- /dev/null
+++ b/resources/assets/js/pages/manage/components/DashboardTeam.vue
@@ -0,0 +1,379 @@
+
+
+
+
+ -
+
{{$L('未完成')}}
+
+ {{blocks.uncompleted || 0}}
+ {{$L('项')}}
+
+ {{$L('查看任务')}} →
+
+ -
+
{{$L('已超期')}}
+
+ {{blocks.overdue || 0}}
+ {{$L('项')}} · {{$L('涉及 (*) 人', blocks.overdue_owner_count)}}
+
+ {{$L('查看任务')}} →
+
+ -
+
{{$L('3 天内到期')}}
+
+ {{blocks.due_soon || 0}}
+ {{$L('项')}}
+
+ {{$L('查看任务')}} →
+
+ -
+
{{$L('本周完成')}}
+
+ {{blocks.week_completed || 0}}
+ {{$L('项')}} · {{weekDiffText}}
+
+
+
+
+
+
+
+ {{$L('成员任务分配')}}
+
+ {{$L('进行中')}}
+ {{$L('待处理')}}
+ {{$L('待测试')}}
+ {{$L('已超期')}}
+
+
+
+ -
+ {{member.nickname.substring(0, 1)}}
+ {{member.nickname}}
+
+
+
+
+
+
+
+
+
+ {{member.overdue}} {{$L('超期')}} · {{$L('(*) 项', member.total)}}
+
+
+ -
+ ?
+ {{$L('未分配')}}
+
+
+
+ {{$L('(*) 项', blocks.no_owner)}}
+
+
+
+ {{memberExpand ? $L('收起') : $L('查看全部 (*) 人', members.length) + ' →'}}
+
+
+
+
+ {{$L('优先级分布')}}
+ {{$L('未完成 (*) 项', blocks.uncompleted || 0)}}
+
+
+
+
+ {{item.name}}
+ {{$L('(*) 项', item.num)}} · {{item.percent}}%
+
+
+
+
+
+
{{$L('点击任一档位可下钻到任务列表')}}
+
+
+
+
+
+
+ {{$L('重点关注任务')}}
+ {{chip.label}} {{chip.num}}
+
+
+
+
+ {{$L('任务')}}
+ {{$L('项目')}}
+ {{$L('负责人')}}
+ {{$L('优先级')}}
+ {{$L('状态')}}
+ {{$L('截止时间')}}
+
+
+ {{item.name}}
+ {{item.project_name}}
+
+
+ {{item.owner.nickname.substring(0, 1)}}
+ {{item.owner.nickname}}
+
+
+ ?{{$L('待分配')}}
+
+
+
+
+
+ {{item.p_name}}
+
+ —
+
+ {{item.flow_item_name || $L('未完成')}}
+ {{deadlineText(item.end_at) || '—'}}
+
+
+
{{$L('暂无任务')}}
+
{{$L('加载更多')}}({{$L('还有 (*) 项', currentList.total - currentList.list.length)}})
+
{{$L('全部显示完毕')}}
+
+
+
+
+
+
diff --git a/resources/assets/js/pages/manage/components/dashboard-team-mock.js b/resources/assets/js/pages/manage/components/dashboard-team-mock.js
new file mode 100644
index 000000000..83097cde9
--- /dev/null
+++ b/resources/assets/js/pages/manage/components/dashboard-team-mock.js
@@ -0,0 +1,242 @@
+/**
+ * 团队概览 Mock 数据(仅接口对接前使用,确认交互后整体删除本文件)
+ *
+ * 结构与规划中的接口返回保持一致,便于无缝替换:
+ * - fetchTeamStats() → api/dashboard/team/stats
+ * - fetchTeamTasks() → api/dashboard/team/tasks?type=xxx&page=N(20条/页)
+ *
+ * 口径说明(与后端实现约定一致):
+ * - members[].segments 按任务表 flow_item_name 的状态前缀(start/progress/test)聚合,
+ * 已超期的任务不论状态归入 overdue 段
+ * - priority 按系统优先级设置动态分档,未设置优先级的任务归入「未设置」
+ */
+
+const PAGE_SIZE = 20;
+
+// 成员池(nickname/color 用于 Mock 字母头像,真实接口返回 userid 后换 UserAvatar)
+const MEMBERS = [
+ {userid: 0, nickname: "林晓雨", color: "#3a7bd5", total: 14, overdue: 2, segments: {progress: 7, start: 4, test: 1}},
+ {userid: 0, nickname: "王志强", color: "#8a63d2", total: 13, overdue: 3, segments: {progress: 5, start: 4, test: 1}},
+ {userid: 0, nickname: "陈嘉明", color: "#1e9e55", total: 11, overdue: 3, segments: {progress: 4, start: 3, test: 1}},
+ {userid: 0, nickname: "赵倩", color: "#d9822b", total: 9, overdue: 0, segments: {progress: 5, start: 4, test: 0}},
+ {userid: 0, nickname: "孙浩然", color: "#5f9ea0", total: 7, overdue: 2, segments: {progress: 3, start: 2, test: 0}},
+ {userid: 0, nickname: "李雪", color: "#c2554f", total: 6, overdue: 1, segments: {progress: 2, start: 3, test: 0}},
+ {userid: 0, nickname: "周明", color: "#4f7a5b", total: 5, overdue: 0, segments: {progress: 2, start: 3, test: 0}},
+ {userid: 0, nickname: "吴婷", color: "#9a6ec2", total: 4, overdue: 1, segments: {progress: 1, start: 2, test: 0}},
+ {userid: 0, nickname: "郑凯", color: "#3f8fa8", total: 3, overdue: 0, segments: {progress: 1, start: 2, test: 0}},
+ {userid: 0, nickname: "冯丽", color: "#b8863d", total: 3, overdue: 0, segments: {progress: 2, start: 1, test: 0}},
+ {userid: 0, nickname: "韩磊", color: "#6b7ac2", total: 2, overdue: 0, segments: {progress: 1, start: 1, test: 0}},
+ {userid: 0, nickname: "杨帆", color: "#52a06e", total: 1, overdue: 0, segments: {progress: 0, start: 1, test: 0}},
+];
+
+const PROJECTS = ["DooTask v2", "点餐系统", "TTPOS-MENU", "财务管理", "项目收支"];
+
+const PRIORITIES = [
+ {level: 1, name: "紧急", color: "#d94f46"},
+ {level: 2, name: "高", color: "#d9822b"},
+ {level: 3, name: "中", color: "#3a7bd5"},
+ {level: 4, name: "低", color: "#9aa096"},
+];
+
+const FLOWS = [
+ {status: "start", name: "待处理"},
+ {status: "progress", name: "进行中"},
+ {status: "test", name: "审核中"},
+];
+
+const TASK_NAMES = [
+ "建议放开部门群组的自定义头像权限", "【收款】佳博 WiFi 打印机联调", "点餐系统会员余额对账异常排查",
+ "电子菜单需开通 Stripe 账户的申请", "公开分享链接的选项没有做初始化记录", "建议增加任务外链访问功能",
+ "客户端登录页视觉走查", "周报数据汇总脚本修复", "移动端消息推送延迟优化方案评估",
+ "建议将任务模板和任务描述的内容编辑框保持一致", "仪表盘统计口径梳理与确认", "客户反馈工单归类规则更新",
+ "灰度发布检查清单补充", "新版工作流状态配置文档评审", "财务管理报表导出字段确认",
+ "点餐系统打印模板多语言适配", "API 文档站点信息架构调整", "财务管理季度结算流程自动化",
+ "项目收支月度盘点模板整理", "移动端首页白屏问题排查", "消息中心已读状态同步异常",
+ "新员工入职引导流程梳理", "数据备份策略评审与演练", "第三方登录接口升级适配",
+ "文件预览服务内存占用优化", "会议室预定模块需求评审", "客户端自动更新失败重试机制",
+ "通知推送模板文案统一", "任务导出 Excel 字段缺失修复", "项目归档流程权限确认",
+ "服务端日志切割脚本调整", "看板拖拽卡顿性能分析", "多语言翻译缺失项补齐",
+ "接口限流阈值评估与调整", "运营后台数据大盘需求确认", "旧版附件迁移方案评审",
+ "扫码登录二维码过期时间调整", "子任务批量操作交互优化", "网盘分享链接权限梳理",
+ "审批流转节点通知缺失排查", "版本发布公告文案准备", "工时统计报表口径确认",
+];
+
+// —— 以下为确定性生成逻辑(不用随机数,保证每次渲染一致)——
+// 时间一律基于 $A.daytz()(服务器时区),与真实接口返回口径一致
+
+function fmt(day) {
+ return day.format("YYYY-MM-DD HH:mm:00");
+}
+
+function dayOffset(days, hour) {
+ return $A.daytz().add(days, "day").hour(hour).minute(0).second(0);
+}
+
+/**
+ * 生成未完成任务池:84 条 = 成员 78 条 + 未分配 6 条
+ * 超期 12、3 日内到期 18、紧急/高 29、未分配 6,与 stats 数字咬合
+ */
+function buildPool() {
+ const pool = [];
+ const owners = [];
+ MEMBERS.forEach(member => {
+ for (let i = 0; i < member.total; i++) {
+ owners.push({member, isOverdue: i < member.overdue});
+ }
+ });
+ for (let i = 0; i < 6; i++) {
+ owners.push({member: null, isOverdue: false});
+ }
+ // 优先级配额:紧急 8、高 21、中 31、低 16、未设置 8(合计 84,紧急+高=29)
+ // 用互质数打散分配顺序,避免同一成员的任务优先级扎堆
+ const priorityRank = owners.map((_, i) => i).sort((a, b) => ((a * 53) % owners.length) - ((b * 53) % owners.length));
+ const priorityMap = {};
+ priorityRank.forEach((ownerIndex, rank) => {
+ if (rank < 8) {
+ priorityMap[ownerIndex] = PRIORITIES[0];
+ } else if (rank < 29) {
+ priorityMap[ownerIndex] = PRIORITIES[1];
+ } else if (rank < 60) {
+ priorityMap[ownerIndex] = PRIORITIES[2];
+ } else if (rank < 76) {
+ priorityMap[ownerIndex] = PRIORITIES[3];
+ } else {
+ priorityMap[ownerIndex] = null;
+ }
+ });
+ // 非超期任务里:18 条 3 日内到期(时间保证在未来)、30 条较远截止、其余无截止
+ const nonOverdue = owners.map((item, i) => item.isOverdue ? -1 : i).filter(i => i >= 0);
+ const soonRank = [...nonOverdue].sort((a, b) => ((a * 37) % owners.length) - ((b * 37) % owners.length));
+ const soonSet = new Set(soonRank.slice(0, 18));
+ const farSet = new Set(soonRank.slice(18, 48));
+ owners.forEach((item, index) => {
+ const priority = priorityMap[index];
+ //
+ let endAt = null;
+ if (item.isOverdue) {
+ endAt = dayOffset(-((index % 6) + 1), 10 + index % 8);
+ } else if (soonSet.has(index)) {
+ // 现在起 2 小时 ~ 2.5 天内,保证不会因当天时间已过而变成逾期
+ endAt = $A.daytz().add(2 + (index % 3) * 20, "hour").minute(0).second(0);
+ } else if (farSet.has(index)) {
+ endAt = dayOffset(4 + index % 20, 9 + index % 9);
+ }
+ const flow = FLOWS[index % FLOWS.length];
+ const name = TASK_NAMES[index % TASK_NAMES.length] + (index >= TASK_NAMES.length * 2 ? "(二期)" : index >= TASK_NAMES.length ? "(跟进)" : "");
+ pool.push({
+ id: 900000 + index,
+ name,
+ project_name: PROJECTS[index % PROJECTS.length],
+ owner: item.member ? {userid: item.member.userid, nickname: item.member.nickname, color: item.member.color} : null,
+ p_level: priority ? priority.level : 0,
+ p_name: priority ? priority.name : "",
+ p_color: priority ? priority.color : "",
+ flow_item_status: flow.status,
+ flow_item_name: flow.name,
+ end_at: endAt ? fmt(endAt) : "",
+ });
+ });
+ return pool;
+}
+
+let POOL = null;
+
+function getPool() {
+ if (POOL === null) {
+ POOL = buildPool();
+ }
+ return POOL;
+}
+
+function isOverdue(task) {
+ return task.end_at && $A.dayjs(task.end_at) <= $A.daytz();
+}
+
+function isSoon(task) {
+ if (!task.end_at || isOverdue(task)) {
+ return false;
+ }
+ return $A.dayjs(task.end_at) <= $A.daytz().add(3, "day").endOf("day");
+}
+
+function filterPool({type, member, level}) {
+ let list = getPool();
+ if (member) {
+ list = list.filter(task => task.owner && task.owner.nickname === member);
+ } else if (level > 0) {
+ list = list.filter(task => task.p_level === level);
+ } else if (level === -1) {
+ list = list.filter(task => task.p_level === 0);
+ } else {
+ switch (type) {
+ case "overdue":
+ list = list.filter(task => isOverdue(task));
+ break;
+ case "soon":
+ list = list.filter(task => isSoon(task));
+ break;
+ case "hi":
+ list = list.filter(task => task.p_level > 0 && task.p_level <= 2);
+ break;
+ case "noowner":
+ list = list.filter(task => !task.owner);
+ break;
+ }
+ }
+ // 排序:超期最久优先,其次截止时间近的优先,无截止最后
+ return list.slice().sort((a, b) => {
+ if (!a.end_at && !b.end_at) return 0;
+ if (!a.end_at) return 1;
+ if (!b.end_at) return -1;
+ return a.end_at.localeCompare(b.end_at);
+ });
+}
+
+/**
+ * 团队统计(对应 api/dashboard/team/stats)
+ */
+export function fetchTeamStats() {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve({
+ member_count: MEMBERS.length,
+ blocks: {
+ uncompleted: 84,
+ overdue: 12,
+ overdue_owner_count: 5,
+ due_soon: 18,
+ week_completed: 27,
+ last_week_completed: 21,
+ no_owner: 6,
+ },
+ priority: [
+ {level: 1, name: "紧急", color: "#d94f46", num: 8},
+ {level: 2, name: "高", color: "#d9822b", num: 21},
+ {level: 3, name: "中", color: "#3a7bd5", num: 31},
+ {level: 4, name: "低", color: "#9aa096", num: 16},
+ {level: -1, name: "", color: "#c5c8ce", num: 8},
+ ],
+ members: MEMBERS,
+ });
+ }, 300);
+ });
+}
+
+/**
+ * 团队任务列表(对应 api/dashboard/team/tasks)
+ * @param {Object} params {type, page, member, level}
+ */
+export function fetchTeamTasks(params) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ const page = Math.max(1, params.page || 1);
+ const list = filterPool(params);
+ resolve({
+ page,
+ total: list.length,
+ list: list.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE),
+ hasMore: page * PAGE_SIZE < list.length,
+ });
+ }, 300);
+ });
+}
diff --git a/resources/assets/js/pages/manage/components/dashboard-time-mixin.js b/resources/assets/js/pages/manage/components/dashboard-time-mixin.js
new file mode 100644
index 000000000..a521a320f
--- /dev/null
+++ b/resources/assets/js/pages/manage/components/dashboard-time-mixin.js
@@ -0,0 +1,80 @@
+/**
+ * 仪表盘截止时间人性化显示(个人视角 / 团队概览共用)
+ */
+export default {
+ data() {
+ return {
+ nowTime: $A.dayjs().unix(),
+ nowInter: null,
+ }
+ },
+
+ methods: {
+ /**
+ * 截止时间状态 class
+ * @param endAt
+ * @returns {string} end-overdue|end-today|''
+ */
+ deadlineClass(endAt) {
+ if (!endAt) {
+ return ''
+ }
+ const end = $A.dayjs(endAt)
+ const now = $A.daytz(this.nowTime)
+ if (end <= now) {
+ return 'end-overdue'
+ }
+ if (end.format('YYYY-MM-DD') === now.format('YYYY-MM-DD')) {
+ return 'end-today'
+ }
+ return ''
+ },
+
+ /**
+ * 截止时间文案(逾期 N 天 / 今天 HH:mm / 明天 HH:mm / MM-DD)
+ * @param endAt
+ * @returns {string}
+ */
+ deadlineText(endAt) {
+ if (!endAt) {
+ return ''
+ }
+ const end = $A.dayjs(endAt)
+ const now = $A.daytz(this.nowTime)
+ if (end <= now) {
+ const days = now.startOf('day').diff(end.startOf('day'), 'day')
+ if (days > 0) {
+ return this.$L('逾期 (*) 天', days)
+ }
+ return this.$L('逾期 (*) 小时', Math.max(now.diff(end, 'hour'), 1))
+ }
+ if (end.format('YYYY-MM-DD') === now.format('YYYY-MM-DD')) {
+ return `${this.$L('今天')} ${end.format('HH:mm')}`
+ }
+ if (end.format('YYYY-MM-DD') === now.clone().add(1, 'day').format('YYYY-MM-DD')) {
+ return `${this.$L('明天')} ${end.format('HH:mm')}`
+ }
+ if (end.year() === now.year()) {
+ return end.format('MM-DD')
+ }
+ return end.format('YYYY-MM-DD')
+ },
+
+ /**
+ * 时间刷新定时器(驱动 deadline 文案更新)
+ * @param load
+ */
+ loadInterval(load) {
+ if (this.nowInter) {
+ clearInterval(this.nowInter)
+ this.nowInter = null
+ }
+ if (load === false) {
+ return
+ }
+ this.nowInter = setInterval(_ => {
+ this.nowTime = $A.dayjs().unix()
+ }, 10 * 1000)
+ },
+ }
+}
diff --git a/resources/assets/js/pages/manage/dashboard.vue b/resources/assets/js/pages/manage/dashboard.vue
index 544b61cec..0dff2175d 100644
--- a/resources/assets/js/pages/manage/dashboard.vue
+++ b/resources/assets/js/pages/manage/dashboard.vue
@@ -4,141 +4,305 @@
{{warningMsg}}
-
-
-
{{dashboardHello}}
-
-
-
{{$L('搜索')}} ({{mateName}}+F)
+
+
+
+
-
-
- {{$L('服务器时间')}}:
- {{$A.daytz().format('YYYY-MM-DD HH:mm:ss')}}
-
-
-
{{$L('以下是你当前的任务统计数据')}}
-
-
-
-
-
- -
-
{{getTitle('today')}}
-
-
{{dashboardTask.today_count}}
-
-
-
- -
-
{{getTitle('overdue')}}
-
-
{{dashboardTask.overdue_count}}
-
-
-
- -
-
{{getTitle('todo')}}
-
-
{{dashboardTask.todo_count}}
-
-
-
-
-
-
-
-
-
- {{column.title}}
-
- ({{column.count}})
-
-
-
-
-
+
+