Files
subtrack/internal/web/templates/pages/calendar.html
T
subtrack 79a308b1b5
build / build-and-test (push) Successful in 3m28s
feat(v1.6): 键盘 3×4、格子计数排版、时钟修复、已完成折叠、静态资源破缓存
1) 触摸小键盘
- 改为 3 列 × 4 行,底行 ⌫ / 0 / ✓(数字 1-9 按 123/456/789 排列)
- 自定义按键样式:52px 高、圆角、按下位移反馈,确认键为主题绿,不再使用原生按钮外观

2) 日历格子
- 「待完成 / 总数」改为 34px 粗体、主题绿、靠左、垂直居中;左侧打手色竖条保留
- 仍不显示排单缩略信息

3) 抽屉时钟
- 修复不显示:根因是浏览器缓存了旧的 app.js(静态资源 max-age=300)
- 所有 CSS/JS 链接改为 /static/app.css?v=<版本号>,升级后自动取新文件
- 字号放大到 30px、水平垂直居中、24 小时制精确到秒,日期小字放在下方

4) 已完成排单折叠
- 当日清单里已完成/已跳过的单子折叠成一行:尾号 + 姓名 + 套餐 + 状态
- 点这一行展开,显示剩余、打手、撤销打卡与⋯菜单(加急/追加);未完成的行不变
- 折叠箭头改为 CSS 三角,展开/收起有过渡动画

5) 背景
- 管理端背景横线彻底移除(已用浏览器截图确认),大屏网格背景保留

验证:
- 无头 Chrome 实拍暗色/亮色两套日历页(shots/v16-dark.png、shots/v16-light.png)
- 界面断言 14 项全部通过;原有 119 项验收回归全绿;go vet / go test ./... 通过
- 版本号 1.6.0
2026-10-19 02:33:37 +08:00

