xiangqinxiaochengxu/miniprogram/pages/profile-edit/profile-edit.js
2026-04-17 10:49:14 +08:00

371 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const request = require('../../utils/request')
const AUDIT_REQUIRED_FIELDS = ['personality_tags', 'hobbies', 'wechat_id', 'phone']
const PROFILE_LIMITS = {
nickname: 50,
real_name: 20,
city: 30,
job_industry: 50,
job_company: 100,
prefer_city: 30,
self_intro: 500,
wechat_id: 64,
phone: 20,
tagItem: 20,
listItems: 20
}
Page({
data: {
form: {
nickname: '',
real_name: '',
gender: null,
birth_year: null,
city: '',
height: null,
education: null,
job_industry: '',
job_company: '',
income_range: null,
prefer_city: '',
self_intro: '',
personality_tags: [],
hobbies: [],
wechat_id: '',
phone: ''
},
tagsText: '',
hobbiesText: '',
genderOptions: [
{ label: '男', value: 1 },
{ label: '女', value: 2 }
],
educationOptions: [
{ label: '高中', value: 1 },
{ label: '大专', value: 2 },
{ label: '本科', value: 3 },
{ label: '硕士', value: 4 },
{ label: '博士', value: 5 }
],
incomeOptions: [
{ label: '5k以下', value: 1 },
{ label: '5k-10k', value: 2 },
{ label: '10k-20k', value: 3 },
{ label: '20k以上', value: 4 }
],
genderText: '请选择',
educationText: '请选择',
incomeText: '请选择',
loading: false,
saving: false
},
onLoad() {
this.syncPickerTexts(this.data.form)
this.syncExtraTexts(this.data.form)
this.loadProfile()
},
syncExtraTexts(form) {
this.setData({
tagsText: (form.personality_tags || []).join(''),
hobbiesText: (form.hobbies || []).join('')
})
},
parseListText(text) {
return (text || '')
.split(/[,\n]/)
.map((item) => item.trim())
.filter(Boolean)
},
async loadProfile() {
this.setData({ loading: true })
try {
const data = await request({ url: '/users/me' })
const form = {
...this.data.form,
...data,
gender: data.gender == null ? null : Number(data.gender),
birth_year: data.birth_year == null ? null : Number(data.birth_year),
height: data.height == null ? null : Number(data.height),
education: data.education == null ? null : Number(data.education),
income_range: data.income_range == null ? null : Number(data.income_range),
personality_tags: data.personality_tags || [],
hobbies: data.hobbies || [],
wechat_id: data.wechat_id || '',
phone: data.phone || ''
}
this.setData({ form })
this.syncPickerTexts(form)
this.syncExtraTexts(form)
} catch (err) {
console.error('Load profile edit data failed:', err)
} finally {
this.setData({ loading: false })
}
},
handleInput(e) {
const field = e.currentTarget.dataset.field
const value = e.detail.value
if (field === 'personality_tags') {
const tags = this.parseListText(value)
this.setData({
form: { ...this.data.form, personality_tags: tags },
tagsText: value
})
return
}
if (field === 'hobbies') {
const hobbies = this.parseListText(value)
this.setData({
form: { ...this.data.form, hobbies },
hobbiesText: value
})
return
}
this.setData({ [`form.${field}`]: value })
},
handlePickerChange(e) {
const field = e.currentTarget.dataset.field
const optionKeyMap = {
income_range: 'incomeOptions'
}
const options = this.data[optionKeyMap[field] || `${field}Options`]
const index = Number(e.detail.value)
if (!options || !options[index]) {
return
}
const form = {
...this.data.form,
[field]: options[index].value
}
this.setData({ form })
this.syncPickerTexts(form)
},
syncPickerTexts(form) {
this.setData({
genderText: this.getOptionLabel(this.data.genderOptions, form.gender, '请选择'),
educationText: this.getOptionLabel(this.data.educationOptions, form.education, '请选择'),
incomeText: this.getOptionLabel(this.data.incomeOptions, form.income_range, '请选择')
})
},
getOptionLabel(options, value, fallback) {
const normalizedValue = value === null || value === undefined || value === '' ? value : Number(value)
const matched = (options || []).find((item) => item.value === normalizedValue)
return matched ? matched.label : fallback
},
sanitizeText(value, maxLength) {
if (value === null || value === undefined) {
return ''
}
return String(value).trim().slice(0, maxLength)
},
sanitizeNumber(value) {
if (value === null || value === undefined || value === '') {
return null
}
const num = Number(value)
return Number.isFinite(num) ? num : null
},
buildProfilePayload() {
const payload = {
nickname: this.sanitizeText(this.data.form.nickname, PROFILE_LIMITS.nickname) || null,
real_name: this.sanitizeText(this.data.form.real_name, PROFILE_LIMITS.real_name) || null,
gender: this.sanitizeNumber(this.data.form.gender),
birth_year: this.sanitizeNumber(this.data.form.birth_year),
city: this.sanitizeText(this.data.form.city, PROFILE_LIMITS.city) || null,
height: this.sanitizeNumber(this.data.form.height),
education: this.sanitizeNumber(this.data.form.education),
job_industry: this.sanitizeText(this.data.form.job_industry, PROFILE_LIMITS.job_industry) || null,
job_company: this.sanitizeText(this.data.form.job_company, PROFILE_LIMITS.job_company) || null,
income_range: this.sanitizeNumber(this.data.form.income_range),
prefer_city: this.sanitizeText(this.data.form.prefer_city, PROFILE_LIMITS.prefer_city) || null,
self_intro: this.sanitizeText(this.data.form.self_intro, PROFILE_LIMITS.self_intro) || null,
personality_tags: Array.isArray(this.data.form.personality_tags)
? this.data.form.personality_tags.map((item) => this.sanitizeText(item, PROFILE_LIMITS.tagItem)).filter(Boolean).slice(0, PROFILE_LIMITS.listItems)
: [],
hobbies: Array.isArray(this.data.form.hobbies)
? this.data.form.hobbies.map((item) => this.sanitizeText(item, PROFILE_LIMITS.tagItem)).filter(Boolean).slice(0, PROFILE_LIMITS.listItems)
: [],
wechat_id: this.sanitizeText(this.data.form.wechat_id, PROFILE_LIMITS.wechat_id) || null,
phone: this.sanitizeText(this.data.form.phone, PROFILE_LIMITS.phone) || null
}
return payload
},
getValidationLabel(field) {
const labels = {
nickname: '昵称',
real_name: '真实姓名',
city: '所在城市',
job_industry: '行业',
job_company: '公司',
prefer_city: '期望城市',
self_intro: '个人介绍',
wechat_id: '微信号',
phone: '手机号',
personality_tags: '性格标签',
hobbies: '兴趣爱好',
gender: '性别',
birth_year: '出生年份',
height: '身高',
education: '学历',
income_range: '收入范围'
}
return labels[field] || field
},
validateProfilePayload(payload) {
const errors = []
const checkTextLength = (field, maxLength) => {
const value = payload[field]
if (typeof value === 'string' && value.length > maxLength) {
errors.push(`${this.getValidationLabel(field)}不能超过${maxLength}个字符`)
}
}
checkTextLength('nickname', PROFILE_LIMITS.nickname)
checkTextLength('real_name', PROFILE_LIMITS.real_name)
checkTextLength('city', PROFILE_LIMITS.city)
checkTextLength('job_industry', PROFILE_LIMITS.job_industry)
checkTextLength('job_company', PROFILE_LIMITS.job_company)
checkTextLength('prefer_city', PROFILE_LIMITS.prefer_city)
checkTextLength('self_intro', PROFILE_LIMITS.self_intro)
checkTextLength('wechat_id', PROFILE_LIMITS.wechat_id)
checkTextLength('phone', PROFILE_LIMITS.phone)
;['personality_tags', 'hobbies'].forEach((field) => {
const list = payload[field]
const label = this.getValidationLabel(field)
if (!Array.isArray(list)) {
errors.push(`${label}格式不正确`)
return
}
if (list.length > PROFILE_LIMITS.listItems) {
errors.push(`${label}最多${PROFILE_LIMITS.listItems}`)
}
list.forEach((item) => {
if (item.length > PROFILE_LIMITS.tagItem) {
errors.push(`${label}单项不能超过${PROFILE_LIMITS.tagItem}个字符`)
}
})
})
;['gender', 'birth_year', 'height', 'education', 'income_range'].forEach((field) => {
const value = payload[field]
if (value !== null && value !== undefined && !Number.isFinite(value)) {
errors.push(`${this.getValidationLabel(field)}格式不正确`)
}
})
return errors
},
async saveProfile() {
this.setData({ saving: true })
try {
const payload = this.buildProfilePayload()
const validationErrors = this.validateProfilePayload(payload)
if (validationErrors.length > 0) {
wx.showModal({
title: '资料有误',
content: validationErrors.slice(0, 3).join(''),
showCancel: false
})
return false
}
await request({
url: '/users/me',
method: 'PUT',
data: payload
})
this.setData({
form: { ...this.data.form, ...payload }
})
this.syncPickerTexts(this.data.form)
wx.showToast({ title: '保存成功', icon: 'success' })
return true
} catch (err) {
console.error('Save profile failed:', err)
wx.showToast({ title: err.message || '保存失败', icon: 'none' })
return false
} finally {
this.setData({ saving: false })
}
},
async submitAudit() {
const saveOk = await this.saveProfile()
if (!saveOk) {
return
}
const payload = this.buildProfilePayload()
const validationErrors = this.validateProfilePayload(payload)
if (validationErrors.length > 0) {
wx.showModal({
title: '资料有误',
content: validationErrors.slice(0, 3).join(''),
showCancel: false
})
return
}
const missingFields = AUDIT_REQUIRED_FIELDS.filter((field) => {
const value = payload[field]
if (Array.isArray(value)) {
return value.length === 0
}
if (value && typeof value === 'object') {
return Object.keys(value).length === 0
}
return value === null || value === undefined || value === ''
})
if (missingFields.length > 0) {
const labelMap = {
personality_tags: '性格标签',
hobbies: '兴趣爱好',
wechat_id: '微信号',
phone: '手机号'
}
wx.showModal({
title: '资料未完善',
content: `提交审核前请先补充:${missingFields.map((field) => labelMap[field] || field).join('、')}`,
showCancel: false
})
return
}
try {
await request({
url: '/users/submit-audit',
method: 'POST'
})
wx.showToast({ title: '已提交审核', icon: 'success' })
setTimeout(() => {
wx.navigateTo({ url: '/pages/audit-status/audit-status' })
}, 300)
} catch (err) {
console.error('Submit audit failed:', err)
wx.showToast({ title: err.message || '提交审核失败', icon: 'none' })
}
}
})