Files
subtrack/internal/web/templates/pages/customer_detail.html
T
subtrack 621adfa4a8 feat(v1.9.0): Bootstrap 5 迁移第 3/4、4/4 轮(抽屉、表单、大屏内部)
版本号保持 1.9.0,仍为纯前端改动:
- 日历抽屉:快速输入改 form-control,任务/到期列表改 Bootstrap list-group,加急标签改 badge
- 客户详情:套餐下拉、开始日、天数/次数、终止日统一 form-select / form-control
- 大屏内部:KPI 改 card,TOP 榜与今日列表改 list-group,加急改 badge;热力网格保留自绘
- app.css 增加 Bootstrap 收敛规则(list-group 透明背景、任务分隔线)

验证:无头 Chrome 实拍日历、客户详情、大屏三页(shots/bs3-*.png),页面正常渲染
数据库与业务逻辑零改动
2026-10-19 22:17:01 +08:00

199 lines
9.4 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="page">
<header class="cust-head">
<div class="cust-id">
<span class="cust-tail">{{.D.Customer.Tail}}</span>
<div>
<h1>{{.D.Customer.Name}}{{if .D.Customer.Archived}} <span class="chip chip-closed">已停用</span>{{end}}</h1>
<div class="cust-tags">
{{range .D.Customer.TagList}}<span class="tagchip">{{.}}</span>{{end}}
</div>
</div>
</div>
<dl class="cust-meta">
{{if .D.Customer.GameName}}<div><dt>游戏</dt><dd>{{.D.Customer.GameName}}</dd></div>{{end}}
{{if .D.Customer.GameServer}}<div><dt>区服</dt><dd>{{.D.Customer.GameServer}}</dd></div>{{end}}
{{if .D.Customer.Contact}}<div><dt>联系</dt><dd>{{.D.Customer.Contact}}</dd></div>{{end}}
{{if .D.Customer.Source}}<div><dt>来源</dt><dd>{{.D.Customer.Source}}</dd></div>{{end}}
<div><dt>打手</dt><dd>
<span class="asg{{if eq .D.Customer.AssigneeID 0}} none{{end}}"><i style="background:{{if .D.Customer.AssigneeColor}}{{.D.Customer.AssigneeColor}}{{else}}#8A8F98{{end}}"></i>{{if .D.Customer.AssigneeName}}{{.D.Customer.AssigneeName}}{{else}}未指派{{end}}</span>
<form method="post" action="/customers/{{.D.Customer.ID}}/assignee" class="assign-form">
<select name="assignee_id">
<option value="0">未指派</option>
{{range .D.Users}}<option value="{{.ID}}"{{if eq $.D.Customer.AssigneeID .ID}} selected{{end}}>{{if .DisplayName}}{{.DisplayName}}{{else}}{{.Username}}{{end}}</option>{{end}}
</select>
<button class="btn btn-sm" type="submit">保存</button>
</form>
</dd></div>
<div><dt>建档</dt><dd>{{.D.Customer.CreatedAt}}</dd></div>
</dl>
<div class="page-actions">
<a class="btn" href="/customers/{{.D.Customer.ID}}/edit">编辑资料</a>
{{if .D.Customer.Archived}}
<form method="post" action="/customers/{{.D.Customer.ID}}/restore"><button class="btn" type="submit">恢复客户</button></form>
{{else}}
<form method="post" action="/customers/{{.D.Customer.ID}}/archive" data-confirm="停用后该客户不再出现在日历和大屏,订阅会保留。确定停用?"><button class="btn btn-ghost" type="submit">停用客户</button></form>
{{end}}
<form method="post" action="/customers/{{.D.Customer.ID}}/delete" data-confirm="将永久删除该客户及其全部订阅、打卡与追加记录,无法恢复。确定删除?">
<input type="hidden" name="confirm" value="delete">
<button class="btn btn-ghost" type="submit" style="color:var(--danger)">彻底删除</button>
</form>
<a class="btn btn-ghost" href="/customers">返回列表</a>
</div>
</header>
{{if .D.Customer.Note}}<div class="notice note">{{.D.Customer.Note}}</div>{{end}}
{{if .D.CustCal}}
<section class="section">
<div class="section-head">
<h2>当月排单</h2>
<div class="minical-nav">
<a class="btn btn-sm btn-ghost" href="{{.D.CustCal.Prev}}">‹</a>
<b>{{.D.CustCal.Title}}</b>
<a class="btn btn-sm btn-ghost" href="{{.D.CustCal.Next}}">›</a>
<span class="muted small">{{.D.CustCal.TaskDays}} 天有单 · {{.D.CustCal.TotalTasks}} 单</span>
</div>
</div>
<div class="minical">
<div class="minical-dow">{{range .D.CustCal.Weekdays}}<span>{{.}}</span>{{end}}</div>
{{range .D.CustCal.Weeks}}
<div class="minical-row">
{{range .}}
<a class="minical-cell{{if not .InMonth}} out{{end}}{{if .IsToday}} today{{end}}{{if eq .Count 0}} no-task{{end}}{{if .AllDone}} all-done{{end}}" href="/calendar?d={{.Date}}" title="{{.Date}}">
<span class="mc-day">{{.Day}}</span>
{{if gt .Count 0}}<span class="mc-count">{{.Done}}/{{.Count}}</span>{{end}}
</a>
{{end}}
</div>
{{end}}
</div>
</section>
{{end}}
<section class="section">
<div class="section-head">
<h2>订阅</h2>
<span class="muted">同一套餐只能有一条订阅,需要更多时长请用「追加」</span>
</div>
{{if .D.Subs}}
<div class="subgrid">
{{range .D.Subs}}{{template "sub-card" .}}{{end}}
</div>
{{else}}
<p class="muted">还没有订阅。用下面的表单为他开通第一个套餐。</p>
{{end}}
{{if .D.Packages}}
<form method="post" action="/customers/{{.D.Customer.ID}}/subscriptions" class="inline-form" data-sub-form>
<select class="form-select form-select-sm" name="package_id" required data-pkg-select>
<option value="">选择套餐…</option>
{{range .D.Packages}}
<option value="{{.ID}}" data-unit="{{if .Season}}season{{else}}{{.Unit}}{{end}}" data-amount="{{.DefaultAmount}}" data-validity="{{.DefaultValidityDays}}"{{if .Archived}} disabled{{end}}{{if .SeasonExpired $.TodayStr}} disabled{{end}}>
{{.Name}} · {{.UnitLabel}} {{if .Season}}{{.SeasonStart}}~{{.SeasonEnd}}{{else}}{{.DefaultAmount}}{{if eq .Unit "day"}}天{{else}}次{{end}}{{end}}{{if .SeasonExpired $.TodayStr}}(赛季已结束){{end}}{{if .Archived}}(已归档){{end}}
</option>
{{end}}
</select>
<label class="inline-field" data-start-wrap>开始日
<input class="form-control form-control-sm" type="date" name="start_date" value="{{.TodayStr}}" required>
</label>
<label class="inline-field" data-days-wrap>{{/* 默认显示 */}}
天数 <input class="form-control form-control-sm" type="number" name="days" min="1" max="3650" value="30">
</label>
<label class="inline-field" data-count-wrap hidden>
次数 <input class="form-control form-control-sm" type="number" name="total_count" min="1" max="10000" value="10">
</label>
<label class="inline-field" data-end-wrap>
终止日(可留空)<input class="form-control form-control-sm" type="date" name="end_date">
</label>
<button class="btn btn-primary" type="submit">开通订阅</button>
</form>
{{else}}
<p class="muted">还没有可用套餐,<a href="/packages/new">先新建套餐</a>。</p>
{{end}}
</section>
{{if .D.Extend}}{{template "extend-panel" .D.Extend}}{{end}}
{{if .D.Timeline}}
<section class="section">
<div class="section-head">
<h2>时间轴</h2>
<span class="muted">{{.D.Timeline.Start}} → {{.D.Timeline.End}} · 灰色为暂停段</span>
</div>
<div class="timeline">
<div class="tl-axis">
{{range .D.Timeline.Ticks}}<span class="tl-tick" style="left:{{pct .Left}}%">{{.Label}}</span>{{end}}
</div>
{{range .D.Timeline.Bars}}
<div class="tl-row">
<span class="tl-name">{{.Name}}</span>
<div class="tl-track">
<i class="tl-bar st-{{.State}}" style="--tag:{{.Color}};left:{{pct .Left}}%;width:{{pct .Width}}%"></i>
{{range .Pauses}}<u class="tl-pause" style="left:{{pct .Left}}%;width:{{pct .Width}}%"></u>{{end}}
{{if .HasDue}}<em class="tl-due" style="left:{{pct .DueLeft}}%"></em>{{end}}
</div>
<span class="tl-legend">{{.Label}}</span>
</div>
{{end}}
</div>
</section>
{{end}}
<section class="section">
<div class="section-head">
<h2>服务时长追加记录</h2>
<span class="muted">每次追加都留痕,可作为对账依据</span>
</div>
{{if .D.Adjustments}}
<div class="table-wrap">
<table class="table tight">
<thead>
<tr><th>时间</th><th>套餐</th><th>类型</th><th>计算起点</th><th>结束日变化</th><th>增量</th><th>备注</th><th>操作人</th></tr>
</thead>
<tbody>
{{range .D.Adjustments}}
<tr>
<td class="mono small">{{.CreatedAt}}</td>
<td><span class="dot" style="background:{{index $.D.AdjSubColor .SubID}}"></span>{{index $.D.AdjSubName .SubID}}</td>
<td>{{.KindLabel}}</td>
<td class="mono small">{{if .BaseDate}}{{.BaseDate}}{{else}}—{{end}}</td>
<td class="mono small">{{if .OldEndDate}}{{.OldEndDate}} → {{end}}{{if .NewEndDate}}{{.NewEndDate}}{{else}}—{{end}}</td>
<td class="mono">
{{if gt .DaysAdded 0}}<span class="plus">+{{.DaysAdded}} 天</span>{{end}}
{{if gt .CountAdded 0}}<span class="plus">+{{.CountAdded}} 次</span>{{end}}
{{if and (le .DaysAdded 0) (le .CountAdded 0)}}—{{end}}
</td>
<td class="small">{{if .Note}}{{.Note}}{{else}}—{{end}}</td>
<td class="small">{{if .OperatorName}}{{.OperatorName}}{{else}}—{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{else}}<p class="muted">还没有追加记录。</p>{{end}}
</section>
<section class="section">
<div class="section-head">
<h2>打卡历史</h2>
<span class="muted">最近 30 条</span>
</div>
{{if .D.Checkins}}
<ul class="checkin-list">
{{range .D.Checkins}}
<li>
<span class="mono">{{.ServiceDate}}</span>
<span class="dot" style="background:{{.SubColor}}"></span>
<span>{{.SubName}}</span>
<span class="chip {{if eq .Status "done"}}chip-active{{else}}chip-paused{{end}}">{{if eq .Status "done"}}完成{{else}}跳过{{end}}</span>
{{if .Note}}<span class="muted small">{{.Note}}</span>{{end}}
<span class="muted small">{{if .OperatorName}}{{.OperatorName}}{{end}}</span>
</li>
{{end}}
</ul>
{{else}}<p class="muted">还没有打卡记录。</p>{{end}}
</section>
</div>
{{end}}