diff --git a/pets-be/internal/service/report.go b/pets-be/internal/service/report.go index 8250253..88db0cb 100644 --- a/pets-be/internal/service/report.go +++ b/pets-be/internal/service/report.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "strings" "time" "github.com/sundynix/pets-be/internal/model" @@ -47,19 +48,59 @@ func (s *Service) GetWeeklyReport(userID, petID string) (*WeeklyReport, error) { Count(&highRisk) status := "稳定成长" + risk := "无高风险异常记录" if highRisk > 0 { status = "需要关注" + // 原来这句写死成「无高风险异常记录」,和上面刚算出来的 highRisk 自相矛盾: + // 记了 3 条高风险,摘要还说没有 + risk = fmt.Sprintf("有 %d 条高风险异常记录", highRisk) } + return &WeeklyReport{ - Summary: fmt.Sprintf("本周 %s 完成 %d 项任务,体重 %+.1fkg,无高风险异常记录。", pet.Name, tasksCompleted, gain), + Summary: fmt.Sprintf("本周 %s 完成 %d 项任务,体重 %+.1fkg,%s。", + pet.Name, tasksCompleted, gain, risk), TasksCompleted: tasksCompleted, WeightGain: gain, HighRiskCount: highRisk, HealthStatus: status, - NextWeekFocus: "第 2 针疫苗提醒、继续观察体重趋势、避免频繁更换食物。", + NextWeekFocus: s.nextWeekFocus(petID, highRisk, gain), }, nil } +// nextWeekFocus 下周重点。原来是一句写死的「第 2 针疫苗提醒、继续观察体重趋势、 +// 避免频繁更换食物」——不管谁的宠物、多大年纪、有没有异常,看到的都是这句, +// 而且「第 2 针疫苗」对成年猫狗根本不适用。改成按真实数据拼。 +func (s *Service) nextWeekFocus(petID string, highRisk int64, gain float64) string { + var parts []string + + // 未来 7 天到期的提醒,是最具体的「下周要做什么」 + now := time.Now() + var rems []model.Reminder + s.db.Where("pet_id = ? AND next_due_date >= ? AND next_due_date <= ?", + petID, now, now.AddDate(0, 0, 7)).Order("next_due_date asc").Limit(2).Find(&rems) + for _, r := range rems { + parts = append(parts, r.Title) + } + + if highRisk > 0 { + parts = append(parts, "继续观察上周记录的异常,必要时就医") + } + + // 一周没称过体重(gain 恰好为 0 也可能是真没变,所以查条数而不是看 gain) + var weighed int64 + s.db.Model(&model.HealthRecord{}). + Where("pet_id = ? AND type = ? AND occurred_at >= ?", petID, model.RecordWeight, now.AddDate(0, 0, -7)). + Count(&weighed) + if weighed == 0 { + parts = append(parts, "本周还没称体重,补记一次") + } + + if len(parts) == 0 { + return "各项都稳定,保持现在的记录节奏就好。" + } + return strings.Join(parts, ";") + "。" +} + // BillCategory 账单分类项 type BillCategory struct { Category string `json:"category"` diff --git a/pets-fe/app.json b/pets-fe/app.json index 1ab3921..0c7ae95 100644 --- a/pets-fe/app.json +++ b/pets-fe/app.json @@ -11,7 +11,6 @@ "pages/mine/mine", "pages/decorate/decorate", "pages/settings/settings", - "pages/ai/ai", "pages/user/user", "pages/relations/relations" ], diff --git a/pets-fe/app.wxss b/pets-fe/app.wxss index 4a903bd..88d1da3 100644 --- a/pets-fe/app.wxss +++ b/pets-fe/app.wxss @@ -427,3 +427,20 @@ page{scrollbar-width:none;-ms-overflow-style:none} color:var(--primary-dark);background:var(--primary-soft);border:1rpx solid var(--primary-line); } .follow-btn.on{color:var(--muted);background:#F4F1EC;border-color:var(--line)} + +/* ===== 记录类型分组宫格 ===== + 记录页和「快速记录」弹层都在用,页面级 wxss 跨不了页 + 四组各一个色系,色值全走 token 的 soft/ink 二件套,没有新色。 + .quick 的配色由 .tone-N 决定(app.wxss 里已有),这里只管组标题和留白 */ +.tg{margin-bottom:var(--sp-4)} +.tg:last-of-type{margin-bottom:0} +.tg-head{ + display:flex;align-items:center;gap:var(--sp-2); + font-size:var(--fs-md);font-weight:var(--fw-b);margin-bottom:var(--sp-3); +} +.tg-bar{width:6rpx;height:26rpx;border-radius:3rpx;background:currentColor} +.tg-n{margin-left:auto;font-size:var(--fs-cap);font-weight:var(--fw);color:var(--muted2)} +.tg.tone-1 .tg-head{color:var(--primary-ink)} +.tg.tone-2 .tg-head{color:var(--green-ink)} +.tg.tone-3 .tg-head{color:var(--blue-ink)} +.tg.tone-4 .tg-head{color:var(--purple-ink)} diff --git a/pets-fe/components/bottom-sheet/bottom-sheet.js b/pets-fe/components/bottom-sheet/bottom-sheet.js index 0c5a82a..04738ff 100644 --- a/pets-fe/components/bottom-sheet/bottom-sheet.js +++ b/pets-fe/components/bottom-sheet/bottom-sheet.js @@ -98,6 +98,13 @@ const SIMPLE_HINT = { supplement: '吃的什么、多大剂量、吃多久', }; +// 异常观察的四组选项。原来散在 AI 风险评估那个方法里当局部变量, +// 现在 buildRecord 也要用,提到模块级 +const SYMPTOMS = ['呕吐', '拉稀', '精神差', '皮肤红']; +const DURATIONS = ['刚刚', '半天', '1天以上']; +const SPIRITS = ['正常', '一般', '明显变差']; +const SEVERITY = ['低', '中', '高']; + const POOP = ['正常', '软便', '拉稀']; const FOOD = ['正常', '偏少', '不吃']; const COST = ['食品', '医疗', '用品']; @@ -119,6 +126,7 @@ Component({ optSel: {}, dateVals: { vaccine: '', deworm: '' }, // 通用简易记录(洗护/清洁那 15 种共用) + typeGroups: [], isSimple: false, simpleLabel: '', simpleDate: '', @@ -151,7 +159,6 @@ Component({ addColor: '', addBreed: '', commentText: '', - riskData: null, comments: [], reminders: [], poster: null, @@ -213,13 +220,13 @@ Component({ patch.replyTo = {}; patch.cmFocus = false; patch.kbHeight = 0; - patch.riskData = null; patch.saving = false; patch.simpleNote = ''; patch.simpleImages = []; } // 是否走通用简易表单。缓存热的时候是同步的;万一没热就按「不是」处理, // 那 9 种的分支写死在 wxml 里不依赖这份缓存,不会因此打不开 + if (type === 'quickRecord') patch.typeGroups = recordTypes.groups() || []; const simple = recordTypes.isSimple(type); patch.isSimple = simple; if (simple) { @@ -754,37 +761,6 @@ Component({ }, // 异常观察 → 生成风险评估(真调后端 AI,失败回退) - onGenRisk() { - const id = store.currentPetId(); - if (!this.optPicked('symp')) { - wx.showToast({ title: '先选一下发生了什么', icon: 'none' }); - return; - } - const symptoms = ['呕吐', '拉稀', '精神差', '皮肤红']; - const durations = ['刚刚', '半天', '1天以上']; - const spirits = ['正常', '一般', '明显变差']; - const body = { - symptoms: [symptoms[this.optIdx('symp')]], - duration: durations[this.segIdx('dur')], - spirit: spirits[this.segIdx('spirit')], - }; - if (!id) { - this.setData({ innerType: 'risk', riskData: null }); - return; - } - wx.showLoading({ title: '分析中...' }); - api - .assessSymptom(id, body) - .then((res) => { - wx.hideLoading(); - this.setData({ innerType: 'risk', riskData: res }); - }) - .catch(() => { - wx.hideLoading(); - this.setData({ innerType: 'risk', riskData: null }); - }); - }, - // 依据当前弹层类型组装一条健康记录 buildRecord() { const t = this.data.innerType; @@ -841,12 +817,17 @@ Component({ return { type: 'vaccine', icon: '💉', title: '疫苗提醒:' + this.data.dateVals.vaccine }; case 'deworm': return { type: 'deworm', icon: '🛡️', title: '驱虫提醒:' + this.data.dateVals.deworm }; - case 'risk': { - // 用 AI 返回的真实风险等级,而不是写死「中」 - const r = this.data.riskData || {}; - const lv = r.risk_level || '中'; - const desc = [r.causes, r.suggestion].filter(Boolean).join(';'); - return { type: 'symptom', icon: '🤒', title: '异常观察:风险' + lv, category: lv, description: desc }; + case 'symptom': { + // 原来这一支不落库:symptom 只是收集表单,真正建记录是在 AI 风险评估 + // 之后的 risk 那一步。AI 下掉后必须自己存,否则「记了异常但没存下来」。 + // + // category 存严重程度(高/中/低)——这是周报 high_risk_count 的依据 + // (report.go:46 按 category='高' 过滤)。原来这个值是 AI 给的, + // 现在改成用户自己选:谁看着它谁最清楚,比规则化猜一个准。 + const sy = SYMPTOMS[this.optIdx('symp')]; + const lv = SEVERITY[this.segIdx('sev')] || '中'; + const desc = ['持续' + DURATIONS[this.segIdx('dur')], '精神' + SPIRITS[this.segIdx('spirit')]].join(','); + return { type: 'symptom', icon: '🤒', title: '异常观察:' + sy, category: lv, description: desc }; } case 'vetSummary': return { type: 'note', icon: '📄', title: '生成就医前摘要' }; diff --git a/pets-fe/components/bottom-sheet/bottom-sheet.wxml b/pets-fe/components/bottom-sheet/bottom-sheet.wxml index 3064cf2..3034d95 100644 --- a/pets-fe/components/bottom-sheet/bottom-sheet.wxml +++ b/pets-fe/components/bottom-sheet/bottom-sheet.wxml @@ -133,7 +133,7 @@ module.exports.sel = function (map, key, index, def) { 异常观察 - 如实描述异常,AI 会给出观察建议与就医提示(非诊断)。 + 记下来,趋势和就医摘要都会用到。持续变差或伴随便血请尽快就医。 呕吐 拉稀 @@ -150,19 +150,14 @@ module.exports.sel = function (map, key, index, def) { 一般 明显变差 - - - - - - AI 观察建议 - 风险等级:{{riskData ? riskData.risk_level : '中'}} - 可能原因:{{riskData ? riskData.causes : '可能与换粮、应激或消化不适有关。不要把这里写成诊断结论。'}} - 建议:{{riskData ? riskData.suggestion : '继续观察精神、食欲和排便;暂停新食物;记录呕吐或腹泻次数。'}} - 建议就医:{{riskData ? riskData.seek_care : '如果持续超过 24 小时,或伴随便血、精神明显变差、频繁呕吐,建议尽快就医。'}} + + 轻微 + 需留意 + 严重 + - - + + @@ -258,6 +253,23 @@ module.exports.sel = function (map, key, index, def) { + + + 记一笔 + 选一件刚做完的事,几秒记完。 + + {{item.label}}{{item.items.length}} + + + {{t.label}} + + + + 还没有配置可记事项 + + 提醒中心 diff --git a/pets-fe/components/fab/fab.wxml b/pets-fe/components/fab/fab.wxml index d273590..4305ea7 100644 --- a/pets-fe/components/fab/fab.wxml +++ b/pets-fe/components/fab/fab.wxml @@ -1 +1 @@ - + diff --git a/pets-fe/pages/ai/ai.js b/pets-fe/pages/ai/ai.js deleted file mode 100644 index 5587fbb..0000000 --- a/pets-fe/pages/ai/ai.js +++ /dev/null @@ -1,114 +0,0 @@ -const store = require('../../utils/store.js'); -const api = require('../../utils/api.js'); -const { toastErr } = require('../../utils/ui.js'); - -const SESSION = 'chat'; -const PRESETS = ['猫咪今天吐了一次怎么办?', '幼猫多久驱虫一次?', '换粮后软便怎么办?', '疫苗打完要注意什么?']; - -// 打字机:每帧吐几个字。太快没效果,太慢让人等,30ms/2 字接近正常朗读速度。 -const TYPE_STEP = 2; -const TYPE_INTERVAL = 30; - -Page({ - data: { - pet: {}, - messages: [], - input: '', - presets: PRESETS, - sending: false, - loaded: false, - }, - onLoad() { - this._unsub = store.subscribe((pet) => this.setData({ pet })); - store - .ready() - .then(() => { - this.setData({ pet: store.getPet() }); - this.loadHistory(); - }) - .catch((e) => toastErr(e)); - }, - onUnload() { - if (this._unsub) this._unsub(); - this.stopTyping(); - }, - - loadHistory() { - const opening = { - role: 'ai', - text: '我是' + (store.getPet().name || '毛孩子') + '的养宠助手。我会优先根据档案、计划和历史记录回答,而不是泛泛聊天。', - }; - api - .aiMessages(SESSION, 50) - .then((list) => { - const history = (list || []).map((m) => ({ role: m.role === 'user' ? 'user' : 'ai', text: m.text })); - this.setData({ messages: [opening].concat(history), loaded: true }, () => this.scrollToBottom()); - }) - .catch(() => this.setData({ messages: [opening], loaded: true })); - }, - - onInput(e) { - this.setData({ input: e.detail.value }); - }, - - onSend(e) { - const preset = e.currentTarget && e.currentTarget.dataset.text; - const text = (preset || this.data.input || '').trim(); - if (!text || this.data.sending) return; - - // 先把用户消息和一个空的 AI 气泡放上去,AI 气泡随后逐字填充 - const messages = this.data.messages.concat([ - { role: 'user', text }, - { role: 'ai', text: '', pending: true }, - ]); - this.setData({ messages, input: '', sending: true }, () => this.scrollToBottom()); - - api - .aiChat({ pet_id: store.currentPetId() || null, session: SESSION, text }) - .then((res) => this.typewrite(res.reply || '(没有返回内容)')) - .catch((e) => { - const msg = (e && e.message) || '网络异常,请稍后再试。'; - // 额度用完不是故障,别让用户以为是网络问题反复重试 - this.typewrite(msg.indexOf('次数') >= 0 ? msg + '\n\n每天的次数是为了控制成本,明天 0 点恢复。' : msg); - }); - }, - - // 逐字把回复填进最后一个气泡 - typewrite(full) { - this.stopTyping(); - const idx = this.data.messages.length - 1; - let n = 0; - this._timer = setInterval(() => { - n = Math.min(full.length, n + TYPE_STEP); - this.setData({ [`messages[${idx}].text`]: full.slice(0, n) }); - if (n % 20 === 0) this.scrollToBottom(); - if (n >= full.length) { - this.stopTyping(); - this.setData({ [`messages[${idx}].pending`]: false, sending: false }, () => this.scrollToBottom()); - } - }, TYPE_INTERVAL); - }, - stopTyping() { - if (this._timer) { - clearInterval(this._timer); - this._timer = null; - } - }, - // 打字过程中点一下可以跳过,不用干等 - onSkip() { - if (!this.data.sending) return; - this.stopTyping(); - this.setData({ sending: false }); - }, - - scrollToBottom() { - this.setData({ scrollInto: 'chat-bottom' }); - }, - - goAIPlan() { - wx.navigateTo({ url: '/pages/plan/plan?tab=aiPlan' }); - }, - onShareAppMessage() { - return { title: '肉垫计划 · AI 养宠助手', path: '/pages/ai/ai' }; - }, -}); diff --git a/pets-fe/pages/ai/ai.json b/pets-fe/pages/ai/ai.json deleted file mode 100644 index 6594cd8..0000000 --- a/pets-fe/pages/ai/ai.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "usingComponents": { - "nav-bar": "/components/nav-bar/nav-bar", - "pt-icon": "/components/pt-icon/index" - } -} diff --git a/pets-fe/pages/ai/ai.wxml b/pets-fe/pages/ai/ai.wxml deleted file mode 100644 index 7fc9b17..0000000 --- a/pets-fe/pages/ai/ai.wxml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - {{item.text}} - - - - - - - - - - 生成养护计划 - {{item}} - - - - - - - - diff --git a/pets-fe/pages/ai/ai.wxss b/pets-fe/pages/ai/ai.wxss deleted file mode 100644 index 3fedd50..0000000 --- a/pets-fe/pages/ai/ai.wxss +++ /dev/null @@ -1,47 +0,0 @@ -/* 整页聊天:上面滚动区自适应,下面输入区固定,中间不留空 */ -.chat-scroll{flex:1;min-height:0} -.chat-body{padding:var(--sp-3) var(--pad-x) var(--sp-5)} -.chat-bottom{height:1rpx} - -.row{display:flex;align-items:flex-start;gap:var(--sp-2);margin-bottom:var(--sp-4)} -.row-user{justify-content:flex-end} -.ai-avatar{ - width:60rpx;height:60rpx;border-radius:var(--r-sm);flex:none;margin-top:4rpx; - display:flex;align-items:center;justify-content:center; - background:var(--cta);color:#fff; -} -.bubble{ - max-width:76%;border-radius:var(--r-md);padding:var(--sp-3) var(--sp-4); - font-size:var(--fs-md);line-height:1.62;word-break:break-word; -} -.bubble-ai{background:#fff;box-shadow:var(--sd-1);border-top-left-radius:var(--r-xs)} -.bubble-user{background:var(--primary-soft);color:var(--primary-ink);border-top-right-radius:var(--r-xs)} -/* 打字光标 */ -.caret{color:var(--primary);animation:blink .9s steps(1) infinite} -@keyframes blink{50%{opacity:0}} - -/* 底部输入区 */ -.dock{ - flex:none;background:rgba(255,255,255,.94); - backdrop-filter:blur(28rpx);-webkit-backdrop-filter:blur(28rpx); - border-top:1rpx solid var(--line); - padding:var(--sp-3) var(--pad-x) calc(var(--sp-3) + env(safe-area-inset-bottom)); -} -.presets{white-space:nowrap;margin-bottom:var(--sp-3)} -.preset{ - display:inline-block;height:var(--h-sm);line-height:var(--h-sm);padding:0 var(--sp-4); - margin-right:var(--sp-2);border-radius:var(--r-full);border:1rpx solid var(--line); - background:#fff;color:var(--text-2);font-size:var(--fs-sm); -} -.input-row{display:flex;align-items:center;gap:var(--sp-3)} -.input-row .input{flex:1;background:var(--surface-2)} -.send{ - width:88rpx;height:88rpx;border-radius:var(--r-md);flex:none; - display:flex;align-items:center;justify-content:center; - background:var(--line);color:#fff;transition:.16s ease; -} -.send.on{background:var(--cta)} -.preset-go{ - display:inline-flex;align-items:center;gap:6rpx; - background:var(--primary-soft);color:var(--primary-dark);border-color:var(--primary-line); -} diff --git a/pets-fe/pages/community/community.js b/pets-fe/pages/community/community.js index 9ae2c2d..001371b 100644 --- a/pets-fe/pages/community/community.js +++ b/pets-fe/pages/community/community.js @@ -128,8 +128,9 @@ Page({ goLearn() { wx.navigateTo({ url: '/pages/learn/learn' }); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); + // 社区的悬浮键本来就该是发帖,不是记录(原来它跳 AI 聊天页) + onCreatePost() { + this.setData({ sheetType: 'createPost', sheetShow: true }); }, // 长按自己的帖子可删除 onLongPressPost(e) { diff --git a/pets-fe/pages/community/community.wxml b/pets-fe/pages/community/community.wxml index 780bfd5..a25c0ce 100644 --- a/pets-fe/pages/community/community.wxml +++ b/pets-fe/pages/community/community.wxml @@ -57,6 +57,6 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; } - + diff --git a/pets-fe/pages/home/home.js b/pets-fe/pages/home/home.js index 36853e3..8a15ae3 100644 --- a/pets-fe/pages/home/home.js +++ b/pets-fe/pages/home/home.js @@ -298,8 +298,9 @@ Page({ onEditPet() { this.openSheetType('editPet'); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); + // 右下角悬浮键:打开 24 项记录选择器(原来是跳 AI 聊天页) + onQuickRecord() { + this.openSheetType('quickRecord'); }, goSettings() { wx.navigateTo({ url: '/pages/settings/settings' }); diff --git a/pets-fe/pages/home/home.wxml b/pets-fe/pages/home/home.wxml index 3432c19..5fe9ea9 100644 --- a/pets-fe/pages/home/home.wxml +++ b/pets-fe/pages/home/home.wxml @@ -117,16 +117,6 @@ - - - AI 今日建议 - - {{summary.advice || '正在生成今日建议…'}} - - 问问 AI 养宠助手 - - - 新手内容 @@ -140,5 +130,5 @@ - + diff --git a/pets-fe/pages/learn/learn.js b/pets-fe/pages/learn/learn.js index ddec272..c8c500e 100644 --- a/pets-fe/pages/learn/learn.js +++ b/pets-fe/pages/learn/learn.js @@ -34,9 +34,6 @@ Page({ closeSheet() { this.setData({ sheetShow: false }); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { return { title: '新手养宠知识合集', path: '/pages/learn/learn' }; }, diff --git a/pets-fe/pages/learn/learn.wxml b/pets-fe/pages/learn/learn.wxml index a4e3dff..767741a 100644 --- a/pets-fe/pages/learn/learn.wxml +++ b/pets-fe/pages/learn/learn.wxml @@ -1,7 +1,7 @@ - + 看完顺手生成你的计划。 @@ -14,5 +14,4 @@ - diff --git a/pets-fe/pages/mine/mine.js b/pets-fe/pages/mine/mine.js index af5b44d..b1634aa 100644 --- a/pets-fe/pages/mine/mine.js +++ b/pets-fe/pages/mine/mine.js @@ -69,9 +69,6 @@ Page({ onAddPet() { this.setData({ sheetType: 'addPet', sheetShow: true }); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { return { title: '肉垫计划 · 每天照顾好毛孩子', path: '/pages/home/home' }; }, diff --git a/pets-fe/pages/mine/mine.wxml b/pets-fe/pages/mine/mine.wxml index 173558c..88b3833 100644 --- a/pets-fe/pages/mine/mine.wxml +++ b/pets-fe/pages/mine/mine.wxml @@ -1,7 +1,7 @@ - + @@ -50,5 +50,4 @@ - diff --git a/pets-fe/pages/onboarding/onboarding.wxml b/pets-fe/pages/onboarding/onboarding.wxml index ebbc0ba..a798945 100644 --- a/pets-fe/pages/onboarding/onboarding.wxml +++ b/pets-fe/pages/onboarding/onboarding.wxml @@ -7,7 +7,7 @@ 肉垫计划 - 新手养宠的 AI 日历、健康记录和成长报告。先生成计划,再每天照着做。 + 新手养宠的养护日历、健康记录和成长报告。先生成计划,再每天照着做。 30天新手计划 diff --git a/pets-fe/pages/plan/plan.js b/pets-fe/pages/plan/plan.js index de92fdb..7bd335f 100644 --- a/pets-fe/pages/plan/plan.js +++ b/pets-fe/pages/plan/plan.js @@ -14,19 +14,11 @@ Page({ data: { pet: {}, planView: 'timeline', - planTabs: [ - { key: 'timeline', label: '路线图' }, - { key: 'aiPlan', label: 'AI 计划' }, - ], plan: null, - aiInput: '', - aiPlan: null, - aiBusy: false, sheetShow: false, sheetType: '', }, - onLoad(options) { - if (options && options.tab === 'aiPlan') this.setData({ planView: 'aiPlan' }); + onLoad() { this._unsub = store.subscribe((pet) => { this.setData({ pet }); if (!this._inited) return; // 首次由 onShow 加载 @@ -59,48 +51,10 @@ Page({ }) .catch(() => this.setData({ plan: null })); }, - // 来自 seg-tabs 的 change 事件,key 即 planView - switchPlanView(e) { - this.setData({ planView: e.detail.key }); - }, - // 计划明细勾选 toggleTimelineTask(e) { const id = e.currentTarget.dataset.id; api.togglePlanTask(id).then(() => this.loadTimeline()).catch((e) => toastErr(e, '操作失败')); }, - // AI 计划 - onAiInput(e) { - this.setData({ aiInput: e.detail.value }); - }, - genAIPlan() { - const id = store.currentPetId(); - const input = (this.data.aiInput || '').trim(); - if (!id || !input) { - wx.showToast({ title: '先描述一下情况', icon: 'none' }); - return; - } - this.setData({ aiBusy: true }); - wx.showLoading({ title: 'AI 生成中...' }); - api - .createAIPlan(id, input) - .then((plan) => { - wx.hideLoading(); - this.setData({ aiPlan: plan, aiBusy: false }); - }) - .catch((err) => { - wx.hideLoading(); - this.setData({ aiBusy: false }); - wx.showToast({ title: err.message || '生成失败', icon: 'none' }); - }); - }, - applyAIPlan() { - const p = this.data.aiPlan; - if (!p) return; - api - .applyAIPlan(p.id) - .then(() => wx.showToast({ title: '已加入我的计划', icon: 'success' })) - .catch((err) => wx.showToast({ title: err.message || '加入失败', icon: 'none' })); - }, openSheet(e) { const type = e.currentTarget.dataset.type; if (!type) return; // 无关联动作的计划项只可勾选,不弹层 @@ -112,9 +66,6 @@ Page({ onAddPet() { this.setData({ sheetType: 'addPet', sheetShow: true }); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { return { title: '我给毛孩子做了份养护计划', path: '/pages/plan/plan' }; }, diff --git a/pets-fe/pages/plan/plan.wxml b/pets-fe/pages/plan/plan.wxml index 225d1af..e3bde74 100644 --- a/pets-fe/pages/plan/plan.wxml +++ b/pets-fe/pages/plan/plan.wxml @@ -3,7 +3,7 @@ - + 还没有毛孩子的档案 @@ -11,8 +11,6 @@ 建一份档案 - - @@ -37,34 +35,7 @@ - - - AI 计划模式Plan - 描述 {{pet.name}} 的近况(阶段、近期变化、担心的问题),AI 会提取关键信息并生成可执行计划。 - - - - - - 已提取信息 - - 阶段{{aiPlan.extracted.stage}} - 风险{{aiPlan.extracted.risk}} - 重点{{aiPlan.extracted.priority}} - 提醒{{aiPlan.extracted.reminder}} - - 生成的计划 - - - - {{item.day_label}}· {{item.title}}{{item.description}} - - - - - - diff --git a/pets-fe/pages/record/record.js b/pets-fe/pages/record/record.js index edfa693..9919a0c 100644 --- a/pets-fe/pages/record/record.js +++ b/pets-fe/pages/record/record.js @@ -265,9 +265,6 @@ Page({ onAddPet() { this.openSheetType('addPet'); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { return { title: '用肉垫计划记录毛孩子的成长', path: '/pages/record/record' }; }, diff --git a/pets-fe/pages/record/record.wxml b/pets-fe/pages/record/record.wxml index 133fc76..9a69837 100644 --- a/pets-fe/pages/record/record.wxml +++ b/pets-fe/pages/record/record.wxml @@ -3,7 +3,7 @@ - + 还没有毛孩子的档案 @@ -88,5 +88,4 @@ - diff --git a/pets-fe/pages/record/record.wxss b/pets-fe/pages/record/record.wxss index 4c33bec..25c7996 100644 --- a/pets-fe/pages/record/record.wxss +++ b/pets-fe/pages/record/record.wxss @@ -57,19 +57,3 @@ .ins-title{flex:1;font-size:var(--fs-md);font-weight:var(--fw-b);line-height:1.4} .ins-detail{color:var(--text-2);font-size:var(--fs-sm);line-height:1.6;margin-top:var(--sp-2)} .ins-ev{color:var(--muted);font-size:var(--fs-cap);margin-top:var(--sp-2)} - -/* ===== 记一笔的分组 ===== - 四组各一个色系,色值全走 token 的 soft/ink 二件套,没有新色。 - .quick 的配色由 .tone-N 决定(app.wxss 里已有),这里只管组标题和留白 */ -.tg{margin-bottom:var(--sp-4)} -.tg:last-of-type{margin-bottom:0} -.tg-head{ - display:flex;align-items:center;gap:var(--sp-2); - font-size:var(--fs-md);font-weight:var(--fw-b);margin-bottom:var(--sp-3); -} -.tg-bar{width:6rpx;height:26rpx;border-radius:3rpx;background:currentColor} -.tg-n{margin-left:auto;font-size:var(--fs-cap);font-weight:var(--fw);color:var(--muted2)} -.tg.tone-1 .tg-head{color:var(--primary-ink)} -.tg.tone-2 .tg-head{color:var(--green-ink)} -.tg.tone-3 .tg-head{color:var(--blue-ink)} -.tg.tone-4 .tg-head{color:var(--purple-ink)} diff --git a/pets-fe/pages/report/report.js b/pets-fe/pages/report/report.js index 52e7c7f..2a6f904 100644 --- a/pets-fe/pages/report/report.js +++ b/pets-fe/pages/report/report.js @@ -57,9 +57,6 @@ Page({ onAddPet() { this.setData({ sheetType: 'addPet', sheetShow: true }); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { // 带上宠物名,转发出去别人一眼知道是谁家的 const n = (this.data.pet && this.data.pet.name) || '我家毛孩子'; diff --git a/pets-fe/pages/report/report.wxml b/pets-fe/pages/report/report.wxml index 3bf9647..d6d63c4 100644 --- a/pets-fe/pages/report/report.wxml +++ b/pets-fe/pages/report/report.wxml @@ -3,7 +3,7 @@ - + 还没有毛孩子的档案 @@ -57,5 +57,4 @@ - diff --git a/pets-fe/pages/settings/settings.js b/pets-fe/pages/settings/settings.js index 50c989b..e477c81 100644 --- a/pets-fe/pages/settings/settings.js +++ b/pets-fe/pages/settings/settings.js @@ -73,9 +73,6 @@ Page({ this.setData({ sheetShow: false }); this.loadSummary(); }, - onFab() { - wx.navigateTo({ url: '/pages/ai/ai' }); - }, onShareAppMessage() { return { title: '肉垫计划 · 新手养宠助手', path: '/pages/home/home' }; }, diff --git a/pets-fe/utils/api.js b/pets-fe/utils/api.js index 8c654fb..7027e2a 100644 --- a/pets-fe/utils/api.js +++ b/pets-fe/utils/api.js @@ -85,8 +85,10 @@ const api = { planCalendar: (id, month) => request({ url: `/api/pets/${id}/plan/calendar${month ? `?month=${month}` : ''}` }), dayPlan: (id, date) => request({ url: `/api/pets/${id}/day-plan?date=${date}` }), - createAIPlan: (id, input) => request({ url: `/api/pets/${id}/ai-plan`, method: 'POST', data: { input } }), - applyAIPlan: (planId) => request({ url: `/api/ai-plan/${planId}/apply`, method: 'POST' }), + // AI 相关:用户端入口已全部下掉,后端 10 个路由和配额配置都还在。 + // 想开回来把这几行解注释、再把入口接上就行,不用重写。 + // createAIPlan: (id, input) => request({ url: `/api/pets/${id}/ai-plan`, method: 'POST', data: { input } }), + // applyAIPlan: (planId) => request({ url: `/api/ai-plan/${planId}/apply`, method: 'POST' }), togglePlanTask: (taskId) => request({ url: `/api/plan-tasks/${taskId}/toggle`, method: 'POST' }), // 提醒 @@ -133,11 +135,11 @@ const api = { activatePro: () => request({ url: '/api/pro/activate', method: 'POST' }), // AI - aiMessages: (session, limit) => - request({ url: `/api/ai/messages?session=${session || ''}&limit=${limit || 30}` }), - aiChat: (body) => request({ url: '/api/ai/chat', method: 'POST', data: body }), - assessSymptom: (id, body) => - request({ url: `/api/pets/${id}/ai/assess-symptom`, method: 'POST', data: body }), + // aiMessages: (session, limit) => + // request({ url: `/api/ai/messages?session=${session || ''}&limit=${limit || 30}` }), + // aiChat: (body) => request({ url: '/api/ai/chat', method: 'POST', data: body }), + // assessSymptom: (id, body) => + // request({ url: `/api/pets/${id}/ai/assess-symptom`, method: 'POST', data: body }), }; module.exports = api;