Initial commit
This commit is contained in:
1
miniprogram/pages/pet-edit/pet-edit.json
Normal file
1
miniprogram/pages/pet-edit/pet-edit.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "navigationStyle": "custom", "usingComponents": {} }
|
||||
169
miniprogram/pages/pet-edit/pet-edit.ts
Normal file
169
miniprogram/pages/pet-edit/pet-edit.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { api } from '../../utils/api'
|
||||
import { BREED_OPTIONS } from '../../utils/constants'
|
||||
import { getPetEmoji } from '../../utils/format'
|
||||
|
||||
const SPECIES_LABELS: Record<string, string> = { dog: '狗狗', cat: '猫猫', other: '其他' }
|
||||
const PET_TAGS = ['活泼', '温顺', '黏人', '独立', '爱玩', '怕生', '聪明', '贪吃', '爱运动', '安静']
|
||||
|
||||
const app = getApp<{ userInfo: any }>()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 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: '狗狗',
|
||||
allTags: PET_TAGS,
|
||||
},
|
||||
|
||||
onLoad(query: { id?: string }) {
|
||||
const info = wx.getSystemInfoSync()
|
||||
this.setData({ statusBarHeight: info.statusBarHeight })
|
||||
|
||||
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
|
||||
this.setData({
|
||||
form: {
|
||||
name: pet.name,
|
||||
species: pet.species,
|
||||
breed: pet.breed,
|
||||
gender: pet.gender,
|
||||
birthday: pet.birthday || '',
|
||||
bio: pet.bio || '',
|
||||
tags: pet.tags || [],
|
||||
emoji: pet.emoji,
|
||||
},
|
||||
speciesLabel: SPECIES_LABELS[pet.species] || '狗狗',
|
||||
})
|
||||
},
|
||||
|
||||
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]
|
||||
this.setData({
|
||||
'form.species': species,
|
||||
'form.breed': '',
|
||||
speciesLabel: SPECIES_LABELS[species],
|
||||
'form.emoji': getPetEmoji(species),
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
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),
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
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 < 5) tags.push(tag)
|
||||
this.setData({ 'form.tags': tags })
|
||||
},
|
||||
|
||||
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 {
|
||||
await api.updatePet(isEdit ? { ...form, _id: petId } : form)
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||||
setTimeout(() => wx.navigateBack(), 800)
|
||||
} catch {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '保存失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
onDelete() {
|
||||
wx.showModal({
|
||||
title: '删除宠物档案',
|
||||
content: '确定要删除吗?',
|
||||
confirmText: '删除',
|
||||
confirmColor: '#ff4f91',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
wx.showToast({ title: '已删除', icon: 'success' })
|
||||
setTimeout(() => wx.navigateBack(), 800)
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
onBack() { wx.navigateBack() },
|
||||
})
|
||||
101
miniprogram/pages/pet-edit/pet-edit.wxml
Normal file
101
miniprogram/pages/pet-edit/pet-edit.wxml
Normal file
@@ -0,0 +1,101 @@
|
||||
<view class="page">
|
||||
<view class="safe-top" style="height: {{statusBarHeight}}px"></view>
|
||||
<view class="nav">
|
||||
<view class="nav-back" bindtap="onBack">取消</view>
|
||||
<view class="nav-title">{{isEdit ? '编辑宠物' : '添加宠物'}}</view>
|
||||
<view class="nav-save" bindtap="onSave">保存</view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y="true" class="scroll" enhanced="true" show-scrollbar="false">
|
||||
<view class="form">
|
||||
|
||||
<!-- 宠物头像 -->
|
||||
<view class="avatar-section" bindtap="onChoosePhoto">
|
||||
<view class="pet-avatar" style="background: linear-gradient(135deg, #c9f7df, #d9d2ff);">
|
||||
<text class="pet-emoji-big">{{form.emoji || '🐾'}}</text>
|
||||
</view>
|
||||
<text class="avatar-tip">点击更换照片</text>
|
||||
</view>
|
||||
|
||||
<!-- 名字 -->
|
||||
<view class="field-group">
|
||||
<view class="field-item">
|
||||
<text class="field-label">宠物名字 *</text>
|
||||
<input class="field-input" value="{{form.name}}" bindinput="onField" data-key="name" placeholder="给TA起个名字" maxlength="10" />
|
||||
</view>
|
||||
|
||||
<!-- 物种 -->
|
||||
<view class="field-item" bindtap="onSpecies">
|
||||
<text class="field-label">物种 *</text>
|
||||
<view class="field-select">
|
||||
<text>{{speciesLabel}}</text>
|
||||
<text class="select-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 品种 -->
|
||||
<view class="field-item" bindtap="onBreed">
|
||||
<text class="field-label">品种</text>
|
||||
<view class="field-select">
|
||||
<text>{{form.breed || '选择品种'}}</text>
|
||||
<text class="select-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 性别 -->
|
||||
<view class="field-item">
|
||||
<text class="field-label">性别</text>
|
||||
<view class="gender-btns">
|
||||
<view class="gender-btn {{form.gender === 'female' ? 'g-active' : ''}}" bindtap="onGender" data-v="female">女生 ♀</view>
|
||||
<view class="gender-btn {{form.gender === 'male' ? 'g-active' : ''}}" bindtap="onGender" data-v="male">男生 ♂</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 生日 -->
|
||||
<view class="field-item">
|
||||
<text class="field-label">生日</text>
|
||||
<picker mode="date" value="{{form.birthday}}" bindchange="onBirthday">
|
||||
<view class="field-select">
|
||||
<text>{{form.birthday || '选择生日'}}</text>
|
||||
<text class="select-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 简介 -->
|
||||
<view class="field-item field-item-col">
|
||||
<text class="field-label">简介</text>
|
||||
<textarea
|
||||
class="field-textarea"
|
||||
value="{{form.bio}}"
|
||||
bindinput="onField"
|
||||
data-key="bio"
|
||||
placeholder="写几句关于TA的介绍..."
|
||||
maxlength="100"
|
||||
auto-height="true"
|
||||
show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 性格标签 -->
|
||||
<view class="tags-section">
|
||||
<text class="tags-title">性格标签</text>
|
||||
<view class="tag-grid">
|
||||
<view
|
||||
wx:for="{{allTags}}"
|
||||
wx:key="index"
|
||||
class="tag-chip {{form.tags.includes(item) ? 'tag-on' : ''}}"
|
||||
bindtap="onTagToggle"
|
||||
data-tag="{{item}}"
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 删除 -->
|
||||
<view wx:if="{{isEdit}}" class="delete-btn" bindtap="onDelete">删除这只宠物</view>
|
||||
|
||||
<view style="height: 60rpx;"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
35
miniprogram/pages/pet-edit/pet-edit.wxss
Normal file
35
miniprogram/pages/pet-edit/pet-edit.wxss
Normal file
@@ -0,0 +1,35 @@
|
||||
.page { min-height: 100vh; background: linear-gradient(180deg, #fff8f2 0%, #f5f0ff 100%); }
|
||||
.nav { display: flex; align-items: center; padding: 14rpx 28rpx; gap: 16rpx; }
|
||||
.nav-back { font-size: 28rpx; font-weight: 700; color: #9b8fa8; }
|
||||
.nav-title { flex: 1; font-size: 32rpx; font-weight: 900; color: #272235; text-align: center; }
|
||||
.nav-save { font-size: 28rpx; font-weight: 900; color: #ff4f91; }
|
||||
.scroll { }
|
||||
|
||||
.form { padding: 0 28rpx; }
|
||||
|
||||
.avatar-section { display: flex; flex-direction: column; align-items: center; padding: 28rpx 0 32rpx; gap: 12rpx; }
|
||||
.pet-avatar { width: 140rpx; height: 140rpx; border-radius: 44rpx; display: flex; align-items: center; justify-content: center; box-shadow: 0 12rpx 24rpx rgba(78,56,96,0.15); }
|
||||
.pet-emoji-big { font-size: 72rpx; line-height: 1; }
|
||||
.avatar-tip { font-size: 24rpx; color: #9b8fa8; }
|
||||
|
||||
.field-group { background: rgba(255,255,255,0.90); border-radius: 36rpx; border: 1rpx solid rgba(255,255,255,0.72); overflow: hidden; box-shadow: 0 12rpx 26rpx rgba(78,56,96,0.08); margin-bottom: 24rpx; }
|
||||
.field-item { display: flex; align-items: center; justify-content: space-between; padding: 24rpx 28rpx; border-bottom: 1rpx solid rgba(43,37,61,0.06); }
|
||||
.field-item:last-child { border-bottom: none; }
|
||||
.field-item-col { flex-direction: column; align-items: flex-start; gap: 14rpx; }
|
||||
.field-label { font-size: 28rpx; font-weight: 700; color: #272235; flex-shrink: 0; }
|
||||
.field-input { flex: 1; font-size: 28rpx; color: #272235; text-align: right; }
|
||||
.field-select { display: flex; align-items: center; gap: 8rpx; font-size: 28rpx; color: #9b8fa8; }
|
||||
.select-arrow { font-size: 36rpx; color: #c4b8d0; font-weight: 300; }
|
||||
.field-textarea { width: 100%; min-height: 100rpx; font-size: 28rpx; color: #272235; line-height: 1.6; }
|
||||
|
||||
.gender-btns { display: flex; gap: 14rpx; }
|
||||
.gender-btn { background: rgba(255,255,255,0.80); border: 1rpx solid rgba(43,37,61,0.12); border-radius: 999rpx; padding: 12rpx 24rpx; font-size: 26rpx; color: #9b8fa8; font-weight: 700; }
|
||||
.g-active { background: #ffe5f0; border-color: #ffb6d0; color: #a91d5b; }
|
||||
|
||||
.tags-section { margin-bottom: 28rpx; }
|
||||
.tags-title { display: block; font-size: 26rpx; font-weight: 900; color: #9b8fa8; margin-bottom: 16rpx; }
|
||||
.tag-grid { display: flex; gap: 14rpx; flex-wrap: wrap; }
|
||||
.tag-chip { background: rgba(255,255,255,0.80); border: 1rpx solid rgba(43,37,61,0.10); border-radius: 999rpx; padding: 12rpx 24rpx; font-size: 26rpx; color: #6a6178; font-weight: 700; }
|
||||
.tag-on { background: #f0e8ff; border-color: #d4b8ff; color: #8c5cff; }
|
||||
|
||||
.delete-btn { text-align: center; font-size: 28rpx; color: #ff4f91; background: rgba(255,79,145,0.08); border-radius: 999rpx; padding: 24rpx; margin-top: 40rpx; }
|
||||
Reference in New Issue
Block a user