268 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
<div class="cal-page">
<header class="cal-head">
<div class="cal-title">
<a class="btn btn-ghost btn-square" href="/calendar?y={{.D.PrevY}}&m={{.D.PrevM}}&pkg={{.D.PkgID}}" aria-label="上个月">‹</a>
<h1>{{.D.Title}}</h1>
<a class="btn btn-ghost btn-square" href="/calendar?y={{.D.NextY}}&m={{.D.NextM}}&pkg={{.D.PkgID}}" aria-label="下个月">›</a>
<a class="btn btn-sm" href="/calendar?pkg={{.D.PkgID}}">回到今天</a>
</div>
<div class="cal-stats">
<span><em>{{.D.MonthDue}}</em>本月到期</span>
<span><em>{{.D.MonthTasks}}</em>本月待打</span>
<span><em>{{.D.MonthDone}}</em>已完成</span>
{{if gt .D.MonthUrgent 0}}<span class="text-urgent"><em>{{.D.MonthUrgent}}</em>加急</span>{{end}}
<a class="btn btn-primary btn-sm" href="/customers/new">+ 新建客户</a>
</div>
</header>
{{if .D.HasTabs}}
<nav class="pkg-tabs">
{{range .D.Tabs}}
<a class="{{if eq .ID $.D.PkgID}}on{{end}}" style="--tab:{{.Color}}"
href="/calendar?y={{$.D.Year}}&m={{$.D.Month}}&d={{$.D.Selected}}&pkg={{.ID}}">
<i></i>{{.Name}}{{if gt .Count 0}}<em>{{.Count}}</em>{{end}}
</a>
{{end}}
</nav>
{{end}}
{{if .D.NoPackages}}
<div class="notice">还没有服务套餐,先去 <a href="/packages/new">新建套餐</a>,再给客户开通。</div>
{{else if .D.NoCustomers}}
<div class="notice">还没有客户,<a href="/customers/new">新建第一位客户</a>。</div>
{{end}}
<div class="cal-body">
<div class="cal-grid" role="grid">
<div class="cal-dow" role="row">
{{range .D.Weekdays}}<span>{{.}}</span>{{end}}
</div>
{{range .D.Weeks}}
<div class="cal-week" role="row">
{{range .}}
<a class="cal-cell heat-{{.Heat}}{{if not .InMonth}} out{{end}}{{if .IsToday}} today{{end}}{{if .IsSelected}} sel{{end}}{{if .IsWeekend}} weekend{{end}}{{if .DueCount}} has-due{{end}}"
href="/calendar?y={{$.D.Year}}&m={{$.D.Month}}&d={{.Date}}&pkg={{$.D.PkgID}}" role="gridcell">
{{if .Strip}}<span class="asg-strip" aria-hidden="true">{{range .Strip}}<i style="background:{{.}}"></i>{{end}}</span>{{end}}
<div class="cell-top">
<span class="dnum">{{.Day}}</span>
<span class="cell-badges">
{{if gt .UrgentCnt 0}}<span class="urgent-dot" title="{{.UrgentCnt}} 项加急"></span>{{end}}
{{if .DueCount}}<span class="due-badge"><em>到期</em>{{.DueCount}}</span>{{end}}
</span>
</div>
{{if .TaskCount}}
<div class="cell-count">
<b>{{.PendingCount}}</b><span>/ {{.TaskCount}}</span>
</div>
{{end}}
</a>
{{end}}
</div>
{{end}}
</div>
<aside class="drawer">
<div class="drawer-clock">
<b data-clock>--:--:--</b>
<span>{{.D.TodayStr}}</span>
</div>
<header class="drawer-head">
<h2>{{.D.SelectedLabel}}</h2>
<div class="drawer-tags">
{{if .D.SelectedIsToday}}<span class="chip chip-active">今天</span>{{end}}
{{if .D.SelectedIsPast}}<span class="chip chip-closed">已过去</span>{{end}}
<span class="chip chip-plain">{{.D.TaskPending}} 待完成</span>
</div>
</header>
<section class="drawer-block">
<h3>当日待完成 <em>{{.D.TaskPending}}</em><span class="muted small">/ {{len .D.Tasks}} 单</span></h3>
<form class="quick-form" method="post" action="/checkins/quick">
<input type="hidden" name="date" value="{{.D.Selected}}">
<input type="hidden" name="pkg" value="{{.D.PkgID}}">
<input type="hidden" name="back" value="{{.D.Back}}">
<input type="text" name="tail" placeholder="输入手机尾号,回车即可完成" maxlength="8" autocomplete="off" inputmode="numeric" aria-label="手机尾号快速完成">
<button class="btn btn-sm btn-primary" type="submit">完成</button>
</form>
<p class="muted small quick-hint">唯一匹配直接完成;同尾号多人 / 一单多服务会列出让你勾选。</p>
<div class="keypad" data-keypad role="group" aria-label="数字小键盘">
<button type="button" class="key" data-key="1">1</button>
<button type="button" class="key" data-key="2">2</button>
<button type="button" class="key" data-key="3">3</button>
<button type="button" class="key" data-key="4">4</button>
<button type="button" class="key" data-key="5">5</button>
<button type="button" class="key" data-key="6">6</button>
<button type="button" class="key" data-key="7">7</button>
<button type="button" class="key" data-key="8">8</button>
<button type="button" class="key" data-key="9">9</button>
<button type="button" class="key key-fn" data-key="back" aria-label="退格">⌫</button>
<button type="button" class="key" data-key="0">0</button>
<button type="button" class="key key-ok" data-key="ok" aria-label="确认完成">✓</button>
</div>
{{if .D.PickTail}}
<section class="picker">
<header class="picker-head">
<h4>尾号 {{.D.PickTail}} 的待完成项</h4>
<a class="btn btn-sm btn-ghost" href="{{.D.Back}}">收起</a>
</header>
{{if .D.PickRows}}
<form method="post" action="/checkins/batch">
<input type="hidden" name="date" value="{{.D.Selected}}">
<input type="hidden" name="back" value="{{.D.Back}}">
<ul class="pick-list">
{{range .D.PickRows}}
<li>
<label>
<input type="checkbox" name="sub_ids" value="{{.ID}}"{{if not .Done}}{{if not .Skipped}} checked{{end}}{{end}}>
<span class="ticket" style="--tag:{{.PackageColor}}"><b>{{.CustomerTail}}</b>{{.CustomerName}}</span>
<span class="pick-pkg">{{.PackageName}}</span>
<span class="pick-remain">{{.RemainShort}}</span>
{{if .Done}}<span class="chip chip-active">已完成</span>{{end}}
{{if .Skipped}}<span class="chip chip-paused">已跳过</span>{{end}}
</label>
</li>
{{end}}
</ul>
<div class="actions">
<button class="btn btn-primary btn-sm" type="submit">确认完成选中项</button>
<a class="btn btn-sm" href="{{.D.Back}}">取消</a>
</div>
</form>
{{else}}
<p class="muted">{{.D.PickMsg}}</p>
{{end}}
</section>
{{end}}
{{if .D.Tasks}}
<ul class="task-list">
{{range .D.Tasks}}
{{if or .Done .Skipped}}
<li class="task task-done" style="--asg:{{.AssigneeHex}}">
<details class="done-fold">
<summary>
<span class="ticket" style="--tag:{{.PackageColor}}"><b>{{.CustomerTail}}</b>{{.CustomerName}}</span>
<span class="pkg">{{.PackageName}}</span>
<span class="chip {{if .Done}}chip-active{{else}}chip-paused{{end}}">{{if .Done}}已完成{{else}}已跳过{{end}}</span>
{{if .Urgent}}<span class="tag-urgent">加急</span>{{end}}
<span class="fold-caret" aria-hidden="true"></span>
</summary>
<div class="done-detail">
<div class="task-meta">
<span class="asg{{if .AssigneeNone}} none{{end}}"><i style="background:{{.AssigneeHex}}"></i>{{.AssigneeLabel}}</span>
<span>{{.RemainText}}</span>
{{if .DueText}}<span class="due due-{{.Eval.Level}}">{{.DueText}}</span>{{end}}
</div>
{{if .Note}}<div class="task-note">{{.Note}}{{if .Operator}} · {{.Operator}}{{end}}</div>{{end}}
<div class="task-actions">
<form method="post" action="/checkins/{{.CheckinID}}/delete">
<input type="hidden" name="back" value="{{$.D.Back}}">
<button class="btn btn-sm btn-ghost" type="submit">撤销打卡</button>
</form>
<details class="more-actions">
<summary class="btn btn-sm btn-ghost" title="更多操作" aria-label="更多操作">⋯</summary>
<div class="more-menu">
<form method="post" action="/tasks/urgent">
<input type="hidden" name="sub_id" value="{{.ID}}">
<input type="hidden" name="date" value="{{$.D.Selected}}">
<input type="hidden" name="urgent" value="{{if .Urgent}}0{{else}}1{{end}}">
<input type="hidden" name="back" value="{{$.D.Back}}">
<button class="btn btn-sm {{if .Urgent}}btn-urgent{{else}}btn-ghost{{end}}" type="submit">{{if .Urgent}}取消加急{{else}}标记加急{{end}}</button>
</form>
<a class="btn btn-sm" href="/calendar?y={{$.D.Year}}&m={{$.D.Month}}&d={{$.D.Selected}}&pkg={{$.D.PkgID}}&extend={{.ID}}#extend">追加时长</a>
</div>
</details>
</div>
</div>
</details>
</li>
{{else}}
<li class="task{{if .Urgent}} urgent{{end}}" style="--asg:{{.AssigneeHex}}">
<div class="task-main">
<a class="task-title" href="/customers/{{.CustomerID}}">
<span class="ticket" style="--tag:{{.PackageColor}}"><b>{{.CustomerTail}}</b>{{.CustomerName}}</span>
<span class="pkg">{{.PackageName}}</span>
{{if .Urgent}}<span class="tag-urgent">加急</span>{{end}}
</a>
<div class="task-meta">
<span class="asg{{if .AssigneeNone}} none{{end}}"><i style="background:{{.AssigneeHex}}"></i>{{.AssigneeLabel}}</span>
<span>{{.RemainText}}</span>
{{if .DueText}}<span class="due due-{{.Eval.Level}}">{{.DueText}}</span>{{end}}
</div>
{{if .Note}}<div class="task-note">{{.Note}}{{if .Operator}} · {{.Operator}}{{end}}</div>{{end}}
</div>
<div class="task-actions">
<form method="post" action="/subscriptions/{{.ID}}/checkin">
<input type="hidden" name="date" value="{{$.D.Selected}}">
<input type="hidden" name="status" value="done">
<input type="hidden" name="back" value="{{$.D.Back}}">
<button class="btn btn-sm btn-primary">完成</button>
</form>
<details class="more-actions">
<summary class="btn btn-sm btn-ghost" title="更多操作" aria-label="更多操作">⋯</summary>
<div class="more-menu">
<form method="post" action="/subscriptions/{{.ID}}/checkin">
<input type="hidden" name="date" value="{{$.D.Selected}}">
<input type="hidden" name="status" value="skipped">
<input type="hidden" name="back" value="{{$.D.Back}}">
<button class="btn btn-sm btn-ghost" type="submit">跳过这次</button>
</form>
<form method="post" action="/tasks/urgent">
<input type="hidden" name="sub_id" value="{{.ID}}">
<input type="hidden" name="date" value="{{$.D.Selected}}">
<input type="hidden" name="urgent" value="{{if .Urgent}}0{{else}}1{{end}}">
<input type="hidden" name="back" value="{{$.D.Back}}">
<button class="btn btn-sm {{if .Urgent}}btn-urgent{{else}}btn-ghost{{end}}" type="submit">{{if .Urgent}}取消加急{{else}}标记加急{{end}}</button>
</form>
<a class="btn btn-sm" href="/calendar?y={{$.D.Year}}&m={{$.D.Month}}&d={{$.D.Selected}}&pkg={{$.D.PkgID}}&extend={{.ID}}#extend">追加时长</a>
</div>
</details>
</div>
</li>
{{end}}
{{end}}
</ul>
{{else}}<p class="muted">这天没有待打任务。</p>{{end}}
</section>
{{if .D.Extend}}{{template "extend-panel" .D.Extend}}{{end}}
<section class="drawer-block">
<h3>这天到期 <em>{{len .D.Due}}</em></h3>
{{if .D.Due}}
<ul class="due-list">
{{range .D.Due}}
<li>
<a class="due-main" href="/customers/{{.CustomerID}}">
<span class="ticket" style="--tag:{{.PackageColor}}"><b>{{.CustomerTail}}</b>{{.CustomerName}}</span>
<span class="pkg">{{.PackageName}}</span>
<span class="rem">{{.RemainShort}}</span>
</a>
<a class="btn btn-sm" href="/calendar?y={{$.D.Year}}&m={{$.D.Month}}&d={{$.D.Selected}}&pkg={{$.D.PkgID}}&extend={{.ID}}#extend">追加</a>
</li>
{{end}}
</ul>
{{else}}<p class="muted">没有到期订阅。</p>{{end}}
</section>
<section class="drawer-block">
<h3>近 7 天到期</h3>
{{if .D.OtherDue}}
<ul class="mini-due">
{{range .D.OtherDue}}
<li>
<a href="/customers/{{.CustomerID}}"><span class="ticket" style="--tag:{{.PackageColor}}"><b>{{.CustomerTail}}</b>{{.CustomerName}}</span></a>
<span class="muted">{{.PackageName}}</span>
<span class="sm-date">{{.EndDate}}</span>
</li>
{{end}}
</ul>
{{else}}<p class="muted">7 天内没有其它到期。</p>{{end}}
</section>
</aside>
</div>
</div>
{{end}}