feat(fe): AI 独立成页(打字机)+ 修快速记录 6 处问题 + 导航栏去接缝
AI 从底部弹层改成独立页面 pages/ai/ai: - 整屏聊天,输入框固定在底部,不再是「问题都打不完」的小格子 - 回复逐字吐出(30ms/2 字),点一下可跳过 - 会话 session 从 sheet 改为 chat,历史拉 50 条 - 组件里对应的 ai 分支和 aiMessages/aiInput/sendAI 一并删掉, 避免两套实现并存 快速记录九个入口逐个排查,修掉 6 处: 1. 疫苗/驱虫的默认日期写死成 2026-07-21 / 2026-07-15,早已是过去的 日期 —— 改成打开时按今天 +30 / +90 天算 2. 异常观察没选症状时,optIdx 返回 0 会被当成选了「呕吐」,等于替 用户瞎填 —— 加 optPicked 判断,没选就提示 3. 体重为空时会静默存成「上次的体重」,凭空造一条假数据 —— 改为必填校验 4. 消费金额为空会存成 ¥0 —— 必填校验 5. 用药药品名为空 —— 必填校验 6. 照片弹层里「模拟上传预览」是没删掉的假文案 顺带清理:保存按钮上的 data-text / data-icon 从来没被读过(onSave 只用 buildRecord 的返回值),是纯误导的死属性,删掉。我上一条 commit 把它们 说成「写库值」是说错了 —— 真正写库的是 buildRecord 里的 icon 字段,那部分 仍然没动。 导航栏原来是一整条半透明色块,底边和页面渐变硬碰硬,屏幕上横着一道接缝。 改成上实下虚的渐变,并用同一条 mask 把毛玻璃一起淡出(只淡背景不淡 blur 的话,接缝会从色差变成糊边)。返回键做成圆形,和右上角胶囊视觉对称。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@ function fmtDate(iso) {
|
||||
return d.getMonth() + 1 + '月' + d.getDate() + '日';
|
||||
}
|
||||
|
||||
// n 天后的 YYYY-MM-DD,给提醒类弹层做默认日期
|
||||
function daysLater(n) {
|
||||
const d = new Date(Date.now() + n * 86400000);
|
||||
const p = (x) => (x < 10 ? '0' + x : '' + x);
|
||||
return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
|
||||
}
|
||||
|
||||
function ageFromBirthday(bday) {
|
||||
if (!bday) return '';
|
||||
const b = new Date(bday);
|
||||
@@ -60,7 +67,7 @@ Component({
|
||||
pet: {},
|
||||
segSel: {},
|
||||
optSel: {},
|
||||
dateVals: { vaccine: '2026-07-21', deworm: '2026-07-15' },
|
||||
dateVals: { vaccine: '', deworm: '' },
|
||||
wInput: '',
|
||||
wNote: '',
|
||||
poopNote: '',
|
||||
@@ -87,8 +94,6 @@ Component({
|
||||
addColor: '',
|
||||
addBreed: '',
|
||||
commentText: '',
|
||||
aiInput: '',
|
||||
aiMessages: [],
|
||||
riskData: null,
|
||||
comments: [],
|
||||
reminders: [],
|
||||
@@ -147,6 +152,9 @@ Component({
|
||||
patch.wInput = (pet.weight || '').replace('kg', '');
|
||||
patch.wNote = '';
|
||||
}
|
||||
// 提醒日期给个合理的默认值。原本写死成固定日期,时间一过就成了「提醒昨天」
|
||||
if (type === 'vaccine') patch.dateVals = { ...this.data.dateVals, vaccine: daysLater(30) };
|
||||
if (type === 'deworm') patch.dateVals = { ...this.data.dateVals, deworm: daysLater(90) };
|
||||
if (type === 'editPet') {
|
||||
// 预填当前宠物档案
|
||||
patch.addName = pet.name || '';
|
||||
@@ -160,15 +168,6 @@ Component({
|
||||
};
|
||||
patch.optSel = { addStage: Math.max(0, STAGES.indexOf(pet.stage)) };
|
||||
}
|
||||
if (type === 'ai') {
|
||||
patch.aiMessages = [
|
||||
{
|
||||
role: 'ai',
|
||||
text: `我是 ${pet.name || '毛孩子'} 的养宠助手。我会优先基于档案、计划和历史记录回答,而不是泛泛聊天。`,
|
||||
},
|
||||
];
|
||||
patch.aiInput = '';
|
||||
}
|
||||
this.setData(patch);
|
||||
this.loadSheetData(type);
|
||||
},
|
||||
@@ -190,8 +189,6 @@ Component({
|
||||
api.getPoster(id).then((p) => this.setData({ poster: p })).catch(() => {});
|
||||
} else if (type === 'reportDetail' && id) {
|
||||
api.weeklyReport(id).then((r) => this.setData({ reportDetail: r })).catch(() => {});
|
||||
} else if (type === 'ai') {
|
||||
this.loadAIHistory();
|
||||
} else if (type === 'pro') {
|
||||
api.getPro().then((p) => this.setData({ proInfo: p })).catch(() => {});
|
||||
}
|
||||
@@ -492,10 +489,18 @@ Component({
|
||||
const v = this.data.optSel[group];
|
||||
return v === undefined ? 0 : v;
|
||||
},
|
||||
// 与 optIdx 的区别:没选就是没选,不当成选了第 0 项
|
||||
optPicked(group) {
|
||||
return this.data.optSel[group] !== undefined;
|
||||
},
|
||||
|
||||
// 异常观察 → 生成风险评估(真调后端 AI,失败回退)
|
||||
onGenRisk() {
|
||||
const id = store.currentPetId();
|
||||
if (!this.optPicked('symp')) {
|
||||
wx.showToast({ title: '先选一下发生了什么', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const symptoms = ['呕吐', '拉稀', '精神差', '皮肤红'];
|
||||
const durations = ['刚刚', '半天', '1天以上'];
|
||||
const spirits = ['正常', '一般', '明显变差'];
|
||||
@@ -597,13 +602,26 @@ Component({
|
||||
},
|
||||
|
||||
onSave() {
|
||||
const t = this.data.innerType;
|
||||
if (t === 'cost' && !(parseFloat(this.data.costAmount) > 0)) {
|
||||
wx.showToast({ title: '先填个金额', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (t === 'medicine' && !(this.data.medName || '').trim()) {
|
||||
wx.showToast({ title: '先填药品名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.createAndClose(this.buildRecord());
|
||||
},
|
||||
onSaveWeight() {
|
||||
let w = (this.data.wInput || '').toString().replace(/\s+/g, '');
|
||||
if (!w) w = (this.data.pet.weight || '').replace('kg', '');
|
||||
const num = parseFloat(w) || 0;
|
||||
const weight = w.includes('kg') ? w : w + 'kg';
|
||||
const w = (this.data.wInput || '').toString().replace(/\s+/g, '').replace('kg', '');
|
||||
const num = parseFloat(w);
|
||||
// 原来空值会静默存成「上次的体重」,等于凭空造一条假数据
|
||||
if (!w || !(num > 0)) {
|
||||
wx.showToast({ title: '填一个有效体重', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const weight = w + 'kg';
|
||||
this.createAndClose({ type: 'weight', icon: '⚖️', title: '体重 ' + weight, num_value: num, description: (this.data.wNote || '').trim() });
|
||||
},
|
||||
onSavePhoto() {
|
||||
@@ -745,35 +763,5 @@ Component({
|
||||
this.close();
|
||||
},
|
||||
|
||||
// AI 聊天
|
||||
onAiInput(e) { this.setData({ aiInput: e.detail.value }); },
|
||||
// 拉取历史对话,接在开场白之后;没有历史就保持开场白
|
||||
loadAIHistory() {
|
||||
api
|
||||
.aiMessages('sheet', 30)
|
||||
.then((list) => {
|
||||
if (!list || !list.length) return;
|
||||
const history = list.map((m) => ({ role: m.role === 'user' ? 'user' : 'ai', text: m.text }));
|
||||
this.setData({ aiMessages: this.data.aiMessages.concat(history) });
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
sendAI(e) {
|
||||
const preset = e.currentTarget.dataset.text;
|
||||
const text = (preset || this.data.aiInput || '').trim();
|
||||
if (!text) return;
|
||||
const msgs = this.data.aiMessages.concat([{ role: 'user', text }]);
|
||||
this.setData({ aiMessages: msgs, aiInput: '' });
|
||||
api
|
||||
.aiChat({ pet_id: store.currentPetId() || null, session: 'sheet', text })
|
||||
.then((res) => {
|
||||
this.setData({ aiMessages: this.data.aiMessages.concat([{ role: 'ai', text: res.reply }]) });
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({
|
||||
aiMessages: this.data.aiMessages.concat([{ role: 'ai', text: '网络异常,请稍后再试。' }]),
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user