喜喜页面的个人画像。
This commit is contained in:
@@ -1,5 +1,179 @@
|
||||
<template>
|
||||
<scroll-view class="panel-scroll" scroll-y>
|
||||
<view class="card">
|
||||
<text class="section-title">基本信息</text>
|
||||
<view class="field">
|
||||
<text class="label">手机号码</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="local.myPhone"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
placeholder="请输入手机号码"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">名字</text>
|
||||
<input class="input" v-model="local.name" placeholder="请输入名字" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">称呼</text>
|
||||
<input class="input" v-model="local.salutation" placeholder="例如:小王" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">性别</text>
|
||||
<radio-group class="gender-group" @change="onGenderRadioChange">
|
||||
<label class="gender-item">
|
||||
<radio value="男" :checked="local.gender === '男'" color="#e91e8c" />
|
||||
<text class="gender-text">男</text>
|
||||
</label>
|
||||
<label class="gender-item">
|
||||
<radio value="女" :checked="local.gender === '女'" color="#e91e8c" />
|
||||
<text class="gender-text">女</text>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">年龄</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="local.age"
|
||||
type="number"
|
||||
placeholder="请输入年龄"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="section-title">职业与居住</text>
|
||||
<view class="field">
|
||||
<text class="label">行业</text>
|
||||
<input class="input" v-model="local.industry" placeholder="所在行业" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">收入水平</text>
|
||||
<input class="input" v-model="local.incomeLevel" placeholder="收入水平" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">居住区域</text>
|
||||
<input class="input" v-model="local.residentialArea" placeholder="居住区域" placeholder-class="ph" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="section-title">兴趣与关系</text>
|
||||
<view class="field">
|
||||
<text class="label">性格</text>
|
||||
<textarea
|
||||
class="area area--sm"
|
||||
v-model="local.personality"
|
||||
placeholder="性格特点"
|
||||
maxlength="500"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">兴趣爱好</text>
|
||||
<textarea
|
||||
class="area area--sm"
|
||||
v-model="local.hobbies"
|
||||
placeholder="兴趣爱好"
|
||||
maxlength="500"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">喜欢的美食</text>
|
||||
<input class="input" v-model="local.favoriteFood" placeholder="喜欢的美食" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">喜欢的电影类型</text>
|
||||
<input class="input" v-model="local.favoriteMovieGenres" placeholder="例如:科幻、文艺" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">最近关系的小事</text>
|
||||
<textarea
|
||||
class="area area--sm"
|
||||
v-model="local.recentRelationshipNote"
|
||||
placeholder="最近与感情/相处相关的一件小事"
|
||||
maxlength="1000"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="section-title">教育与健康</text>
|
||||
<view class="field">
|
||||
<text class="label">学历</text>
|
||||
<input class="input" v-model="local.education" placeholder="学历" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">专业</text>
|
||||
<input class="input" v-model="local.major" placeholder="专业" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">身高(厘米)</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="local.heightCm"
|
||||
type="number"
|
||||
placeholder="例如 170"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">体重(千克)</text>
|
||||
<input class="input" v-model="local.weightKg" placeholder="例如 65.5" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">健康状况</text>
|
||||
<input class="input" v-model="local.healthStatus" placeholder="健康状况" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">对自己的评价</text>
|
||||
<textarea
|
||||
class="area"
|
||||
v-model="local.selfEvaluation"
|
||||
placeholder="对自己的评价"
|
||||
maxlength="2000"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="section-title">家庭与物质条件</text>
|
||||
<view class="field">
|
||||
<text class="label">父亲职业</text>
|
||||
<input class="input" v-model="local.fatherOccupation" placeholder="父亲职业" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">母亲职业</text>
|
||||
<input class="input" v-model="local.motherOccupation" placeholder="母亲职业" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">兄妹几个</text>
|
||||
<input class="input" v-model="local.siblingsDesc" placeholder="例如:独生子女 / 有一个哥哥" placeholder-class="ph" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">房子(有无、贷款等)</text>
|
||||
<textarea
|
||||
class="area area--sm"
|
||||
v-model="local.houseSituation"
|
||||
placeholder="房产情况"
|
||||
maxlength="500"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">车子(有无等)</text>
|
||||
<input class="input" v-model="local.carSituation" placeholder="车辆情况" placeholder-class="ph" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="label">我是谁(恋爱相关的自我描述)</text>
|
||||
<textarea
|
||||
@@ -20,45 +194,239 @@
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
<button class="btn-save" type="primary" @click="save">保存</button>
|
||||
|
||||
<view class="card">
|
||||
<text class="label">备注</text>
|
||||
<textarea
|
||||
class="area area--sm"
|
||||
v-model="local.remark"
|
||||
placeholder="其他备注"
|
||||
maxlength="1000"
|
||||
:show-confirm-bar="false"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<button class="btn-save" type="primary" :loading="saveSubmitting" :disabled="saveSubmitting" @click="save">
|
||||
保存
|
||||
</button>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { post } from '@/common/request.js';
|
||||
|
||||
const KEY = 'xixi_who_am_i';
|
||||
|
||||
function trimStr(v) {
|
||||
if (v == null) return '';
|
||||
return String(v).trim();
|
||||
}
|
||||
|
||||
/** 中国大陆手机号:1 开头第二位 3-9,共 11 位 */
|
||||
function isValidChinaMobile(phone) {
|
||||
return /^1[3-9]\d{9}$/.test(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前校验:未填写的项不校验;填写了则必须符合规则
|
||||
* @returns {{ ok: true } | { ok: false, message: string }}
|
||||
*/
|
||||
function validateLoveWhoIFields(local) {
|
||||
const phone = trimStr(local.myPhone);
|
||||
if (phone !== '') {
|
||||
if (!isValidChinaMobile(phone)) {
|
||||
return { ok: false, message: '请输入正确的11位手机号' };
|
||||
}
|
||||
}
|
||||
|
||||
const ageStr = trimStr(local.age);
|
||||
if (ageStr !== '') {
|
||||
if (!/^\d+$/.test(ageStr)) {
|
||||
return { ok: false, message: '年龄请填写正整数' };
|
||||
}
|
||||
const age = parseInt(ageStr, 10);
|
||||
if (age < 1 || age > 120) {
|
||||
return { ok: false, message: '年龄需在 1~120 岁' };
|
||||
}
|
||||
}
|
||||
|
||||
const heightStr = trimStr(local.heightCm);
|
||||
if (heightStr !== '') {
|
||||
if (!/^\d+$/.test(heightStr)) {
|
||||
return { ok: false, message: '身高请填写正整数(厘米)' };
|
||||
}
|
||||
const h = parseInt(heightStr, 10);
|
||||
if (h < 50 || h > 250) {
|
||||
return { ok: false, message: '身高需在 50~250 厘米' };
|
||||
}
|
||||
}
|
||||
|
||||
const weightStr = trimStr(local.weightKg);
|
||||
if (weightStr !== '') {
|
||||
const normalized = weightStr.replace(/,/g, '');
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(normalized)) {
|
||||
return { ok: false, message: '体重请填写数字,最多两位小数(千克)' };
|
||||
}
|
||||
const w = parseFloat(normalized);
|
||||
if (!Number.isFinite(w) || w < 15 || w > 400) {
|
||||
return { ok: false, message: '体重需在 15~400 千克' };
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
/** 提交 /api/loveWhoI/add 的请求体(camelCase,与 Spring 默认一致) */
|
||||
function buildLoveWhoIAddPayload(local) {
|
||||
const ageStr = trimStr(local.age);
|
||||
const heightStr = trimStr(local.heightCm);
|
||||
const weightStr = trimStr(local.weightKg);
|
||||
const payload = {
|
||||
myPhone: trimStr(local.myPhone),
|
||||
name: trimStr(local.name),
|
||||
salutation: trimStr(local.salutation),
|
||||
gender: trimStr(local.gender),
|
||||
industry: trimStr(local.industry),
|
||||
incomeLevel: trimStr(local.incomeLevel),
|
||||
residentialArea: trimStr(local.residentialArea),
|
||||
personality: trimStr(local.personality),
|
||||
hobbies: trimStr(local.hobbies),
|
||||
favoriteFood: trimStr(local.favoriteFood),
|
||||
favoriteMovieGenres: trimStr(local.favoriteMovieGenres),
|
||||
recentRelationshipNote: trimStr(local.recentRelationshipNote),
|
||||
education: trimStr(local.education),
|
||||
major: trimStr(local.major),
|
||||
healthStatus: trimStr(local.healthStatus),
|
||||
selfEvaluation: trimStr(local.selfEvaluation),
|
||||
fatherOccupation: trimStr(local.fatherOccupation),
|
||||
motherOccupation: trimStr(local.motherOccupation),
|
||||
siblingsDesc: trimStr(local.siblingsDesc),
|
||||
houseSituation: trimStr(local.houseSituation),
|
||||
carSituation: trimStr(local.carSituation),
|
||||
remark: trimStr(local.remark),
|
||||
};
|
||||
if (ageStr !== '') {
|
||||
const n = parseInt(ageStr, 10);
|
||||
if (Number.isFinite(n)) payload.age = n;
|
||||
}
|
||||
if (heightStr !== '') {
|
||||
const n = parseInt(heightStr, 10);
|
||||
if (Number.isFinite(n)) payload.heightCm = n;
|
||||
}
|
||||
if (weightStr !== '') {
|
||||
const w = parseFloat(weightStr);
|
||||
if (Number.isFinite(w)) payload.weightKg = w;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
function defaultLocal() {
|
||||
return {
|
||||
loveWhoIId: '',
|
||||
myPhone: '',
|
||||
name: '',
|
||||
salutation: '',
|
||||
gender: '',
|
||||
age: '',
|
||||
industry: '',
|
||||
incomeLevel: '',
|
||||
residentialArea: '',
|
||||
personality: '',
|
||||
hobbies: '',
|
||||
favoriteFood: '',
|
||||
favoriteMovieGenres: '',
|
||||
recentRelationshipNote: '',
|
||||
education: '',
|
||||
major: '',
|
||||
heightCm: '',
|
||||
weightKg: '',
|
||||
healthStatus: '',
|
||||
selfEvaluation: '',
|
||||
fatherOccupation: '',
|
||||
motherOccupation: '',
|
||||
siblingsDesc: '',
|
||||
houseSituation: '',
|
||||
carSituation: '',
|
||||
remark: '',
|
||||
selfIntro: '',
|
||||
partnerExpect: '',
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'XixiWhoAmI',
|
||||
data() {
|
||||
return {
|
||||
local: {
|
||||
selfIntro: '',
|
||||
partnerExpect: '',
|
||||
},
|
||||
local: defaultLocal(),
|
||||
saveSubmitting: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
onGenderRadioChange(e) {
|
||||
this.local.gender = e.detail.value || '';
|
||||
},
|
||||
load() {
|
||||
const base = defaultLocal();
|
||||
try {
|
||||
const raw = uni.getStorageSync(KEY);
|
||||
if (raw && typeof raw === 'object') {
|
||||
this.local.selfIntro = raw.selfIntro || '';
|
||||
this.local.partnerExpect = raw.partnerExpect || '';
|
||||
this.local = { ...base, ...raw };
|
||||
} else {
|
||||
this.local = base;
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
this.local = base;
|
||||
}
|
||||
},
|
||||
save() {
|
||||
async save() {
|
||||
if (this.saveSubmitting) return;
|
||||
const check = validateLoveWhoIFields(this.local);
|
||||
if (!check.ok) {
|
||||
uni.showToast({ title: check.message, icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.saveSubmitting = true;
|
||||
try {
|
||||
uni.setStorageSync(KEY, { ...this.local });
|
||||
uni.showToast({ title: '已保存', icon: 'success' });
|
||||
this.$emit('saved');
|
||||
const requestData = buildLoveWhoIAddPayload(this.local);
|
||||
const res = await post('/api/loveWhoI/add', requestData);
|
||||
const payload = res.data && typeof res.data === 'object' ? res.data : {};
|
||||
const ok = res.statusCode === 200 && payload.success === true;
|
||||
|
||||
if (ok) {
|
||||
const row = payload.data;
|
||||
if (row && typeof row === 'object' && row.id) {
|
||||
this.local.loveWhoIId = String(row.id);
|
||||
}
|
||||
try {
|
||||
uni.setStorageSync(KEY, { ...this.local });
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
uni.showToast({ title: payload.message || '已保存', icon: 'success' });
|
||||
this.$emit('saved');
|
||||
} else {
|
||||
const msg = payload.message || `服务端保存失败(${res.statusCode || '?'})`;
|
||||
try {
|
||||
uni.setStorageSync(KEY, { ...this.local });
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
uni.showToast({ title: msg, icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '保存失败', icon: 'none' });
|
||||
console.error('[XixiWhoAmI] save failed:', e);
|
||||
try {
|
||||
uni.setStorageSync(KEY, { ...this.local });
|
||||
} catch (err) {
|
||||
/* ignore */
|
||||
}
|
||||
const msg = e?.message || '网络异常,已暂存本地';
|
||||
uni.showToast({ title: msg, icon: 'none' });
|
||||
} finally {
|
||||
this.saveSubmitting = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -78,12 +446,57 @@ export default {
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 24rpx rgba(233, 30, 140, 0.06);
|
||||
}
|
||||
.section-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #e91e8c;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.input {
|
||||
width: 100%;
|
||||
min-height: 72rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
padding: 16rpx;
|
||||
background: #fff8fa;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.gender-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 40rpx;
|
||||
padding: 8rpx 4rpx;
|
||||
}
|
||||
.gender-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.gender-text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.ph {
|
||||
color: #b0b0b0;
|
||||
}
|
||||
.area {
|
||||
width: 100%;
|
||||
@@ -96,6 +509,9 @@ export default {
|
||||
background: #fff8fa;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.area--sm {
|
||||
min-height: 140rpx;
|
||||
}
|
||||
.btn-save {
|
||||
margin-top: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
|
||||
Reference in New Issue
Block a user