From 1584aabcc5d1bebb7e1b6835f3ab4595068f2d39 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Wed, 29 Jul 2026 18:02:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(fe):=20Phase=20D=20=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E4=B8=BB=E9=A1=B5=E8=A3=85=E6=89=AE=EF=BC=8C=E3=80=8C=E6=88=91?= =?UTF-8?q?=E7=9A=84=E3=80=8D=E5=92=8C=E3=80=8C=E5=AE=A0=E5=8F=8B=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E3=80=8D=E5=85=B1=E7=94=A8=E4=B8=80=E5=A5=97=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## profile-head 组件 头图 / 头像 / 昵称 / 签名 / 帖子关注粉丝 / 养宠数据 / 宠物名片墙, pages/mine 和 pages/user 用的是同一个组件、同一个接口 (GET /users/:id/profile)。 这件事必须一开始就做对:自己看和别人看要是两套渲染,会慢慢长歪, 装扮功能也就失去意义了——你调半天颜色,结果只有自己那页变了。 两边唯一的差别是底部按钮:自己看是「装扮我的主页」,别人看是「关注 TA」。 顺带把 mine 的头部从 /user/summary 切到了 /users/:id/profile。原来两边 字段对不上,装扮效果在「我的」页根本预览不到。 主题只在组件 wxss 里定义 --pf-a/--pf-b/--pf-ink 三个变量,下面所有着色 都走变量。后端存 key 不存色值,就是为了这套配色随时能在前端改。 ## pages/decorate 装扮页 头图上传 / 五套主题色 / 个性签名 60 字 / 宠物墙开关,顶部实时预览。 预览用的就是 profile-head 本身,不是仿一个——仿的迟早会和真的长得不一样。 喂给它一份改过的 card 就行,组件那边不需要知道自己在被预览。 头图有个坑单独处理了:bg_file_id 只在真动过的时候才传。新上传带新 id, 点「恢复默认渐变」带空串,都没动就不传字段。否则「只改个主题色」会把 已有头图冲掉。上传时先用本地临时路径顶上,不让用户对着转圈等。 联调验证(本地 9090,真上传了一张图走完存储链路): 上传 → file id + MinIO url 保存装扮 → bg_url 落库,主题 violet,签名落库 主页读回 → bio/theme/show_pets/bg_url/宠物墙/养宠数据 全对 只改主题 → theme 变 sky,头图还在(没被冲掉) 恢复默认渐变 → bg_url 清空,theme 和 bio 都没被动 别人视角 → is_self=false、followed=false,装扮和宠物墙都能看到 Co-Authored-By: Claude Opus 5 --- pets-fe/app.json | 1 + pets-fe/components/profile-head/index.js | 36 ++++++ pets-fe/components/profile-head/index.json | 6 + pets-fe/components/profile-head/index.wxml | 54 +++++++++ pets-fe/components/profile-head/index.wxss | 59 ++++++++++ pets-fe/pages/decorate/decorate.js | 129 +++++++++++++++++++++ pets-fe/pages/decorate/decorate.json | 8 ++ pets-fe/pages/decorate/decorate.wxml | 53 +++++++++ pets-fe/pages/decorate/decorate.wxss | 30 +++++ pets-fe/pages/mine/mine.js | 31 ++--- pets-fe/pages/mine/mine.json | 3 +- pets-fe/pages/mine/mine.wxml | 35 +----- pets-fe/pages/mine/mine.wxss | 18 +-- pets-fe/pages/user/user.js | 21 ++-- pets-fe/pages/user/user.json | 3 +- pets-fe/pages/user/user.wxml | 24 +--- pets-fe/pages/user/user.wxss | 14 +-- pets-fe/utils/api.js | 1 + 18 files changed, 426 insertions(+), 100 deletions(-) create mode 100644 pets-fe/components/profile-head/index.js create mode 100644 pets-fe/components/profile-head/index.json create mode 100644 pets-fe/components/profile-head/index.wxml create mode 100644 pets-fe/components/profile-head/index.wxss create mode 100644 pets-fe/pages/decorate/decorate.js create mode 100644 pets-fe/pages/decorate/decorate.json create mode 100644 pets-fe/pages/decorate/decorate.wxml create mode 100644 pets-fe/pages/decorate/decorate.wxss diff --git a/pets-fe/app.json b/pets-fe/app.json index 9a51c22..1ab3921 100644 --- a/pets-fe/app.json +++ b/pets-fe/app.json @@ -9,6 +9,7 @@ "pages/learn/learn", "pages/article/article", "pages/mine/mine", + "pages/decorate/decorate", "pages/settings/settings", "pages/ai/ai", "pages/user/user", diff --git a/pets-fe/components/profile-head/index.js b/pets-fe/components/profile-head/index.js new file mode 100644 index 0000000..e02950b --- /dev/null +++ b/pets-fe/components/profile-head/index.js @@ -0,0 +1,36 @@ +// 个人主页头部。「我的」和「宠友主页」共用同一份渲染——自己看和别人看 +// 长得不一样的话,两边会慢慢长歪,装扮功能也就失去意义了。 +// 差别只有一处:自己看是「装扮主页」,别人看是「关注 TA」。 +Component({ + options: { addGlobalClass: true }, + properties: { + card: { type: Object, value: null }, + // 装扮页的实时预览:只看效果,不要底部那排按钮 + preview: { type: Boolean, value: false }, + }, + data: { initial: '', theme: 'warm' }, + observers: { + card(c) { + if (!c) return; + this.setData({ + // WXML 取不了字符串下标,首字得在 JS 里算 + initial: (c.nickname || '?').slice(0, 1), + theme: c.theme || 'warm', + }); + }, + }, + methods: { + onFollow() { + this.triggerEvent('follow'); + }, + onDecorate() { + this.triggerEvent('decorate'); + }, + onRelations(e) { + this.triggerEvent('relations', { kind: e.currentTarget.dataset.kind }); + }, + onPet(e) { + this.triggerEvent('pet', { id: e.currentTarget.dataset.id }); + }, + }, +}); diff --git a/pets-fe/components/profile-head/index.json b/pets-fe/components/profile-head/index.json new file mode 100644 index 0000000..a88211e --- /dev/null +++ b/pets-fe/components/profile-head/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "pt-icon": "/components/pt-icon/index" + } +} diff --git a/pets-fe/components/profile-head/index.wxml b/pets-fe/components/profile-head/index.wxml new file mode 100644 index 0000000..4507357 --- /dev/null +++ b/pets-fe/components/profile-head/index.wxml @@ -0,0 +1,54 @@ + + + + + + + + + + {{initial}} + + {{card.nickname || '毛孩子家长'}}AI + {{card.bio || '这个人很懒,还没写签名'}} + + + {{card.post_count || 0}}帖子 + + {{card.following_count || 0}}关注 + + + {{card.follower_count || 0}}粉丝 + + + + + + {{card.stats.days || 0}}养宠天数 + {{card.stats.records || 0}}累计记录 + {{card.stats.streak || 0}}连续打卡 + + + + + + + + {{item.emoji}} + + {{item.name}} + {{item.stage}} + + + + + + 装扮我的主页 + + + {{card.followed ? '已关注' : '关注 TA'}} + + + + diff --git a/pets-fe/components/profile-head/index.wxss b/pets-fe/components/profile-head/index.wxss new file mode 100644 index 0000000..85c6657 --- /dev/null +++ b/pets-fe/components/profile-head/index.wxss @@ -0,0 +1,59 @@ +/* 主题只在这里定义两个变量,下面所有着色都走变量。 + 后端存的是 theme key 不是色值,就是为了这套配色能随时在前端改。 */ +.pf{--pf-a:#F5A947;--pf-b:#FFE6BA;--pf-ink:#8A5A18} +.th-warm{--pf-a:#F5A947;--pf-b:#FFE6BA;--pf-ink:#8A5A18} +.th-mint{--pf-a:#3FB98A;--pf-b:#D7F3E7;--pf-ink:#1F6B4E} +.th-sky{--pf-a:#4A9BE0;--pf-b:#D8ECFB;--pf-ink:#245F94} +.th-violet{--pf-a:#9B7BE0;--pf-b:#E8DFFA;--pf-ink:#5B3F9B} +.th-ink{--pf-a:#5A5550;--pf-b:#E4DED7;--pf-ink:#2D2925} + +.pf{ + background:#fff;border-radius:var(--r-lg);overflow:hidden; + box-shadow:var(--sd-1);margin-bottom:var(--sp-5); +} +.pf-bg{height:220rpx;background:linear-gradient(135deg,var(--pf-a),var(--pf-b));position:relative} +.pf-bg-img{width:100%;height:100%;display:block} + +.pf-body{padding:0 var(--sp-5) var(--sp-5);text-align:center} +/* 头像压在头图上,负 margin 提上去一半 */ +.pf-av{ + width:144rpx;height:144rpx;margin:-72rpx auto var(--sp-3); + border-radius:50%;background:var(--pf-b);color:var(--pf-ink); + display:flex;align-items:center;justify-content:center; + font-size:var(--fs-xl);font-weight:var(--fw-b); + border:6rpx solid #fff;overflow:hidden;box-shadow:var(--sd-1); +} +.pf-av-img{width:100%;height:100%;display:block} +.pf-name{font-size:var(--fs-lg);font-weight:var(--fw-b);color:var(--text)} +.pf-bio{ + font-size:var(--fs-sm);color:var(--muted);margin-top:6rpx; + line-height:1.5;word-break:break-all; +} + +.pf-stats{display:flex;margin:var(--sp-4) 0} +.pf-stat{flex:1;display:flex;flex-direction:column;align-items:center;gap:4rpx} +.pf-n{font-size:var(--fs-lg);font-weight:var(--fw-b);color:var(--text)} +.pf-l{font-size:var(--fs-cap);color:var(--muted)} + +.pf-keep{ + display:flex;background:var(--pf-b);border-radius:var(--r-md); + padding:var(--sp-3) 0;margin-bottom:var(--sp-4); +} +.pf-keep-i{flex:1;display:flex;flex-direction:column;align-items:center;gap:2rpx} +.pf-kn{font-size:var(--fs-md);font-weight:var(--fw-b);color:var(--pf-ink)} +.pf-kl{font-size:var(--fs-cap);color:var(--pf-ink);opacity:.75} + +.pf-wall{white-space:nowrap;margin-bottom:var(--sp-4)} +.pf-pet{ + display:inline-block;width:150rpx;vertical-align:top; + margin-right:var(--sp-3);white-space:normal; +} +.pf-pet:last-child{margin-right:0} +.pf-pet-av{ + width:112rpx;height:112rpx;margin:0 auto 8rpx;border-radius:var(--r-md); + background:var(--surface-2);display:flex;align-items:center;justify-content:center; + font-size:52rpx;overflow:hidden; +} +.pf-pet-img{width:100%;height:100%;display:block} +.pf-pet-n{font-size:var(--fs-cap);font-weight:var(--fw-b);color:var(--text);text-align:center} +.pf-pet-s{font-size:var(--fs-cap);color:var(--muted);text-align:center} diff --git a/pets-fe/pages/decorate/decorate.js b/pets-fe/pages/decorate/decorate.js new file mode 100644 index 0000000..c2680a8 --- /dev/null +++ b/pets-fe/pages/decorate/decorate.js @@ -0,0 +1,129 @@ +const store = require('../../utils/store.js'); +const api = require('../../utils/api.js'); +const { toastErr } = require('../../utils/ui.js'); + +// key 要和后端 service.UserThemes 一致,色值只在 profile-head 的 wxss 里 +const THEMES = [ + { key: 'warm', label: '暖橙' }, + { key: 'mint', label: '薄荷' }, + { key: 'sky', label: '天青' }, + { key: 'violet', label: '藕紫' }, + { key: 'ink', label: '墨灰' }, +]; + +Page({ + data: { + themes: THEMES, + form: { bio: '', bg_url: '', bg_file_id: '', theme: 'warm', show_pets: true }, + bioLen: 0, + preview: null, + saving: false, + }, + onLoad() { + store + .ready() + .then(() => this.loadCard()) + .catch((e) => toastErr(e)); + }, + loadCard() { + const uid = (store.getUser() || {}).id; + if (!uid) return; + api + .userCard(uid) + .then((card) => { + this.setData({ + form: { + bio: card.bio || '', + bg_url: card.bg_url || '', + bg_file_id: '', // 没重新上传就不带这个字段,避免把已有头图覆盖成空 + theme: card.theme || 'warm', + show_pets: card.show_pets !== false, + }, + bioLen: (card.bio || '').length, + }); + this._card = card; + this.syncPreview(); + }) + .catch((e) => toastErr(e)); + }, + // 预览喂给 profile-head 的就是一份改过的 card,组件那边不用知道自己在被预览 + syncPreview() { + const f = this.data.form; + this.setData({ + preview: Object.assign({}, this._card || {}, { + bio: f.bio, + bg_url: f.bg_url, + theme: f.theme, + show_pets: f.show_pets, + }), + }); + }, + pickBg() { + wx.chooseMedia({ + count: 1, + mediaType: ['image'], + sizeType: ['compressed'], + success: (res) => { + const path = res.tempFiles[0].tempFilePath; + // 先用本地路径顶上,别让用户对着转圈等上传 + this.setData({ 'form.bg_url': path }); + this.syncPreview(); + wx.showLoading({ title: '上传中…' }); + api + .uploadFile(path) + .then((file) => { + wx.hideLoading(); + this.setData({ 'form.bg_file_id': file.id, 'form.bg_url': file.url }); + this.syncPreview(); + }) + .catch((e) => { + wx.hideLoading(); + this.setData({ 'form.bg_url': (this._card && this._card.bg_url) || '' }); + this.syncPreview(); + toastErr(e, '上传失败'); + }); + }, + }); + }, + clearBg() { + // 空字符串是「清掉头图」的显式信号,和「没传」不是一回事 + this.setData({ 'form.bg_url': '', 'form.bg_file_id': '' }); + this.syncPreview(); + }, + pickTheme(e) { + this.setData({ 'form.theme': e.currentTarget.dataset.key }); + this.syncPreview(); + }, + onBio(e) { + this.setData({ 'form.bio': e.detail.value, bioLen: e.detail.value.length }); + this.syncPreview(); + }, + onShowPets(e) { + this.setData({ 'form.show_pets': e.detail.value }); + this.syncPreview(); + }, + save() { + if (this.data.saving) return; + const f = this.data.form; + const body = { bio: f.bio, theme: f.theme, show_pets: f.show_pets }; + // 只有真的动过头图才带 bg_file_id:新上传带新 id,点了「恢复默认渐变」带空串, + // 两者都没有就不传,让后端保持原样 + const had = (this._card && this._card.bg_url) || ''; + if (f.bg_file_id) body.bg_file_id = f.bg_file_id; + else if (had && !f.bg_url) body.bg_file_id = ''; + + this.setData({ saving: true }); + api + .updateDecoration(body) + .then((user) => { + this.setData({ saving: false }); + store.setUser(user); // 返回的就是改完的 user,别再多请求一次 + wx.showToast({ title: '已保存', icon: 'success' }); + setTimeout(() => wx.navigateBack(), 600); + }) + .catch((e) => { + this.setData({ saving: false }); + toastErr(e, '保存失败'); + }); + }, +}); diff --git a/pets-fe/pages/decorate/decorate.json b/pets-fe/pages/decorate/decorate.json new file mode 100644 index 0000000..3151e63 --- /dev/null +++ b/pets-fe/pages/decorate/decorate.json @@ -0,0 +1,8 @@ +{ + "usingComponents": { + "nav-bar": "/components/nav-bar/nav-bar", + "pt-icon": "/components/pt-icon/index", + "profile-head": "/components/profile-head/index" + }, + "navigationBarTitleText": "装扮主页" +} diff --git a/pets-fe/pages/decorate/decorate.wxml b/pets-fe/pages/decorate/decorate.wxml new file mode 100644 index 0000000..d2c9529 --- /dev/null +++ b/pets-fe/pages/decorate/decorate.wxml @@ -0,0 +1,53 @@ + + + + + + + + + 头图 + + + + + 点这里换一张头图 + + + 恢复默认渐变 + + + + 主题色 + + + + {{item.label}} + + + + + + + 个性签名 + {{bioLen}}/60 + + + + + + + + 展示我的毛孩子 + 关掉后别人访问你的主页看不到宠物名片墙 + + + + + + {{saving ? '保存中…' : '保存'}} + + diff --git a/pets-fe/pages/decorate/decorate.wxss b/pets-fe/pages/decorate/decorate.wxss new file mode 100644 index 0000000..65ef28e --- /dev/null +++ b/pets-fe/pages/decorate/decorate.wxss @@ -0,0 +1,30 @@ +.dc-bg{ + height:240rpx;border-radius:var(--r-md);overflow:hidden; + background:var(--surface-2);display:flex;align-items:center;justify-content:center; +} +.dc-bg-img{width:100%;height:100%;display:block} +.dc-bg-empty{display:flex;flex-direction:column;align-items:center;gap:var(--sp-2);color:var(--muted)} +.dc-hint{font-size:var(--fs-cap)} +.dc-clear{margin-top:var(--sp-3);text-align:center} + +.dc-themes{display:flex;gap:var(--sp-3)} +.dc-theme{ + flex:1;display:flex;flex-direction:column;align-items:center;gap:8rpx; + padding:var(--sp-3) 0;border-radius:var(--r-sm);border:2rpx solid transparent; +} +.dc-theme.on{border-color:var(--primary);background:var(--primary-soft)} +.dc-swatch{width:64rpx;height:64rpx;border-radius:50%;box-shadow:var(--sd-1)} +.dc-tname{font-size:var(--fs-cap);color:var(--muted)} +.dc-theme.on .dc-tname{color:var(--primary-dark);font-weight:var(--fw-b)} + +/* 色值和 profile-head 的主题保持一致,改那边这里也要跟着改 */ +.sw-warm{background:linear-gradient(135deg,#F5A947,#FFE6BA)} +.sw-mint{background:linear-gradient(135deg,#3FB98A,#D7F3E7)} +.sw-sky{background:linear-gradient(135deg,#4A9BE0,#D8ECFB)} +.sw-violet{background:linear-gradient(135deg,#9B7BE0,#E8DFFA)} +.sw-ink{background:linear-gradient(135deg,#5A5550,#E4DED7)} + +.dc-count{font-size:var(--fs-cap);color:var(--muted)} + +.dc-row{display:flex;align-items:center;gap:var(--sp-4)} +.dc-row-main{flex:1;min-width:0} diff --git a/pets-fe/pages/mine/mine.js b/pets-fe/pages/mine/mine.js index 7add4db..5e86611 100644 --- a/pets-fe/pages/mine/mine.js +++ b/pets-fe/pages/mine/mine.js @@ -3,7 +3,7 @@ const api = require('../../utils/api.js'); const { toastErr } = require('../../utils/ui.js'); Page({ - data: { user: {}, pets: [], summary: {}, initial: '', sheetShow: false, sheetType: '' }, + data: { user: {}, pets: [], card: null, sheetShow: false, sheetType: '' }, onLoad() { this._unsub = store.subscribe(() => this.applyUser()); }, @@ -15,19 +15,21 @@ Page({ .ready() .then(() => { this.applyUser(); - this.loadSummary(); + this.loadCard(); }) .catch((e) => toastErr(e)); }, applyUser() { - const user = store.getUser() || {}; - // WXML 取不了字符串下标,首字在这里算 - this.setData({ pets: store.getPets(), user, initial: (user.nickname || '?').slice(0, 1) }); + this.setData({ pets: store.getPets(), user: store.getUser() || {} }); }, - loadSummary() { + // 头部数据走 /users/:id/profile,和别人看我时是同一个接口—— + // 换成 /user/summary 的话两边字段会对不上,装扮效果也预览不到 + loadCard() { + const uid = (this.data.user && this.data.user.id) || ''; + if (!uid) return; api - .userSummary() - .then((summary) => this.setData({ summary })) + .userCard(uid) + .then((card) => this.setData({ card })) .catch(() => {}); }, // 看自己的公开主页,和别人看到的是同一个页面、同一套渲染 @@ -41,16 +43,19 @@ Page({ goRelations(e) { const uid = (this.data.user && this.data.user.id) || ''; if (!uid) return wx.showToast({ title: '登录信息还没就绪', icon: 'none' }); - wx.navigateTo({ url: `/pages/relations/relations?id=${uid}&kind=${e.currentTarget.dataset.kind}` }); + wx.navigateTo({ url: `/pages/relations/relations?id=${uid}&kind=${e.detail.kind}` }); + }, + goDecorate() { + wx.navigateTo({ url: '/pages/decorate/decorate' }); }, goPlan() { wx.navigateTo({ url: '/pages/plan/plan' }); }, - goRecord() { - wx.switchTab({ url: '/pages/record/record' }); - }, + // 列表行传 dataset,宠物名片墙传 detail,两边都进这里 onPickPet(e) { - store.switchPet(e.currentTarget.dataset.id); + const id = (e.detail && e.detail.id) || e.currentTarget.dataset.id; + if (!id) return; + store.switchPet(id); wx.switchTab({ url: '/pages/home/home' }); }, openSheet(e) { diff --git a/pets-fe/pages/mine/mine.json b/pets-fe/pages/mine/mine.json index fa34741..1f40594 100644 --- a/pets-fe/pages/mine/mine.json +++ b/pets-fe/pages/mine/mine.json @@ -3,6 +3,7 @@ "nav-bar": "/components/nav-bar/nav-bar", "bottom-sheet": "/components/bottom-sheet/bottom-sheet", "fab": "/components/fab/fab", - "pt-icon": "/components/pt-icon/index" + "pt-icon": "/components/pt-icon/index", + "profile-head": "/components/profile-head/index" } } diff --git a/pets-fe/pages/mine/mine.wxml b/pets-fe/pages/mine/mine.wxml index 503356e..173558c 100644 --- a/pets-fe/pages/mine/mine.wxml +++ b/pets-fe/pages/mine/mine.wxml @@ -2,35 +2,12 @@ - - - - - - {{initial}} - - - {{user.nickname || '毛孩子家长'}} - {{user.bio || '还没有个性签名'}} - - - + + - - - {{summary.following || 0}}关注 - - - {{summary.followers || 0}}粉丝 - - - {{summary.records || 0}}记录 - - - - - 看看别人眼里的我 - + + 看看别人眼里的我 @@ -62,7 +39,7 @@ - 多宠物管理{{summary.pets || 0}} 只 + 多宠物管理{{card.stats.pet_count || 0}} 只 diff --git a/pets-fe/pages/mine/mine.wxss b/pets-fe/pages/mine/mine.wxss index f299eed..18f8a24 100644 --- a/pets-fe/pages/mine/mine.wxss +++ b/pets-fe/pages/mine/mine.wxss @@ -1,19 +1,3 @@ -.me-card{padding:var(--sp-5)} -.me-top{display:flex;align-items:center;gap:var(--sp-3)} -.me-av{ - width:110rpx;height:110rpx;border-radius:50%;flex:none;overflow:hidden; - display:flex;align-items:center;justify-content:center; - background:var(--primary-soft);color:var(--primary-ink);font-size:44rpx;font-weight:var(--fw-b); -} -.me-av-img{width:100%;height:100%} -.me-main{flex:1;min-width:0} -.me-name{font-size:var(--fs-lg);font-weight:var(--fw-b)} -.me-bio{color:var(--muted);font-size:var(--fs-sm);margin-top:6rpx} -.me-gear{flex:none;color:var(--muted)} -.me-stats{display:flex;margin:var(--sp-4) 0} -.me-stat{flex:1;display:flex;flex-direction:column;align-items:center;gap:4rpx} -.me-n{font-size:var(--fs-lg);font-weight:var(--fw-b)} -.me-l{font-size:var(--fs-cap);color:var(--muted)} .pet-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) 0;border-bottom:1rpx solid var(--line)} .pet-row:last-child{border-bottom:0} @@ -23,3 +7,5 @@ background:var(--primary-soft);font-size:40rpx; } .pet-info{flex:1;min-width:0} + +.me-preview{margin-bottom:var(--sp-5)} diff --git a/pets-fe/pages/user/user.js b/pets-fe/pages/user/user.js index b8f2fe6..60c66be 100644 --- a/pets-fe/pages/user/user.js +++ b/pets-fe/pages/user/user.js @@ -1,4 +1,5 @@ const api = require('../../utils/api.js'); +const store = require('../../utils/store.js'); const { toastErr } = require('../../utils/ui.js'); const TAG_CLASS = { 求助: 'warn', 经验: 'blue', 精选: 'purple', 避坑: 'red', 晒宠: '' }; @@ -28,12 +29,7 @@ Page({ loadCard() { api .userCard(this.data.uid) - .then((card) => - this.setData({ - card: { ...card, initial: (card.nickname || '?').slice(0, 1) }, - loading: false, - }), - ) + .then((card) => this.setData({ card, loading: false })) .catch((e) => { this.setData({ loading: false }); toastErr(e); @@ -76,9 +72,20 @@ Page({ }); }, goRelations(e) { - const kind = e.currentTarget.dataset.kind; + const kind = e.detail.kind; wx.navigateTo({ url: `/pages/relations/relations?id=${this.data.uid}&kind=${kind}` }); }, + // 自己看自己的主页时,头部按钮是「装扮」而不是「关注」 + goDecorate() { + wx.navigateTo({ url: '/pages/decorate/decorate' }); + }, + // 别人的宠物只是名片,点了不跳;自己的才切过去看 + goPet(e) { + const c = this.data.card; + if (!c || !c.is_self) return; + store.switchPet(e.detail.id); + wx.switchTab({ url: '/pages/home/home' }); + }, previewImage(e) { const { urls, cur } = e.currentTarget.dataset; if (urls && urls.length) wx.previewImage({ urls, current: cur }); diff --git a/pets-fe/pages/user/user.json b/pets-fe/pages/user/user.json index 6594cd8..b4ba05a 100644 --- a/pets-fe/pages/user/user.json +++ b/pets-fe/pages/user/user.json @@ -1,6 +1,7 @@ { "usingComponents": { "nav-bar": "/components/nav-bar/nav-bar", - "pt-icon": "/components/pt-icon/index" + "pt-icon": "/components/pt-icon/index", + "profile-head": "/components/profile-head/index" } } diff --git a/pets-fe/pages/user/user.wxml b/pets-fe/pages/user/user.wxml index fff75fa..39b4d14 100644 --- a/pets-fe/pages/user/user.wxml +++ b/pets-fe/pages/user/user.wxml @@ -9,27 +9,9 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; } 加载中… - - - - {{card.initial}} - - {{card.nickname}}AI - - - {{card.post_count}}帖子 - - {{card.following_count}}关注 - - - {{card.follower_count}}粉丝 - - - - - {{card.followed ? '已关注' : '关注 TA'}} - - + diff --git a/pets-fe/pages/user/user.wxss b/pets-fe/pages/user/user.wxss index 23bb3b5..dba8ff6 100644 --- a/pets-fe/pages/user/user.wxss +++ b/pets-fe/pages/user/user.wxss @@ -1,12 +1,2 @@ -.u-head{text-align:center;padding:var(--sp-6) var(--sp-5)} -.u-av{ - width:140rpx;height:140rpx;border-radius:50%;margin:0 auto var(--sp-3);overflow:hidden; - display:flex;align-items:center;justify-content:center; - background:var(--primary-soft);color:var(--primary-ink);font-size:56rpx;font-weight:var(--fw-b); -} -.u-av-img{width:100%;height:100%} -.u-name{font-size:var(--fs-lg);font-weight:var(--fw-b)} -.u-stats{display:flex;justify-content:center;gap:var(--sp-6);margin:var(--sp-4) 0} -.u-stat{display:flex;flex-direction:column;align-items:center;gap:4rpx} -.u-n{font-size:var(--fs-lg);font-weight:var(--fw-b)} -.u-l{font-size:var(--fs-cap);color:var(--muted)} +/* 头部整块(头像/昵称/签名/计数/养宠数据/宠物墙)已抽成 components/profile-head, + 帖子卡走 app.wxss 的 .post-card。这里没有页面独有样式了。 */ diff --git a/pets-fe/utils/api.js b/pets-fe/utils/api.js index 3649017..ce01589 100644 --- a/pets-fe/utils/api.js +++ b/pets-fe/utils/api.js @@ -46,6 +46,7 @@ const api = { // 用户 userSummary: () => request({ url: '/api/user/summary' }), + updateDecoration: (body) => request({ url: '/api/user/decoration', method: 'PUT', data: body }), // 宠物 getPets: () => request({ url: '/api/pets' }),