371 lines
11 KiB
JavaScript
371 lines
11 KiB
JavaScript
|
|
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' })
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|