fix: 修复评论/资料/导航胶囊/宠物档案/定位等多处问题,补全相关云函数

- 新增 addComment/getComments/getFollowList/followUser/getUser/updateProfile/updatePet/deletePet 云函数并完成部署
- 统一通过 app.globalData.navBarInfo 避让系统胶囊按钮(编辑资料/编辑宠物等页面保存按钮被遮挡问题)
- 编辑资料页所在城市改为省份 picker 选择
- 修复"我的"页动态为空(getUserPosts 应传 openid 而非数据库 _id)
- 重构编辑宠物页:品种改用 picker 滚轮(替代有数量上限的 actionSheet),标签改用 selectedTagSet 映射 + 自定义标签输入(5字以内、仅中英文/数字、最多8个)
- 接通宠物保存/删除真实云函数调用并同步本地缓存
- 移除 feed 页对不存在的 getStories 云函数调用,修复模拟器卡死
- 移除 post 页对不存在的 wx.reverseGeocoder API 的调用
- 统一扩展 AppGlobalData 类型并修正所有 getApp 调用的泛型,解决 TS 报错
This commit is contained in:
2026-06-07 15:59:21 +08:00
parent 24891d9004
commit 3d21a9d297
49 changed files with 937 additions and 195 deletions

View File

@@ -1,15 +1,26 @@
import { api } from '../../utils/api'
import { BREED_OPTIONS } from '../../utils/constants'
import { getPetEmoji } from '../../utils/format'
import { AppGlobalData } from '../../types/index'
const SPECIES_LABELS: Record<string, string> = { dog: '狗狗', cat: '猫猫', other: '其他' }
const PET_TAGS = ['活泼', '温顺', '黏人', '独立', '爱玩', '怕生', '聪明', '贪吃', '爱运动', '安静']
const app = getApp<{ userInfo: any }>()
const PRESET_TAGS = ['活泼', '温顺', '黏人', '独立', '爱玩', '怕生', '聪明', '贪吃', '爱运动', '安静']
const app = getApp<{ globalData: AppGlobalData }>()
// 根据已选标签数组生成 {tag: boolean} 映射,供 WXML 直接判断选中态
function buildTagSet(tags: string[]): Record<string, boolean> {
const set: Record<string, boolean> = {}
tags.forEach(t => { set[t] = true })
return set
}
Page({
data: {
statusBarHeight: 0,
navbarHeight: 44,
navbarPaddingRight: 0,
isEdit: false,
petId: '',
form: {
@@ -23,12 +34,24 @@ Page({
emoji: '🐶',
},
speciesLabel: '狗狗',
allTags: PET_TAGS,
// 品种
currentBreeds: BREED_OPTIONS['dog'],
breedIndex: -1,
// 标签
allTags: [...PRESET_TAGS] as string[],
selectedTagSet: {} as Record<string, boolean>,
customTagInput: '',
},
onLoad(query: { id?: string }) {
const info = wx.getSystemInfoSync()
this.setData({ statusBarHeight: info.statusBarHeight })
const { statusBarHeight, navbarHeight, navbarPaddingRight } = app.globalData.navBarInfo
this.setData({
statusBarHeight,
navbarHeight,
navbarPaddingRight,
currentBreeds: BREED_OPTIONS['dog'],
})
if (query.id) {
this.setData({ isEdit: true, petId: query.id })
@@ -40,6 +63,15 @@ Page({
const user = app.globalData?.userInfo
const pet = user?.pets?.find((p: any) => p._id === id)
if (!pet) return
const breeds = BREED_OPTIONS[pet.species] || BREED_OPTIONS['dog']
const breedIndex = breeds.indexOf(pet.breed)
const petTags: string[] = pet.tags || []
// 把宠物已有的自定义标签合并进 allTags
const merged = [...PRESET_TAGS]
petTags.forEach(t => { if (!merged.includes(t)) merged.push(t) })
this.setData({
form: {
name: pet.name,
@@ -48,10 +80,14 @@ Page({
gender: pet.gender,
birthday: pet.birthday || '',
bio: pet.bio || '',
tags: pet.tags || [],
tags: petTags,
emoji: pet.emoji,
},
speciesLabel: SPECIES_LABELS[pet.species] || '狗狗',
currentBreeds: breeds,
breedIndex,
allTags: merged,
selectedTagSet: buildTagSet(petTags),
})
},
@@ -65,27 +101,27 @@ Page({
itemList: ['狗狗', '猫猫', '其他'],
success: res => {
const species = (['dog', 'cat', 'other'] as const)[res.tapIndex]
const breeds = BREED_OPTIONS[species]
this.setData({
'form.species': species,
'form.breed': '',
speciesLabel: SPECIES_LABELS[species],
'form.emoji': getPetEmoji(species),
speciesLabel: SPECIES_LABELS[species],
currentBreeds: breeds,
breedIndex: -1,
})
},
})
},
onBreed() {
const breeds = BREED_OPTIONS[this.data.form.species] || BREED_OPTIONS.dog
wx.showActionSheet({
itemList: breeds,
success: res => {
const breed = breeds[res.tapIndex]
this.setData({
'form.breed': breed,
'form.emoji': getPetEmoji(this.data.form.species, breed),
})
},
// 品种 picker 选中回调
onBreedChange(e: WechatMiniprogram.CustomEvent) {
const idx = Number(e.detail.value)
const breed = this.data.currentBreeds[idx]
this.setData({
breedIndex: idx,
'form.breed': breed,
'form.emoji': getPetEmoji(this.data.form.species, breed),
})
},
@@ -102,13 +138,64 @@ Page({
this.setData({ 'form.birthday': dob, 'form.age': age })
},
// 预设标签 — 点击切换
onTagToggle(e: WechatMiniprogram.CustomEvent) {
const tag = e.currentTarget.dataset.tag as string
const tags = [...this.data.form.tags]
const idx = tags.indexOf(tag)
if (idx >= 0) tags.splice(idx, 1)
else if (tags.length < 5) tags.push(tag)
this.setData({ 'form.tags': tags })
if (idx >= 0) {
tags.splice(idx, 1)
} else {
if (tags.length >= 8) {
wx.showToast({ title: '最多选8个标签', icon: 'none' })
return
}
tags.push(tag)
}
this.setData({ 'form.tags': tags, selectedTagSet: buildTagSet(tags) })
},
// 自定义标签输入
onCustomTagInput(e: WechatMiniprogram.CustomEvent) {
this.setData({ customTagInput: e.detail.value })
},
// 添加自定义标签
onAddCustomTag() {
const raw = this.data.customTagInput.trim()
if (!raw) return
// 仅允许中英文和数字最多5个字符
if ([...raw].length > 5) {
wx.showToast({ title: '标签最多5个字', icon: 'none' })
return
}
if (!/^[一-龥a-zA-Z0-9]+$/.test(raw)) {
wx.showToast({ title: '只能输入文字或字母', icon: 'none' })
return
}
const tags = [...this.data.form.tags]
if (tags.includes(raw)) {
this.setData({ customTagInput: '' })
return
}
if (tags.length >= 8) {
wx.showToast({ title: '最多选8个标签', icon: 'none' })
return
}
tags.push(raw)
const allTags = this.data.allTags.includes(raw)
? this.data.allTags
: [...this.data.allTags, raw]
this.setData({
'form.tags': tags,
selectedTagSet: buildTagSet(tags),
allTags,
customTagInput: '',
})
},
async onChoosePhoto() {
@@ -140,27 +227,51 @@ Page({
}
wx.showLoading({ title: '保存中...' })
try {
await api.updatePet(isEdit ? { ...form, _id: petId } : form)
const saved = await api.updatePet(isEdit ? { ...form, _id: petId } : form)
// 同步本地缓存,让"我的"页面立即看到最新的宠物档案
const user = app.globalData.userInfo
if (user) {
const pets = Array.isArray(user.pets) ? [...user.pets] : []
const idx = pets.findIndex((p: any) => p._id === saved._id)
if (idx >= 0) pets[idx] = saved
else pets.push(saved)
user.pets = pets
}
wx.hideLoading()
wx.showToast({ title: '保存成功', icon: 'success' })
setTimeout(() => wx.navigateBack(), 800)
} catch {
} catch (e) {
wx.hideLoading()
wx.showToast({ title: '保存失败', icon: 'none' })
const message = e instanceof Error ? e.message : '保存失败'
wx.showToast({ title: message, icon: 'none' })
}
},
onDelete() {
const { petId } = this.data
wx.showModal({
title: '删除宠物档案',
content: '确定要删除吗?',
confirmText: '删除',
confirmColor: '#ff4f91',
success: res => {
if (res.confirm) {
wx.showToast({ title: '删除', icon: 'success' })
setTimeout(() => wx.navigateBack(), 800)
}
if (!res.confirm) return
wx.showLoading({ title: '删除中...' })
api.deletePet(petId)
.then(() => {
const user = app.globalData.userInfo
if (user && Array.isArray(user.pets)) {
user.pets = user.pets.filter((p: any) => p._id !== petId)
}
wx.hideLoading()
wx.showToast({ title: '已删除', icon: 'success' })
setTimeout(() => wx.navigateBack(), 800)
})
.catch((e: unknown) => {
wx.hideLoading()
const message = e instanceof Error ? e.message : '删除失败'
wx.showToast({ title: message, icon: 'none' })
})
},
})
},