281 lines
8.3 KiB
TypeScript
281 lines
8.3 KiB
TypeScript
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 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: {
|
||
name: '',
|
||
species: 'dog' as 'dog' | 'cat' | 'other',
|
||
breed: '',
|
||
gender: 'female' as 'female' | 'male',
|
||
birthday: '',
|
||
bio: '',
|
||
tags: [] as string[],
|
||
emoji: '🐶',
|
||
},
|
||
speciesLabel: '狗狗',
|
||
// 品种
|
||
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()
|
||
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 })
|
||
this.loadPet(query.id)
|
||
}
|
||
},
|
||
|
||
loadPet(id: string) {
|
||
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,
|
||
species: pet.species,
|
||
breed: pet.breed,
|
||
gender: pet.gender,
|
||
birthday: pet.birthday || '',
|
||
bio: pet.bio || '',
|
||
tags: petTags,
|
||
emoji: pet.emoji,
|
||
},
|
||
speciesLabel: SPECIES_LABELS[pet.species] || '狗狗',
|
||
currentBreeds: breeds,
|
||
breedIndex,
|
||
allTags: merged,
|
||
selectedTagSet: buildTagSet(petTags),
|
||
})
|
||
},
|
||
|
||
onField(e: WechatMiniprogram.CustomEvent) {
|
||
const key = e.currentTarget.dataset.key as string
|
||
this.setData({ [`form.${key}`]: e.detail.value })
|
||
},
|
||
|
||
onSpecies() {
|
||
wx.showActionSheet({
|
||
itemList: ['狗狗', '猫猫', '其他'],
|
||
success: res => {
|
||
const species = (['dog', 'cat', 'other'] as const)[res.tapIndex]
|
||
const breeds = BREED_OPTIONS[species]
|
||
this.setData({
|
||
'form.species': species,
|
||
'form.breed': '',
|
||
'form.emoji': getPetEmoji(species),
|
||
speciesLabel: SPECIES_LABELS[species],
|
||
currentBreeds: breeds,
|
||
breedIndex: -1,
|
||
})
|
||
},
|
||
})
|
||
},
|
||
|
||
// 品种 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),
|
||
})
|
||
},
|
||
|
||
onGender(e: WechatMiniprogram.CustomEvent) {
|
||
this.setData({ 'form.gender': e.currentTarget.dataset.v })
|
||
},
|
||
|
||
onBirthday(e: WechatMiniprogram.CustomEvent) {
|
||
const dob = e.detail.value as string
|
||
const diffMs = Date.now() - new Date(dob).getTime()
|
||
const years = Math.floor(diffMs / (365.25 * 24 * 3600 * 1000))
|
||
const months = Math.floor((diffMs % (365.25 * 24 * 3600 * 1000)) / (30.44 * 24 * 3600 * 1000))
|
||
const age = years > 0 ? `${years}岁${months > 0 ? months + '个月' : ''}` : `${months}个月`
|
||
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 >= 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() {
|
||
wx.chooseMedia({
|
||
count: 1,
|
||
mediaType: ['image'],
|
||
sourceType: ['album', 'camera'],
|
||
success: async res => {
|
||
const path = res.tempFiles[0].tempFilePath
|
||
wx.showLoading({ title: '上传中...' })
|
||
try {
|
||
const cloudPath = `pet-avatars/${Date.now()}.jpg`
|
||
const r = await wx.cloud.uploadFile({ cloudPath, filePath: path })
|
||
this.setData({ 'form.avatarUrl': r.fileID })
|
||
wx.hideLoading()
|
||
} catch {
|
||
wx.hideLoading()
|
||
wx.showToast({ title: '上传失败', icon: 'none' })
|
||
}
|
||
},
|
||
})
|
||
},
|
||
|
||
async onSave() {
|
||
const { form, isEdit, petId } = this.data
|
||
if (!form.name.trim()) {
|
||
wx.showToast({ title: '请填写宠物名字', icon: 'none' })
|
||
return
|
||
}
|
||
wx.showLoading({ title: '保存中...' })
|
||
try {
|
||
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 (e) {
|
||
wx.hideLoading()
|
||
const message = e instanceof Error ? e.message : '保存失败'
|
||
wx.showToast({ title: message, icon: 'none' })
|
||
}
|
||
},
|
||
|
||
onDelete() {
|
||
const { petId } = this.data
|
||
wx.showModal({
|
||
title: '删除宠物档案',
|
||
content: '确定要删除吗?',
|
||
confirmText: '删除',
|
||
confirmColor: '#d4596a',
|
||
success: res => {
|
||
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' })
|
||
})
|
||
},
|
||
})
|
||
},
|
||
|
||
onBack() { wx.navigateBack() },
|
||
})
|