feat(V0.2): 任务模块界面调整 —— 首屏/项目管理/底部安全距离
Docker Build and Push / build-image (push) Successful in 4m8s

按 2026-10-24 评审的 5 条调整实现 (需求 T-VW-08~10 / T-PJ-11/12 / T-UI-11):

首屏 (T-VW-08/09):
- /task 改为「上=最近项目条(3 个+更多入口), 下=当前项目任务管理」
- 完整项目列表迁至 /task/list, 从「更多项目」进入; /task/:pid 保留供深链

最近项目排序 (T-VW-10):
- 卡片长按 300ms 拖动换位, 结果存设备端 prefs.recentOrder
- 顺序 = 手动排序 > 最近访问(recentIds) > 列表序, 归档项目不展示

项目管理 (T-PJ-11/12):
- 新增 ProjectManage: 成员 / 看板列 / 项目信息 三分区统一入口
- 项目信息含 改名 / 归档切换 / 删除(个人项目隐藏) / 退出(仅创建人)
- 项目页头部只留「项目管理 + 新增」, 移除散置的成员/列设置按钮

底部安全距离 (T-UI-11):
- 任务详情 / 人员选择 / 拷贝弹层 padding-bottom = 安全区 + 20px
- 小屏下弹层 maxHeight + 内部滚动, 底部按钮完整可见

已验证: 97 文件语法 0 错误、相对导入 0 缺失
This commit is contained in:
jif2.zhang
2026-10-24 20:32:44 +08:00
parent 6153b9db99
commit 0cdbfb434f
13 changed files with 771 additions and 179 deletions
+48 -9
View File
@@ -1,19 +1,27 @@
// 任务模块: 设备端偏好 (不落库, 不跨设备)
// lastProjectId 上次访问的项目 (T-VW-07)
// viewMode 按项目记忆的视图 list | board (T-VW-03/06)
// recentIds 自动按最近访问排序的项目 id (最前 = 最近), 上限 10 (T-VW-08)
// recentOrder 首屏最近项目卡片的手动排序结果, 优先于 recentIds (T-VW-10)
const KEY = 'iboard_task_prefs'
function blank() {
return { lastProjectId: null, viewMode: {}, recentIds: [], recentOrder: [] }
}
function read() {
try {
const raw = localStorage.getItem(KEY)
if (!raw) return { lastProjectId: null, viewMode: {} }
if (!raw) return blank()
const obj = JSON.parse(raw)
return {
lastProjectId: obj.lastProjectId ?? null,
viewMode: obj.viewMode && typeof obj.viewMode === 'object' ? obj.viewMode : {},
recentIds: Array.isArray(obj.recentIds) ? obj.recentIds : [],
recentOrder: Array.isArray(obj.recentOrder) ? obj.recentOrder : [],
}
} catch {
return { lastProjectId: null, viewMode: {} }
return blank()
}
}
@@ -46,13 +54,44 @@ export function setViewMode(projectId, mode) {
write(cur)
}
/** 清理已不可见的项目记录 */
export function prunePrefs(visibleIds) {
const set = new Set((visibleIds || []).map(String))
/** 最近访问记录: 把 id 提到队首, 上限 10 (T-VW-08) */
export function getRecentIds() {
return read().recentIds
}
export function setRecentIds(id) {
if (!id) return
const cur = read()
if (cur.lastProjectId && !set.has(String(cur.lastProjectId))) cur.lastProjectId = null
for (const k of Object.keys(cur.viewMode)) {
if (!set.has(k)) delete cur.viewMode[k]
}
cur.recentIds = [id, ...cur.recentIds.filter((x) => x !== id)].slice(0, 10)
write(cur)
}
/** 首屏卡片手动排序 (T-VW-10): 完整顺序, 排在前面的项目优先展示 */
export function getRecentOrder() {
return read().recentOrder
}
export function setRecentOrder(ids) {
const cur = read()
cur.recentOrder = (Array.isArray(ids) ? ids : []).map(Number).filter(Boolean)
write(cur)
}
/** 清理已不可见的项目记录 */
export function prunePrefs(visibleIds) {
const set = new Set((visibleIds || []).map(Number))
const cur = read()
let dirty = false
if (cur.lastProjectId && !set.has(cur.lastProjectId)) { cur.lastProjectId = null; dirty = true }
for (const k of Object.keys(cur.viewMode)) {
if (!set.has(Number(k))) { delete cur.viewMode[k]; dirty = true }
}
const ri = cur.recentIds.filter((id) => set.has(id))
const ro = cur.recentOrder.filter((id) => set.has(id))
if (ri.length !== cur.recentIds.length || ro.length !== cur.recentOrder.length) dirty = true
if (dirty) {
cur.recentIds = ri
cur.recentOrder = ro
write(cur)
}
}