Files
sundynix-pets/pets-fe/pages/petform/petform.wxml
T
Blizzard 6fd4977f46 feat(fe): 建档挑方案、计划页套方案、阶段漂移提示
后端上一轮做完了但小程序上看不到,这次接上。

## 建档第四步:挑方案
方案按阶段取,所以必须排在「资料」之后(阶段是第三步定的)。卡片上按重复方式
分开报数(每天 3 项 / 每周 2 项 / 一次性 2 项),不报一个总数——4 条「每天」
套一整月就是 120 多条,只给总数用户会以为出 bug。

「先空着,我自己排」是个平等的选项,和方案卡长一样、就在下面一眼能看到,
不是藏起来的退路:已经养熟的宠物主人有自己的节奏,硬塞一套只会被删掉。

默认选第一套——多数用户是新手,给一个合理默认比让他面对空白好。

编辑档案时不出现第四步:改档案不该顺手改计划。

套方案失败不算建档失败:档案已经建好了,方案之后在计划页还能套,
不该把用户退回表单。

## 计划页:空月份和平时都能套
空月份的提示条带「套一套方案」按钮 —— 空着让用户从零排,多数人会直接退出去。
但「+ 加一件」始终在,不能只给套用。

浮层里写明「套用是往里加,不会覆盖你已经排好的」,因为服务端就是按
「日期+标题」去重的,这句话是真的。套完 toast 报排了几件;一件都没排
(全都已存在)时说「这个月已经有这些安排了」,不说「成功」。

## 阶段漂移提示
首页门面卡下面一张卡:「钞票已经 17个月,从幼年期进入成年期了」,
带上新阶段的划分依据、养护重点和体型说明,点进去到建档页改。

用主色不用红色 —— 这是「可以做一下」不是「出问题了」。
改完回首页 onShow 重拉一次,卡片自然消失。

不在首页直接改阶段:改完要不要套新方案是同一件事,放在建档页一起做才连贯。

## 顺手把合并的尾巴清了
manageTasks 那一支删了:它管的是 DailyTask,而今日任务已经并进计划节点,
增删改都在计划页。留着会出现「这里加的任务」和「计划页加的节点」进不同的表。

连带清掉 7 个死方法 + taskSheets + 一个死 require(简易表单那套早随记录表单
搬到 pages/addrecord 了)。首页现在一个弹层都不开,bottom-sheet 组件也去掉了。

弹层累计:js 1110 → 689 行,wxml 485 → 278 行。

## 验证(预生产库实跑)
  建档四步    6 个月柯基 → 幼年期 → 拉到「幼犬标准照护」→ 套用本月 7 条
              → 首页今日任务 4 条
  先空着      今日任务 0 条,疫苗驱虫提醒仍然建
  计划页套用  0 → 7 条;重复套用 0 条(去重生效)
  漂移检测    17 个月柯基存着幼年期 → drifted=true,给出依据/重点/体型
              改成成年期 → drifted=false
              没生日 → 不提示(算不出就不猜)
              「刚到家」→ 不提示(叠加层不是年龄段)

第一版验证脚本把阶段改成了和算出来一样的值,drifted=False 是对的,
但我的 print 无条件打了提示文案,看着像 bug。重测才验到真的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 12:59:27 +08:00

