Files
smartDriveEEUniApp/pages-subpackage/xixi/components/xixi-who-am-i.vue
2026-04-06 09:19:29 +08:00

522 lines
15 KiB
Vue
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.

<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
class="area"
v-model="local.selfIntro"
placeholder="例如:我的性格、价值观、在关系里看重什么…"
maxlength="2000"
:show-confirm-bar="false"
/>
</view>
<view class="card">
<text class="label">对另一半的要求</text>
<textarea
class="area"
v-model="local.partnerExpect"
placeholder="例如:希望对方具备的特质、相处方式、底线与期待…"
maxlength="2000"
:show-confirm-bar="false"
/>
</view>
<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: '年龄需在 1120 岁' };
}
}
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: '身高需在 50250 厘米' };
}
}
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: '体重需在 15400 千克' };
}
}
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: 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 = { ...base, ...raw };
} else {
this.local = base;
}
} catch (e) {
this.local = base;
}
},
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 {
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) {
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;
}
},
},
};
</script>
<style scoped>
.panel-scroll {
height: 100%;
box-sizing: border-box;
padding: 24rpx 28rpx 32rpx;
}
.card {
background: #fff;
border-radius: 20rpx;
padding: 28rpx;
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: 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%;
min-height: 220rpx;
font-size: 28rpx;
line-height: 1.6;
color: #333;
box-sizing: border-box;
padding: 16rpx;
background: #fff8fa;
border-radius: 12rpx;
}
.area--sm {
min-height: 140rpx;
}
.btn-save {
margin-top: 12rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #ff6b9d, #e91e8c);
border: none;
}
</style>