feat(UI): 四 Tab 头部统一 + 清单模板分类 + 宏观 tab 顺序与溢出提示
头部统一 - 新增 PageHeader 组件: 高度 23px (原 NavBar 45px 的一半), 标题 12px 主色加粗, 背景取页面底色 - 打卡 / 宏观 / 监控 三页各自引用, 互不影响; 宏观与监控补齐「宏观」「监控」标题 - 「我」页移除 NavBar 空白条, 蓝色头像区直接贴顶 (不引用 PageHeader, 与其余三页解耦) - 清理失效样式 .me-navbar 清单模板分类 - 分段由 2 项扩为 3 项: 进行中 / 已归档 / 模板; 右上角按钮随分段切换 - 模板独立增删改查: 新增 GET /templates/:id、PUT /templates/:id; 预置模板拒绝改删 - 模板按名称字母顺序排序 (Intl.Collator zh-Hans-CN, 中文按拼音/数字按数值), 前后端同口径 - 新建清单去掉场景选择器, 仅保留名称; scene 改为沿用模板自带场景 - 模板改为从右侧滑出 78vw 面板选择, 首项「不使用模板」 宏观 tab - 顺序改为 报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章, 默认选中新闻 - /macro 由路由直接重定向到 /macro/news, activeFromPath 兜底同样返回 news - SwipeTabs 增加右侧溢出提示: 渐变遮罩 + 主色圆底箭头, 可点击向右翻页, 滚到末尾自动消失 - 切换 tab 时自动把激活项滚到可视区中央
This commit is contained in:
@@ -73,25 +73,40 @@ function withProgress(list) {
|
||||
|
||||
// ---------- 模板 ----------
|
||||
|
||||
// GET /api/checklists/templates
|
||||
// 模板按名称字母顺序 (中文按拼音) 排序, 前后端共用同一比较器语义
|
||||
const NAME_COLLATOR = new Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' })
|
||||
const byName = (a, b) => NAME_COLLATOR.compare(String(a.name || ''), String(b.name || ''))
|
||||
|
||||
// GET /api/checklists/templates 模板列表 (含系统预置, 按字母顺序)
|
||||
router.get('/templates', async (_req, res, next) => {
|
||||
try {
|
||||
await ensureBuiltin()
|
||||
const rows = await prisma.checkListTemplate.findMany({
|
||||
where: { OR: [{ builtin: true }, { userId: _req.user.sub }] },
|
||||
orderBy: [{ builtin: 'desc' }, { id: 'asc' }],
|
||||
})
|
||||
res.json({ items: rows })
|
||||
res.json({ items: rows.sort(byName) })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// POST /api/checklists/templates 另存为模板 { name, scene, items: [{title, note}] }
|
||||
// GET /api/checklists/templates/:id 模板详情
|
||||
router.get('/templates/:id', async (req, res, next) => {
|
||||
try {
|
||||
const row = await prisma.checkListTemplate.findFirst({
|
||||
where: { id: Number(req.params.id), OR: [{ builtin: true }, { userId: req.user.sub }] },
|
||||
})
|
||||
if (!row) return res.status(404).json({ error: '模板不存在' })
|
||||
res.json({ item: row })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// POST /api/checklists/templates 另存为模板 { name, scene?, items: [{title, note}] }
|
||||
router.post('/templates', async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.sub
|
||||
const { name, scene = '', items = [] } = req.body || {}
|
||||
const cleanName = String(name || '').trim()
|
||||
if (!cleanName) return res.status(400).json({ error: '模板名称不能为空' })
|
||||
if (cleanName.length > 20) return res.status(400).json({ error: '模板名称不能超过 20 个字' })
|
||||
const list = (Array.isArray(items) ? items : [])
|
||||
.map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') }))
|
||||
.filter((i) => i.title)
|
||||
@@ -103,12 +118,40 @@ router.post('/templates', async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// DELETE /api/checklists/templates/:id
|
||||
// PUT /api/checklists/templates/:id 改名称 / 检查项 (系统预置模板不可改)
|
||||
router.put('/templates/:id', async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.sub
|
||||
const id = Number(req.params.id)
|
||||
const exist = await prisma.checkListTemplate.findFirst({ where: { id, userId, builtin: false } })
|
||||
if (!exist) return res.status(404).json({ error: '模板不存在、无权限修改或是系统预置模板' })
|
||||
const { name, scene, items } = req.body || {}
|
||||
const data = {}
|
||||
if (name !== undefined) {
|
||||
const n = String(name).trim()
|
||||
if (!n) return res.status(400).json({ error: '模板名称不能为空' })
|
||||
if (n.length > 20) return res.status(400).json({ error: '模板名称不能超过 20 个字' })
|
||||
data.name = n
|
||||
}
|
||||
if (scene !== undefined) data.scene = String(scene)
|
||||
if (items !== undefined) {
|
||||
const list = (Array.isArray(items) ? items : [])
|
||||
.map((i) => ({ title: String(i?.title || '').trim(), note: String(i?.note || '') }))
|
||||
.filter((i) => i.title)
|
||||
if (!list.length) return res.status(400).json({ error: '模板至少需要一个检查项' })
|
||||
data.items = list
|
||||
}
|
||||
const row = await prisma.checkListTemplate.update({ where: { id }, data })
|
||||
res.json({ item: row })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// DELETE /api/checklists/templates/:id 仅自建模板可删
|
||||
router.delete('/templates/:id', async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.sub
|
||||
const id = Number(req.params.id)
|
||||
const exist = await prisma.checkListTemplate.findFirst({ where: { id, userId } })
|
||||
const exist = await prisma.checkListTemplate.findFirst({ where: { id, userId, builtin: false } })
|
||||
if (!exist) return res.status(404).json({ error: '模板不存在或无权删除' })
|
||||
await prisma.checkListTemplate.delete({ where: { id } })
|
||||
res.json({ ok: true })
|
||||
|
||||
Reference in New Issue
Block a user