162 lines
8.2 KiB
Plaintext
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.
<nav-bar title="{{editing ? '编辑档案' : '建一份档案'}}" show-back="{{true}}"></nav-bar>
<!-- 三步进度。编辑时不分步(用户是来改某一项的,不该被按顺序走一遍) -->
<view wx:if="{{!editing}}" class="pf-steps">
<view wx:for="{{steps}}" wx:key="*this" class="pf-step {{index <= step ? 'on' : ''}}">
<view class="pf-dot">{{index < step ? '✓' : index + 1}}</view>
<text class="pf-slabel">{{item}}</text>
</view>
</view>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body pf-body">
<!-- ① 名字 + 头像 -->
<block wx:if="{{editing || step === 0}}">
<view class="card">
<view class="pf-h">先给它起个名字</view>
<view class="pf-av-wrap" bindtap="pickAvatar">
<view class="pf-av">
<image wx:if="{{form.avatar_url}}" class="pf-av-img" src="{{form.avatar_url}}" mode="aspectFill"></image>
<block wx:else><pt-icon name="photo" size="{{56}}"></pt-icon></block>
</view>
<view class="pf-av-tip">{{form.avatar_url ? '换一张' : '传张照片'}}</view>
</view>
<view class="field"><label>名字</label>
<input class="input" placeholder="例如:布丁" placeholder-class="placeholder"
value="{{form.name}}" maxlength="16" bindinput="onName"/></view>
</view>
</block>
<!-- ② 猫还是狗 + 品种 -->
<block wx:if="{{editing || step === 1}}">
<view class="card">
<view class="pf-h">它是猫还是狗?</view>
<view class="pf-species">
<view class="pf-sp {{form.species === 'cat' ? 'on' : ''}}" data-sp="cat" bindtap="pickSpecies">
<pt-icon name="cat" size="{{64}}"></pt-icon><text>猫猫</text>
</view>
<view class="pf-sp {{form.species === 'dog' ? 'on' : ''}}" data-sp="dog" bindtap="pickSpecies">
<pt-icon name="dog" size="{{64}}"></pt-icon><text>狗狗</text>
</view>
</view>
<view class="field"><label>品种</label>
<view class="picker-box {{form.breed ? '' : 'pf-empty'}}" bindtap="openBreed">
{{form.breed || '选一个品种'}}
</view>
</view>
</view>
</block>
<!-- ③ 其他基本信息 -->
<block wx:if="{{editing || step === 2}}">
<view class="card">
<view class="pf-h">再补几项,之后都能改</view>
<view class="field"><label>性别</label><view class="seg">
<view wx:for="{{genders}}" wx:key="*this" class="seg-btn {{form.gender === item ? 'selected' : ''}}"
data-v="{{item}}" bindtap="pickGender">{{item}}</view>
</view></view>
<!-- 生日和到家日分开:领养的成年猫狗生日常常是不知道的,
而「一起生活了多少天」要按到家日算。合成一个字段两件事都说不准 -->
<view class="field"><label>生日</label>
<picker mode="date" value="{{form.birthday}}" end="{{today}}" bindchange="onBirthday">
<view class="picker-box {{form.birthday ? '' : 'pf-empty'}}">{{form.birthday || '不知道可以先跳过'}}</view>
</picker></view>
<view class="field"><label>到家日期</label>
<picker mode="date" value="{{form.arrived_at}}" end="{{today}}" bindchange="onArrived">
<view class="picker-box {{form.arrived_at ? '' : 'pf-empty'}}">{{form.arrived_at || '哪天接回家的'}}</view>
</picker></view>
<view class="field"><label>当前体重(kg</label>
<input class="input" type="digit" placeholder="例如:2.8" placeholder-class="placeholder"
value="{{form.weight}}" bindinput="onWeight"/></view>
<view class="field"><label>毛色</label>
<input class="input" placeholder="例如:橘白 / 奶牛 / 黑" placeholder-class="placeholder"
value="{{form.color}}" maxlength="16" bindinput="onColor"/></view>
<view class="field"><label>当前阶段</label><view class="mini-options">
<view wx:for="{{stages}}" wx:key="*this" class="option {{form.stage === item ? 'selected' : ''}}"
data-v="{{item}}" bindtap="pickStage">{{item}}</view>
</view>
<!-- 把划分标准摆出来。用户不该猜「幼年期」是几个月到几个月,
狗还得知道自己这个体型的阈值和别的体型不一样 -->
<view wx:if="{{stageBasis}}" class="pf-basis">{{stageBasis}}</view>
<view wx:if="{{sizeLabel}}" class="tiny">{{sizeLabel}}——阶段阈值按体型算</view>
<view wx:if="{{stageAuto && stageAuto !== form.stage}}" class="tiny">按生日推算是「{{stageAuto}}」,可以手改</view>
</view>
</view>
</block>
<!-- ④ 挑一套养护方案(编辑时不出现:改档案不该顺手改计划) -->
<block wx:if="{{!editing && step === 3}}">
<view class="card">
<view class="pf-h">要不要套一套养护方案?</view>
<view class="pf-sub">按 {{form.stage}} 配的,套用后会排进本月的计划。之后随时能改、能删。</view>
<view wx:for="{{tplOptions}}" wx:key="id"
class="pf-tpl {{form.template_id === item.id ? 'on' : ''}}"
data-id="{{item.id}}" bindtap="pickTemplate">
<view class="pf-tpl-top">
<view class="pf-tpl-name">{{item.name}}</view>
<view wx:if="{{form.template_id === item.id}}" class="pf-tpl-tick"><pt-icon name="check" size="{{26}}"></pt-icon></view>
</view>
<view class="pf-tpl-sum">{{item.summary}}</view>
<view class="pf-tpl-meta">
<text wx:if="{{item.daily}}">每天 {{item.daily}} 项</text>
<text wx:if="{{item.weekly}}">每周 {{item.weekly}} 项</text>
<text wx:if="{{item.monthly}}">每月 {{item.monthly}} 项</text>
<text wx:if="{{item.once}}">一次性 {{item.once}} 项</text>
</view>
</view>
<!-- 「先空着」必须是个平等的选项,不是藏起来的退路:
已经养熟的宠物主人有自己的节奏,硬塞一套只会被删掉 -->
<view class="pf-tpl {{form.template_id === '' ? 'on' : ''}}" data-id="" bindtap="pickTemplate">
<view class="pf-tpl-top">
<view class="pf-tpl-name">先空着,我自己排</view>
<view wx:if="{{form.template_id === ''}}" class="pf-tpl-tick"><pt-icon name="check" size="{{26}}"></pt-icon></view>
</view>
<view class="pf-tpl-sum">之后在「管理计划」里随时能套用</view>
</view>
<view wx:if="{{!tplOptions.length}}" class="empty">这个阶段还没有配方案,建完档可以自己排</view>
<view class="tiny pf-tpl-note">疫苗和驱虫提醒不管选哪个都会建 —— 漏针是真会出事的</view>
</view>
</block>
</view>
</scroll-view>
<!-- 底部按钮固定 -->
<view class="pf-foot">
<view wx:if="{{!editing && step > 0}}" class="btn btn-ghost pf-back" bindtap="prev">上一步</view>
<view class="btn btn-primary pf-next {{saving ? 'pf-busy' : ''}}" bindtap="next">
{{saving ? '保存中…' : (editing ? '保存' : (step < 3 ? '下一步' : '完成建档'))}}
</view>
</view>
<!-- 品种选择器:左侧列表 + 右侧 A-Z 索引 -->
<view wx:if="{{breedShow}}" class="pf-mask" bindtap="closeBreed">
<view class="pf-sheet" catchtap="noop">
<view class="pf-sheet-h">
<text>选择品种</text>
<view class="link" catchtap="clearBreed">不确定</view>
</view>
<view class="pf-breed-wrap">
<scroll-view class="pf-breed-list" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"
scroll-into-view="{{breedAnchor}}" scroll-with-animation="{{true}}">
<block wx:for="{{breedGroups}}" wx:key="initial">
<view class="pf-bi" id="bi-{{item.initial}}">{{item.initial}}</view>
<view wx:for="{{item.items}}" wx:for-item="b" wx:key="id"
class="pf-brow {{form.breed === b.name ? 'on' : ''}}"
data-name="{{b.name}}" bindtap="pickBreed">{{b.name}}</view>
</block>
<view wx:if="{{!breedGroups.length}}" class="empty">还没有配置品种</view>
</scroll-view>
<view class="pf-index">
<view wx:for="{{breedGroups}}" wx:key="initial" class="pf-ichar"
data-i="{{item.initial}}" bindtap="jumpInitial">{{item.initial}}</view>
</view>
</view>
</view>
</view>