补全页:活动改成和类别同款下拉,两列样式一致
活动原本是 input+datalist、类别是 select,同在一行列里长得不一样,看着别扭。 现在两者共用同一套写法:下拉 + 末尾「其它(自己写)…」+ 选中后露出的文本框 (后端 act_<id>==__custom__ 时读 act_<id>__custom,与类别同一套约定)。 CSS 类也合成一个 fill-sel,脚本只需遍历一次。 顺带:删掉不再需要的 datalist;采纳建议、草稿存取都改走 select.fill-sel。 草稿恢复时对下拉派发 change,所以「其它」的文本框也能跟着恢复显示。
This commit is contained in:
@@ -315,8 +315,14 @@ func (s *Server) adminFillupApply(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pid := range ids {
|
for _, pid := range ids {
|
||||||
// 框里填的是**活动名**:对得上就用现有的,对不上就新建一个
|
// 框里填的是**活动名**:对得上就用现有的,对不上就新建一个。
|
||||||
if name := strings.TrimSpace(r.PostFormValue("act_" + strconv.FormatInt(pid, 10))); name != "" {
|
// 下拉选了「其它(自己写)」时,真正要读的是旁边那个文本框——与类别同一套写法。
|
||||||
|
actKey := "act_" + strconv.FormatInt(pid, 10)
|
||||||
|
actName := strings.TrimSpace(r.PostFormValue(actKey))
|
||||||
|
if actName == "__custom__" {
|
||||||
|
actName = strings.TrimSpace(r.PostFormValue(actKey + "__custom"))
|
||||||
|
}
|
||||||
|
if name := actName; name != "" {
|
||||||
actID, ok := actByName[name]
|
actID, ok := actByName[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
pk, err := s.nextSrcPK(ctx, ex, ds.ID, activitySeq)
|
pk, err := s.nextSrcPK(ctx, ex, ds.ID, activitySeq)
|
||||||
|
|||||||
@@ -266,3 +266,38 @@ func TestFillupCustomCategoryEmptyIsSkipped(t *testing.T) {
|
|||||||
t.Fatalf("没填内容时不该写库,实际 %q", got)
|
t.Fatalf("没填内容时不该写库,实际 %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 活动下拉选「其它(自己写)」时,读的是旁边文本框里的名字——与类别同一套写法。
|
||||||
|
func TestFillupCustomActivity(t *testing.T) {
|
||||||
|
f := newMovementFixture(t)
|
||||||
|
placeID, _ := fillupPlace(t, f)
|
||||||
|
key := "act_" + strconv.FormatInt(placeID, 10)
|
||||||
|
|
||||||
|
applyFillup(t, f, url.Values{key: {"__custom__"}, key + "__custom": {"爬山"}})
|
||||||
|
|
||||||
|
var got string
|
||||||
|
if err := f.pg.QueryRowContext(f.ctx, `SELECT a.name FROM visits v JOIN activities a ON a.id=v.activity_id
|
||||||
|
WHERE v.dataset_id=$1 AND v.place_id=$2 LIMIT 1`, f.dsID, placeID).Scan(&got); err != nil {
|
||||||
|
t.Fatalf("读活动失败: %v", err)
|
||||||
|
}
|
||||||
|
if got != "爬山" {
|
||||||
|
t.Fatalf("应写入手工填的活动名,实际 %q", got)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
f.pg.ExecContext(f.ctx, `DELETE FROM activities WHERE dataset_id=$1 AND name=$2`, f.dsID, "爬山")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选了「其它」却没写名字:什么都不做,别建一个空名活动。
|
||||||
|
func TestFillupCustomActivityEmptyIsSkipped(t *testing.T) {
|
||||||
|
f := newMovementFixture(t)
|
||||||
|
placeID, _ := fillupPlace(t, f)
|
||||||
|
key := "act_" + strconv.FormatInt(placeID, 10)
|
||||||
|
before := f.scalar(t, `SELECT count(*) FROM activities WHERE dataset_id=$1`, f.dsID)
|
||||||
|
|
||||||
|
applyFillup(t, f, url.Values{key: {"__custom__"}, key + "__custom": {" "}})
|
||||||
|
|
||||||
|
if n := f.scalar(t, `SELECT count(*) FROM activities WHERE dataset_id=$1`, f.dsID); n != before {
|
||||||
|
t.Fatalf("没填名字不该建类型,活动数 %d → %d", before, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,14 +52,21 @@
|
|||||||
<td data-label="缺什么">{{$row.Missing}}</td>
|
<td data-label="缺什么">{{$row.Missing}}</td>
|
||||||
<td data-label="活动">
|
<td data-label="活动">
|
||||||
{{if $row.NoAct}}
|
{{if $row.NoAct}}
|
||||||
<input list="fill-act" name="act_{{$row.PlaceID}}" value=""
|
{{$ahint := $row.ActHint}}
|
||||||
placeholder="(不动)" data-hint="{{$row.ActHint}}" style="min-width:8em">
|
<select name="act_{{$row.PlaceID}}" class="fill-sel" data-hint="{{$ahint}}" style="min-width:10em">
|
||||||
|
<option value="">(不动)</option>
|
||||||
|
{{if $ahint}}<option value="{{$ahint}}">建议:{{$ahint}}</option>{{end}}
|
||||||
|
{{range $.Activities}}{{if ne .Name $ahint}}<option value="{{.Name}}">{{.Name}}</option>{{end}}{{end}}
|
||||||
|
<option value="__custom__">其它(自己写)…</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" name="act_{{$row.PlaceID}}__custom" placeholder="例如 爬山"
|
||||||
|
style="display:none;margin-top:4px;min-width:10em">
|
||||||
{{else}}<span class="muted">—</span>{{end}}
|
{{else}}<span class="muted">—</span>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td data-label="类别">
|
<td data-label="类别">
|
||||||
{{if $row.NoCat}}
|
{{if $row.NoCat}}
|
||||||
{{$hint := $row.CatHint}}
|
{{$hint := $row.CatHint}}
|
||||||
<select name="cat_{{$row.PlaceID}}" class="cat-sel" data-hint="{{$hint}}">
|
<select name="cat_{{$row.PlaceID}}" class="fill-sel" data-hint="{{$hint}}" style="min-width:10em">
|
||||||
<option value="">(不动)</option>
|
<option value="">(不动)</option>
|
||||||
{{if $hint}}<option value="{{$hint}}">建议:{{catName $hint}}</option>{{end}}
|
{{if $hint}}<option value="{{$hint}}">建议:{{catName $hint}}</option>{{end}}
|
||||||
{{range $.Categories}}{{if ne .Value $hint}}<option value="{{.Value}}">{{.Label}}</option>{{end}}{{end}}
|
{{range $.Categories}}{{if ne .Value $hint}}<option value="{{.Value}}">{{.Label}}</option>{{end}}{{end}}
|
||||||
@@ -103,16 +110,12 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<datalist id="fill-act">
|
|
||||||
{{range .Activities}}<option value="{{.Name}}"></option>{{end}}
|
|
||||||
</datalist>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
var KEY = 'rond-fillup-draft';
|
var KEY = 'rond-fillup-draft';
|
||||||
|
|
||||||
// 选了「其它(自己写)」就露出旁边的文本框
|
// 活动和类别是同一套下拉:选了「其它(自己写)」就露出旁边的文本框
|
||||||
document.querySelectorAll('select.cat-sel').forEach(function (sel) {
|
document.querySelectorAll('select.fill-sel').forEach(function (sel) {
|
||||||
var box = document.querySelector('input[name="' + sel.name + '__custom"]');
|
var box = document.querySelector('input[name="' + sel.name + '__custom"]');
|
||||||
if (!box) return;
|
if (!box) return;
|
||||||
var sync = function () {
|
var sync = function () {
|
||||||
@@ -124,17 +127,12 @@
|
|||||||
sync();
|
sync();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 采纳本页建议:活动按 data-hint 填进输入框,类别把建议项选上
|
// 采纳本页建议:把建议项选上(活动和类别都是下拉)
|
||||||
var pick = document.getElementById('pick-hints');
|
var pick = document.getElementById('pick-hints');
|
||||||
if (pick) {
|
if (pick) {
|
||||||
pick.addEventListener('click', function () {
|
pick.addEventListener('click', function () {
|
||||||
var n = 0;
|
var n = 0;
|
||||||
document.querySelectorAll('input[name^="act_"]').forEach(function (inp) {
|
document.querySelectorAll('select.fill-sel').forEach(function (sel) {
|
||||||
if (inp.value || !inp.dataset.hint) return;
|
|
||||||
inp.value = inp.dataset.hint;
|
|
||||||
n++;
|
|
||||||
});
|
|
||||||
document.querySelectorAll('select.cat-sel').forEach(function (sel) {
|
|
||||||
if (sel.value || !sel.dataset.hint) return;
|
if (sel.value || !sel.dataset.hint) return;
|
||||||
sel.value = sel.dataset.hint;
|
sel.value = sel.dataset.hint;
|
||||||
n++;
|
n++;
|
||||||
@@ -147,7 +145,7 @@
|
|||||||
// 否则挑了半天,点一下就全没了。
|
// 否则挑了半天,点一下就全没了。
|
||||||
var save = function () {
|
var save = function () {
|
||||||
var draft = {};
|
var draft = {};
|
||||||
document.querySelectorAll('input[name^="act_"], input[name$="__custom"], select.cat-sel').forEach(function (el) {
|
document.querySelectorAll('select.fill-sel, input[name$="__custom"]').forEach(function (el) {
|
||||||
if (el.value) draft[el.name] = el.value;
|
if (el.value) draft[el.name] = el.value;
|
||||||
});
|
});
|
||||||
try { sessionStorage.setItem(KEY, JSON.stringify(draft)); } catch (e) { /* 隐私模式就算了 */ }
|
try { sessionStorage.setItem(KEY, JSON.stringify(draft)); } catch (e) { /* 隐私模式就算了 */ }
|
||||||
|
|||||||
Reference in New Issue
Block a user