Browse Source

feat(authentication): 新增性别选择弹窗组件并优化表单验证逻辑

新增性别选择弹窗组件 `popupSexSelect.vue`,优化用户信息表单的验证逻辑,确保基本资料、展示信息和服务资料的完整性和一致性。同时,修复了头像上传和表单提交的相关问题。
master
前端-胡立永 4 days ago
parent
commit
6fae05b3dc
4 changed files with 465 additions and 58 deletions
  1. +1
    -1
      otherPages/authentication/list/index.vue
  2. +231
    -44
      otherPages/authentication/serve/index.vue
  3. +117
    -13
      otherPages/authentication/serve/index3.vue
  4. +116
    -0
      otherPages/authentication/serve/popupSexSelect.vue

+ 1
- 1
otherPages/authentication/list/index.vue View File

@ -307,7 +307,7 @@
onLoad(() => {
fetchUserInfo()
store.dispatch('fetchPetTypeOptions')
store.dispatch('fetchPetTypeOptions')
})
</script>


+ 231
- 44
otherPages/authentication/serve/index.vue View File

@ -17,14 +17,27 @@
<view class="flex-colc">
<button class="btn-avatar" :plain="true" :hairline="false" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view class="flex-colc">
<image class="head" :src="baseInfo.info.userImage" mode="widthFix"></image>
<image class="head" :src="form.headImage" mode="aspectFill"></image>
<!-- <image class="head" :src="form.headImage" mode="widthFix"></image> -->
<text class="size-28 color-999 mt10">点击更换头像</text>
</view>
</button>
</view>
<view class="form">
<up-form label-position="left" :model="baseFormData" :rules="baseRules" ref="baseFormRef" labelWidth="200rpx">
<up-form-item label="昵称" prop="userName">
<up-input inputAlign="right" v-model="baseFormData.userName" placeholder="请输入" border="none" fontSize="28rpx"></up-input>
</up-form-item>
<up-form-item label="手机号" prop="userTelephone">
<up-input inputAlign="right" v-model="baseFormData.userTelephone" placeholder="请输入" border="none" fontSize="28rpx"></up-input>
</up-form-item>
<up-form-item label="性别" prop="sex">
<view class="flex-rowr" @click="openSexSelectPopup">
<text>{{ baseFormData.sex || '请选择' }}</text>
<up-icon name="arrow-right" color="#999999" size="32rpx"></up-icon>
</view>
</up-form-item>
</up-form>
</view>
</view>
@ -34,6 +47,29 @@
<text class="size-22 color-ffb fw700">(重要)</text>
</view>
<view class="form">
<up-form label-position="left" :model="formData" :rules="rules" ref="dFormRef" labelWidth="200rpx">
<up-form-item label="个人简介" prop="introduction">
<up-input v-model="formData.introduction"
placeholder="请输入个人简介" border="none"
inputAlign="right"
fontSize="28rpx">
</up-input>
</up-form-item>
<up-form-item label="养宠经验" prop="experience">
<up-input inputAlign="right" v-model="formData.experience" placeholder="请输入养宠经验"
border="none" fontSize="28rpx">
<template #suffix>
</template>
</up-input>
</up-form-item>
<up-form-item label="服务记录" prop="serviceRecord">
<view class="flex-rowr" @click="jumpToServeRecord">
<text>{{ `${serviceCount}` }}</text>
<up-icon name="arrow-right" color="#999999" size="32rpx"></up-icon>
</view>
</up-form-item>
</up-form>
</view>
</view>
@ -42,6 +78,20 @@
服务资料
</view>
<view class="form">
<up-form label-position="left" :model="serveFormData" ref="serveFormRef" labelWidth="200rpx">
<up-form-item label="服务宠物类型" prop="type">
<view class="flex-rowr" @click="openTypeSelectPopup">
<text>{{ typeDesc }}</text>
<up-icon name="arrow-right" color="#999999" size="32rpx"></up-icon>
</view>
</up-form-item>
<up-form-item label="增值服务" prop="serve">
<view class="flex-rowr" @click="openServeSelectPopup">
<text>{{ serveDesc }}</text>
<up-icon name="arrow-right" color="#999999" size="32rpx"></up-icon>
</view>
</up-form-item>
</up-form>
</view>
</view>
@ -51,7 +101,9 @@
<popupTypeSelect ref="popupTypeSelectRef" v-model="form.type" @confirm="fetchUpdate" ></popupTypeSelect>
<popupServeSelect ref="popupServeSelectRef" v-model="form.serve" @confirm="fetchUpdate" ></popupServeSelect>
<popupServeSelect ref="popupServeSelectRef" v-model="serveFormData.serve" @confirm="fetchUpdate" ></popupServeSelect>
<popupSexSelect ref="popupSexSelectRef" v-model="baseFormData.sex" @confirm="fetchUpdate" ></popupSexSelect>
<view class="uni-btn-color"
@click="submit">
@ -68,10 +120,13 @@
import popupTypeSelect from "./popupTypeSelect.vue";
import popupServeSelect from "./popupServeSelect.vue";
import popupSexSelect from "./popupSexSelect.vue";
import { serviceLogList } from '@/api/serviceLog'
import {
binBaseInfo,
} from "@/api/home.js"
import { getUserOne, udpateUser } from '@/api/userTeacher'
import { updateUserInfo } from '@/api/user/user'
const store = useStore()
@ -81,17 +136,109 @@
const userInfo = computed(() => {
return store.getters.userInfo
})
const baseInfo = ref(JSON.parse(uni.getStorageSync("baseInfo")))
const serviceCount = ref(0)
const baseFormData = reactive({
userName: '',
userTelephone: '',
sex: ''
})
const baseRules = {
userName: {
type: 'string',
required: true,
message: '请输入昵称',
trigger: ['blur', 'change']
},
userTelephone: {
type: 'string',
required: true,
message: '请输入手机号',
trigger: ['blur', 'change']
},
sex: {
type: 'string',
required: true,
message: '请选择性别',
trigger: ['blur', 'change']
}
}
const baseFormRef = ref(null)
const formData = reactive({
introduction: '',
experience: '',
serviceRecord: ''
})
const rules = {
introduction: {
type: 'string',
required: true,
message: '请输入个人简介',
trigger: ['blur', 'change']
},
experience: {
type: 'string',
required: true,
message: '请输入养宠经验',
trigger: ['blur', 'change']
}
}
const dFormRef = ref(null)
const form = reactive({
headImage: null,
type: [],
serve: [],
id : 0,
})
const serveFormData = reactive({
serve: []
})
const serveFormRef = ref(null)
const fetchUserInfo = async () => {
try {
const data = await getUserOne(userInfo.value.userId)
if (data) {
//
baseFormData.userName = userInfo.value.userName || ''
baseFormData.userTelephone = data.phone || userInfo.value.userTelephone || ''
baseFormData.sex = data.sex === 0 ? '男' : '女'
//
formData.introduction = data.introduction || ''
formData.experience = data.experience || ''
form.headImage = userInfo.value.userImage || ''
form.id = data.id || 0
//
if (data.petType) {
form.type = [data.petType]
}
//
if (data.license) {
serveFormData.serve = data.license.split(';').map(item => parseInt(item))
}
}
} catch (err) {
console.log('--err', err)
}
}
const fetchUpdate = () => {
//
}
const onChooseAvatar = (res) => {
ossUpload(res.target.avatarUrl)
.then(url => {
@ -101,10 +248,6 @@
})
}
const baseInfoRef = ref(null)
const displayInfoRef = ref(null)
const serveInfoRef = ref(null)
const jumpToServeRecord = () => {
uni.navigateTo({
url: "/otherPages/authentication/serve/record"
@ -115,6 +258,11 @@
const openTypeSelectPopup = () => {
popupTypeSelectRef.value.open()
}
const popupSexSelectRef = ref()
const openSexSelectPopup = () => {
popupSexSelectRef.value.open()
}
const petTypeOptions = computed(() => {
return store.getters.petTypeOptions
})
@ -124,8 +272,10 @@
return '请选择'
}
let descArr = typeIds.map(typeId => {
return petTypeOptions.value.find(item => item.id === typeId).title
let t = petTypeOptions.value.find(item => item.id === typeId)
return t && t.title
})
return descArr.join('、')
@ -139,13 +289,13 @@
popupServeSelectRef.value.open()
}
const serveDesc = computed(() => {
const serveIds = form.serve
const serveIds = serveFormData.serve
if (!serveIds.length) {
return '请选择'
}
let descArr = serveIds.map(serveId => {
return increaseServiceOptions.value.find(option => option.id === serveId)?.title
return increaseServiceOptions.value.find(option => option.id == serveId)?.title
})
return descArr.join('、')
@ -158,55 +308,71 @@
})
}
const getBaseInfo = () => {
binBaseInfo(baseInfo.value.userId).then(res => {
binBaseInfo(userInfo.value.userId).then(res => {
baseInfo.value = res.data
uni.setStorageSync(JSON.stringify(res.data))
// baseInfoRef.value.setData(res.data.info)
})
}
function submit(){
//
baseFormRef.value.validate().then(() => {
//
return dFormRef.value.validate()
}).then(res => {
const submitData = {
id : form.id,
phone: baseFormData.userTelephone,
sex: baseFormData.sex === '男' ? 0 : 1,
introduction: formData.introduction,
experience: formData.experience,
petType: form.type.length > 0 ? form.type[0] : null,
license: serveFormData.serve.join(';'),
}
if (form.headImage) {
updateUserInfo({
userImage : form.headImage,
userName: baseFormData.userName,
id : userInfo.value.id,
userId : userInfo.value.userId,
}).then(() => {
store.dispatch('getUserInfo')
})
}
udpateUser(submitData).then(() => {
uni.showToast({
title: '保存成功',
icon: 'success'
})
setTimeout(uni.navigateBack, 1000, -1)
}).catch(err => {
console.error(err)
uni.showToast({
title: '保存失败',
icon: 'error'
})
})
}).catch(err => {
console.log('表单验证失败', err)
})
}
onShow(() => {
getCountSerivce()
getBaseInfo()
fetchUserInfo()
store.dispatch('fetchPetTypeOptions')
store.dispatch('fetchIncreaseServiceOptions')
})
onMounted(()=> {
// todo: fetch data and init data
serveInfoRef.value.setDataByKey('base', configList.value.pet_basic_services.paramValueText)
})
</script>
<style lang="scss" scoped>
.form{
.form-item {
min-height: 90rpx;
padding-top: 10rpx;
width: 100%;
border-bottom: 1rpx solid #f5f5f5;
}
.flex-rowr {
display: flex;
justify-content: flex-end;
align-items: center;
}
.ml10 {
margin-left: 10rpx;
}
}
.page {
padding-top: 16rpx;
padding-bottom: 150rpx;
@ -234,13 +400,18 @@
.head {
width: 165rpx;
height: auto;
height: 165rpx;
margin-top: 28rpx;
}
.btn-avatar {
border: none;
padding: 0;
.head{
width: 140rpx;
height: 140rpx;
border-radius: 50%;
}
}
.title {
@ -262,13 +433,29 @@
}
.form {
padding: 0 22rpx;
padding: 0 38rpx;
:deep(.form-item){
padding-left: 24rpx;
padding-right: 30rpx;
box-sizing: border-box;
}
.form-item {
min-height: 90rpx;
padding-top: 10rpx;
width: 100%;
border-bottom: 1rpx solid #f5f5f5;
}
.flex-rowr {
display: flex;
justify-content: flex-end;
align-items: center;
}
.ml10 {
margin-left: 10rpx;
}
}
.uni-btn-color{


otherPages/authentication/serve/index - 副本.vue → otherPages/authentication/serve/index3.vue View File

@ -99,6 +99,7 @@
import {
binBaseInfo,
} from "@/api/home.js"
import { getUserOne, udpateUser } from '@/api/userTeacher'
const store = useStore()
@ -113,15 +114,83 @@
const serviceCount = ref(0)
const fetchUserInfo = async () => {
try {
const data = await getUserOne(userInfo.value.userId)
if (data) {
const {
name,
sex,
phone,
experience,
petType,
increaseService
} = data
//
baseInfoRef.value.setData({
userName: name,
userTelephone: phone || userInfo.value.userTelephone,
sex: sex === 0 ? '男' : '女'
})
//
displayInfoRef.value.setData({
jianjie: data.introduction || '',
jingyan: experience
})
// form
form.jianjie = data.introduction || ''
form.jingyan = experience || ''
//
if (petType) {
form.type = [petType]
}
//
if (increaseService && Array.isArray(increaseService)) {
form.serve = increaseService
}
//
nextTick(() => {
//
if (serveInfoRef.value) {
serveInfoRef.value.setData({
type: form.type,
zenz: form.serve
})
}
})
}
} catch (err) {
console.log('--err', err)
}
}
const fetchUpdate = (data) => {
// todo
console.log(data);
if (data) {
Object.assign(form, data)
// jianjiejingyandisplayInfoRef
if (data.jianjie !== undefined || data.jingyan !== undefined) {
const updateData = {}
if (data.jianjie !== undefined) updateData.jianjie = data.jianjie
if (data.jingyan !== undefined) updateData.jingyan = data.jingyan
displayInfoRef.value?.setData(updateData)
}
}
}
const form = reactive({
headImage: null,
type: [],
serve: [],
jianjie: '',
jingyan: ''
})
const onChooseAvatar = (res) => {
@ -162,7 +231,7 @@
},
{
name: "女",
value : '',
value : '',
}
]
}
@ -270,28 +339,63 @@
const getBaseInfo = () => {
binBaseInfo(baseInfo.value.userId).then(res => {
baseInfo.value = res.data
uni.setStorageSync(JSON.stringify(res.data))
// baseInfoRef.value.setData(res.data.info)
uni.setStorageSync('baseInfo', JSON.stringify(res.data.info))
// baseInfoRef
if (baseInfoRef.value) {
baseInfoRef.value.setData(res.data.info)
}
})
}
function submit(){
const data = {
userId: userInfo.value.userId,
name: baseInfo.value.info.userName,
phone: baseInfo.value.info.userTelephone,
sex: baseInfo.value.info.sex === '男' ? 0 : 1,
experience: form.jingyan,
introduction: form.jianjie,
petType: form.type.length > 0 ? form.type[0] : undefined,
increaseService: form.serve.length > 0 ? form.serve : undefined
}
udpateUser(data).then(() => {
uni.showToast({
title: '保存成功',
icon: 'success'
})
}).catch(err => {
console.error(err)
uni.showToast({
title: '保存失败',
icon: 'error'
})
})
}
onShow(() => {
getCountSerivce()
getBaseInfo()
onMounted(()=> {
//
store.dispatch('fetchPetTypeOptions')
store.dispatch('fetchIncreaseServiceOptions')
//
nextTick(() => {
if (serveInfoRef.value && configList.value.pet_basic_services) {
serveInfoRef.value.setDataByKey('base', configList.value.pet_basic_services.paramValueText)
}
})
})
onMounted(()=> {
// todo: fetch data and init data
onShow(() => {
//
getBaseInfo()
getCountSerivce()
serveInfoRef.value.setDataByKey('base', configList.value.pet_basic_services.paramValueText)
//
nextTick(() => {
fetchUserInfo()
})
})
</script>

+ 116
- 0
otherPages/authentication/serve/popupSexSelect.vue View File

@ -0,0 +1,116 @@
<template>
<up-popup :show="show" @close="close" round="10" mode="bottom">
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">选择性别</text>
<up-icon name="close" size="24" @click="close"></up-icon>
</view>
<view class="popup-body">
<view class="option-item" v-for="(item, index) in options" :key="index" @click="selectItem(item)">
<text :class="['option-text', { 'selected': modelValue === item.value }]">{{ item.name }}</text>
</view>
</view>
<view class="popup-footer">
<view class="confirm-btn" @click="confirm">确定</view>
</view>
</view>
</up-popup>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue';
const props = defineProps({
modelValue: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue', 'confirm']);
const show = ref(false);
const selectedValue = ref(props.modelValue);
const options = [
{ name: '男', value: '男' },
{ name: '女', value: '女' }
];
const open = () => {
show.value = true;
selectedValue.value = props.modelValue;
};
const close = () => {
show.value = false;
};
const selectItem = (item) => {
selectedValue.value = item.value;
emit('update:modelValue', item.value);
};
const confirm = () => {
close();
emit('confirm');
};
//
defineExpose({
open,
close
});
</script>
<style lang="scss" scoped>
.popup-content {
padding: 30rpx;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
}
.popup-body {
max-height: 600rpx;
overflow-y: auto;
}
.option-item {
padding: 30rpx 0;
border-bottom: 1px solid #f5f5f5;
}
.option-text {
font-size: 28rpx;
color: #333;
}
.selected {
color: #FFBF60;
font-weight: bold;
}
.popup-footer {
margin-top: 40rpx;
}
.confirm-btn {
height: 80rpx;
line-height: 80rpx;
text-align: center;
background-color: #FFBF60;
color: #fff;
border-radius: 40rpx;
font-size: 28rpx;
}
</style>

Loading…
Cancel
Save