用户中心样式调整

This commit is contained in:
2026-04-01 18:10:14 +08:00
parent dabf7a3ec8
commit c83f52dfad
12 changed files with 1640 additions and 504 deletions
+1 -1
View File
@@ -106,7 +106,7 @@ onShow(()=> {
<view class="bg-white rounded-36rpx overflow-hidden mt-18rpx py-30rpx">
<view class="flex-center-sb pr-20rpx mb-8rpx">
<view class="flex items-center">
<view class="w-8rpx h-30rpx bg-#00A76D"></view>
<view class="w-8rpx h-40rpx bg-#00A76D" style="margin-left: 10rpx;border-radius: 20rpx;"></view>
<text class="ml-10rpx text-34rpx text-#333">{{ t('pages-user.balance.detail-list') }}</text>
</view>
<view class="flex items-center">
+236 -70
View File
@@ -1,8 +1,8 @@
<script setup lang="ts">
import ChooseImage from "@/components/choose-image/choose-image.vue";
import {appFeedbackAddPost} from "@/service";
import { appFeedbackAddPost } from "@/service";
import { z } from 'zod';
const {t} = useI18n()
const { t } = useI18n()
const form = ref({
content: '',
@@ -24,9 +24,37 @@ const createValidationSchema = () => {
}
const chooseImageRef = ref()
const showNoticePopup = ref(true)
const confirmCountdown = ref(5)
let countdownTimer: ReturnType<typeof setInterval> | null = null
// 校验单个字段
const validateField = (field: keyof typeof form.value) => {
function startNoticeCountdown() {
if (countdownTimer) {
clearInterval(countdownTimer)
}
confirmCountdown.value = 5
countdownTimer = setInterval(() => {
if (confirmCountdown.value <= 1) {
confirmCountdown.value = 0
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
return
}
confirmCountdown.value -= 1
}, 1000)
}
function handleCloseNoticePopup() {
if (confirmCountdown.value > 0) {
return
}
showNoticePopup.value = false
}
// 校验单个字段(只校验 schema 中存在的字段)
const validateField = (field: 'content' | 'contactPhone') => {
try {
const schema = createValidationSchema()
const fieldSchema = schema.shape[field]
@@ -64,12 +92,12 @@ const handleSubmit = () => {
if (!validateForm()) {
return
}
appFeedbackAddPost({
body: {
...form.value
}
}).then(res=> {
}).then(res => {
uni.showToast({
title: t('toast.submitSuccess'),
icon: 'none'
@@ -88,79 +116,217 @@ const handleChooseImage = () => {
function onImageChange(files: string[]) {
form.value.images = files[0]
}
onLoad(() => {
startNoticeCountdown()
})
onUnload(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
})
</script>
<template>
<navbar :title="t('pages-user.complaints.title')" />
<view class="px-18rpx">
<view class="mb-27rpx mt-32rpx text-28rpx text-#333">
{{ t('pages-user.complaints.description') }}
</view>
<view class="bg-white rounded-14rpx px-18rpx pt-30rpx pb-24rpx">
<view class="text-28rpx text-#333 mb-28rpx">{{ t('pages-user.complaints.feedback-content') }}</view>
<view
class="min-h-234rpx box-border bg-#F6F6F6 rounded-14rpx overflow-hidden px-18rpx py-10rpx"
>
<wd-textarea
v-model="form.content"
:maxlength="500"
custom-class="!bg-#F6F6F6"
custom-textarea-container-class="!bg-#F6F6F6"
no-border
auto-height
:placeholder="t('pages-user.complaints.feedback-content-placeholder')"
@blur="validateField('content')"
/>
</view>
<view class="complaints-root">
<navbar :title="t('pages-user.complaints.title')" />
<view class="complaints-page">
<view>
<view class="text-28rpx text-#333 mt-32rpx mb-24rpx">{{ t('pages-user.complaints.image') }}:</view>
<view @click="handleChooseImage" class="relative w-210rpx h-210rpx">
<image
v-if="form.images"
src="@img/chef/113.png"
class="absolute top--10rpx right--10rpx z-10 w-36rpx h-36rpx"
></image>
<image
v-if="!form.images"
src="@img/chef/112.png"
class="w-210rpx h-210rpx"
></image>
<image
v-else
:src="form.images"
class="w-210rpx h-210rpx rounded-16rpx"
mode="aspectFill"
></image>
<view class="form-card">
<view class="field">
<view class="field__label">{{ t('pages-user.complaints.feedback-content') }}</view>
<view class="field__box field__box--textarea">
<wd-textarea v-model="form.content" :maxlength="500" custom-class="!bg-transparent"
custom-textarea-container-class="!bg-transparent" no-border auto-height
:placeholder="t('pages-user.complaints.feedback-content-placeholder')" @blur="validateField('content')" />
</view>
</view>
</view>
</view>
<view class="mt-14rpx flex-center-sb bg-white h-86rpx rounded-14rpx px-18rpx">
<view class="text-28rpx text-#333 ">{{ t('pages-user.complaints.contact-information') }}:</view>
<wd-input
no-border
custom-class="!p-[12rpx+20rpx] rounded-10rpx"
v-model.trim="form.contactPhone"
placeholderStyle="font-size: 28rpx;line-height: 40rpx;color: #B1B1B1; text-align: right;"
:placeholder="t('common.enter')"
:maxlength="50"
custom-input-class="text-right"
@blur="validateField('contactPhone')"
/>
</view>
<view class="mt-38rpx text-28rpx text-#999">
{{ t('pages-user.complaints.contact-information-tip') }}
<view class="field field--mt">
<view class="field__label">{{ t('pages-user.complaints.image') }}</view>
<view class="upload" @click="handleChooseImage">
<image v-if="form.images" src="@img/chef/113.png" class="upload__remove"></image>
<image v-if="!form.images" src="@img/chef/112.png" class="upload__placeholder"></image>
<image v-else :src="form.images" class="upload__image" mode="aspectFill"></image>
</view>
</view>
<view class="field field--mt">
<view class="field__label">{{ t('pages-user.complaints.contact-information') }}</view>
<view class="field__box field__box--input">
<wd-input v-model.trim="form.contactPhone" no-border custom-class="!p-0 !bg-transparent"
custom-input-class="complaints-input" :placeholder="t('pages-user.complaints.contact-information-placeholder')" :maxlength="50"
@blur="validateField('contactPhone')" />
</view>
</view>
</view>
<view class="complaints-page__desc">
{{ t('pages-user.complaints.description') }}
</view>
<!-- 底部固定提交按钮 -->
<view class="bottom-actions">
<wd-button custom-class="submit-btn" block @click="handleSubmit">
{{ t('common.submit') }}
</wd-button>
</view>
<ChooseImage ref="chooseImageRef" @change="onImageChange" />
<wd-popup v-model="showNoticePopup" :close-on-click-modal="false" custom-class="!bg-transparent">
<view class="notice-dialog">
<view class="notice-dialog__title">{{ t('pages-user.complaints.title') }}</view>
<view class="notice-dialog__content">
{{ t('pages-user.complaints.contact-information-tip') }}
</view>
<wd-button
custom-class="notice-dialog__btn"
block
:disabled="confirmCountdown > 0"
@click="handleCloseNoticePopup"
>
{{ confirmCountdown > 0 ? `${t('common.gotIt')} (${confirmCountdown}s)` : t('common.gotIt') }}
</wd-button>
</view>
</wd-popup>
</view>
<fixed-bottom-large-btn
class="z-100"
fixed
:text="`${t('common.submit')}`"
@click="handleSubmit"
/>
<ChooseImage ref="chooseImageRef" @change="onImageChange" />
</view>
</template>
<style lang="scss" scoped>
.complaints-page {
padding: 32rpx 30rpx calc(160rpx + env(safe-area-inset-bottom));
background: #fff;
min-height: 100vh;
box-sizing: border-box;
}
.complaints-page__desc {
font-size: 22rpx;
line-height: 36rpx;
color: #333;
margin-bottom: 18rpx;
text-align: center;
}
.form-card {
background: #fff;
border-radius: 24rpx;
padding: 26rpx 24rpx 18rpx;
}
.field__label {
font-size: 26rpx;
line-height: 36rpx;
color: #333;
font-weight: 700;
margin-bottom: 16rpx;
}
.field--mt {
margin-top: 26rpx;
}
.field__box {
border: 1rpx solid #ececec;
border-radius: 18rpx;
background: #fff;
overflow: hidden;
}
.field__box--textarea {
padding: 14rpx 16rpx;
min-height: 240rpx;
box-sizing: border-box;
}
.field__box--input {
padding: 18rpx 16rpx;
box-sizing: border-box;
}
:deep(.complaints-input) {
text-align: left;
font-size: 26rpx;
line-height: 36rpx;
color: #333;
}
.upload {
width: 176rpx;
height: 176rpx;
border: 1rpx solid #ececec;
border-radius: 18rpx;
overflow: hidden;
position: relative;
background: #fff;
}
.upload__placeholder,
.upload__image {
width: 176rpx;
height: 176rpx;
}
.upload__remove {
position: absolute;
top: -10rpx;
right: -10rpx;
z-index: 2;
width: 36rpx;
height: 36rpx;
}
.bottom-actions {
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: calc(36rpx + env(safe-area-inset-bottom));
z-index: 20;
}
:deep(.submit-btn) {
height: 108rpx !important;
border-radius: 999rpx !important;
background: #111 !important;
color: #fff !important;
font-size: 32rpx !important;
font-weight: 800 !important;
letter-spacing: 0.06em;
}
.notice-dialog {
width: 620rpx;
background: #fff;
border-radius: 24rpx;
padding: 36rpx 28rpx 28rpx;
box-sizing: border-box;
}
.notice-dialog__title {
text-align: center;
font-size: 30rpx;
line-height: 40rpx;
color: #222;
font-weight: 700;
}
.notice-dialog__content {
margin-top: 18rpx;
margin-bottom: 28rpx;
font-size: 26rpx;
line-height: 38rpx;
color: #666;
// text-align: center;
}
:deep(.notice-dialog__btn) {
height: 84rpx !important;
border-radius: 999rpx !important;
font-size: 28rpx !important;
font-weight: 700 !important;
}
</style>
+6 -5
View File
@@ -101,11 +101,11 @@ function handleSubmit() {
bg-color="#fff"
>
<template #top>
<navbar />
<navbar :title="t('pages-user.coupon.title')" />
<view class="px-30rpx pb-42rpx">
<view class="text-46rpx text-#333 font-bold lh-46rpx mb-36rpx mt-10rpx">
<!-- <view class="text-46rpx text-#333 font-bold lh-46rpx mb-36rpx mt-10rpx">
{{ t("pages-user.coupon.title") }}</view
>
> -->
<view
class="flex items-center h-88rpx px-30rpx bg-#F2F3F6 rounded-88rpx box-border"
>
@@ -131,10 +131,11 @@ function handleSubmit() {
</view>
</template>
<view class="px-30rpx my-38rpx" v-if="isSearch">
<view class="px-30rpx my-34rpx" style="position: fixed; bottom: 10rpx; left: 0; right: 0; z-index: 100;" v-if="isSearch">
<wd-button
block
custom-class="!h-108rpx !text-36rpx !rounded-16rpx"
custom-class="!h-98rpx !text-32rpx !rounded-46rpx"
style="background-color: #000;"
:disabled="isDisabled"
@click="handleSubmit"
>
+37 -48
View File
@@ -1,12 +1,20 @@
<!--
* @Author: ISFP_T 68358856@qq.com
* @Date: 2026-02-25 10:02:44
* @LastEditors: ISFP_T 68358856@qq.com
* @LastEditTime: 2026-04-01 15:51:38
* @FilePath: \chef-link-uniapp\src\pages-user\pages\edit-nickname\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script lang="ts" setup>
import Config from '@/config'
import {useUserStore} from '@/store'
import { useUserStore } from '@/store'
import { appUserEditUserInfoPost } from '@/service'
import {debounce} from 'throttle-debounce';
import {z} from "zod";
import { debounce } from 'throttle-debounce';
import { z } from "zod";
import * as R from "ramda";
const {t} = useI18n()
const { t } = useI18n()
const userStore = useUserStore()
const form = ref({
@@ -15,8 +23,8 @@ const form = ref({
})
const FormSchema = z.object({
firstName: z.string().min(1, {message: t('pages-login.prompt.first-name')}),
surname: z.string().min(1, {message: t('pages-login.prompt.last-name')}),
firstName: z.string().min(1, { message: t('pages-login.prompt.first-name') }),
surname: z.string().min(1, { message: t('pages-login.prompt.last-name') }),
})
@@ -26,10 +34,10 @@ function checkForm(): boolean {
const fieldErrorMessage = validateFormField.error.flatten().fieldErrors
const errorMessage: string | undefined = R.path([0, 0], R.values(fieldErrorMessage))
errorMessage &&
uni.showToast({
title: errorMessage,
icon: 'none',
})
uni.showToast({
title: errorMessage,
icon: 'none',
})
}
return validateFormField.success
}
@@ -42,7 +50,7 @@ async function submit() {
...form.value,
}
})
await uni.showToast({title: t('common.prompt.save-successfully'), icon: 'none'})
await uni.showToast({ title: t('common.prompt.save-successfully'), icon: 'none' })
await userStore.getUserInfo()
setTimeout(uni.navigateBack, 1000)
} catch (e) {
@@ -57,47 +65,28 @@ const handleSubmit = R.when(checkForm, debounce(Config.debounceLongTime, submit,
<template>
<view>
<navbar :title="t('navbar-nickname')"/>
<navbar :title="t('navbar-nickname')" />
<view class="py-36rpx px-30rpx bg-#fff">
<view class="">
<view class="text-28-bold">{{ t("pages-login.sign-up.first-name") }}</view>
<view
class="mt-20rpx flex px-30rpx items-center h-88rpx border-2rpx border-solid border-#D4D4D4 rounded-20rpx">
<wd-input
no-border
clearable
:focus-when-clear="false"
:cursorSpacing="20"
:maxlength="40"
v-model.trim="form.firstName"
custom-class="flex-1"
placeholder=""
>
</wd-input>
</view>
</view>
<view class="mt-36rpx">
<view class="text-28-bold">{{ t("pages-login.sign-up.last-name") }}</view>
<view
class="mt-20rpx flex px-30rpx items-center h-88rpx border-2rpx border-solid border-#D4D4D4 rounded-20rpx">
<wd-input
no-border
clearable
:focus-when-clear="false"
use-prefix-slot
:maxlength="40"
:cursorSpacing="20"
v-model.trim="form.surname"
custom-class="flex-1"
placeholder=""
>
</wd-input>
</view>
<view class="">
<view class="text-28-bold">{{ t("pages-login.sign-up.first-name") }}</view>
<view class="mt-20rpx flex px-30rpx items-center h-88rpx border-2rpx border-solid border-#D4D4D4 rounded-20rpx">
<wd-input no-border clearable :focus-when-clear="false" :cursorSpacing="20" :maxlength="40"
v-model.trim="form.firstName" custom-class="flex-1" placeholder="">
</wd-input>
</view>
</view>
<view class="mt-36rpx">
<view class="text-28-bold">{{ t("pages-login.sign-up.last-name") }}</view>
<view class="mt-20rpx flex px-30rpx items-center h-88rpx border-2rpx border-solid border-#D4D4D4 rounded-20rpx">
<wd-input no-border clearable :focus-when-clear="false" use-prefix-slot :maxlength="40" :cursorSpacing="20"
v-model.trim="form.surname" custom-class="flex-1" placeholder="">
</wd-input>
</view>
</view>
</view>
<view class="mt-318rpx px-30rpx">
<wd-button custom-class="!h-108rpx !text-36rpx font-bold !rounded-16rpx" block @click="handleSubmit">
<view class="mt-318rpx px-30rpx" style="position: fixed; bottom: 30rpx; left: 0; right: 0; z-index: 100;">
<wd-button custom-class="!h-108rpx !text-36rpx font-bold !rounded-46rpx" block @click="handleSubmit">
{{ t('common.save') }}
</wd-button>
</view>
+2 -2
View File
@@ -59,9 +59,9 @@ onLoad(()=> {
:auto="false"
>
<template #top>
<navbar />
<navbar :title="t('pages.mine.help')" />
<view class="px-30rpx pb-42rpx">
<view class="text-46rpx text-#333 font-bold lh-46rpx mb-36rpx mt-10rpx">{{ t("pages.mine.help") }}</view>
<!-- <view class="text-46rpx text-#333 font-bold lh-46rpx mb-36rpx mt-10rpx">{{ t("pages.mine.help") }}</view> -->
<view
class="flex items-center h-88rpx px-30rpx bg-#F2F3F6 rounded-88rpx box-border"
>
@@ -0,0 +1,464 @@
<script setup lang="ts">
import { computed } from 'vue'
type PayPwdMode = 'set' | 'change'
const props = defineProps<{
t: (key: string) => string
isSend: number
payPwdMode: PayPwdMode
showLoginPwdPopup: boolean
showLoginPwdForgetPopup: boolean
showPayPwdPopup: boolean
showPayPwdForgetPopup: boolean
loginPwdForm: { oldLoginPwd: string; newLoginPwd: string; confirmPwd: string }
loginPwdForgetForm: { phone: string; captcha: string; newLoginPwd: string; confirmPwd: string }
payPwdSetForm: { phone: string; captcha: string; payPwd: string; confirmPwd: string }
payPwdChangeForm: { oldPayPwd: string; newPayPwd: string; confirmPwd: string }
payPwdForgetForm: { phone: string; captcha: string; payPwd: string; confirmPwd: string }
}>()
const emit = defineEmits<{
(e: 'update:showLoginPwdPopup', value: boolean): void
(e: 'update:showLoginPwdForgetPopup', value: boolean): void
(e: 'update:showPayPwdPopup', value: boolean): void
(e: 'update:showPayPwdForgetPopup', value: boolean): void
(e: 'submit-login-pwd'): void
(e: 'submit-login-pwd-forget'): void
(e: 'submit-pay-pwd-set'): void
(e: 'submit-pay-pwd-change'): void
(e: 'submit-pay-pwd-forget'): void
(e: 'request-login-forget-code'): void
(e: 'request-pay-set-code'): void
(e: 'request-pay-forget-code'): void
}>()
const loginPwdPopupVisible = computed({
get: () => props.showLoginPwdPopup,
set: (value: boolean) => emit('update:showLoginPwdPopup', value),
})
const loginPwdForgetPopupVisible = computed({
get: () => props.showLoginPwdForgetPopup,
set: (value: boolean) => emit('update:showLoginPwdForgetPopup', value),
})
const payPwdPopupVisible = computed({
get: () => props.showPayPwdPopup,
set: (value: boolean) => emit('update:showPayPwdPopup', value),
})
const payPwdForgetPopupVisible = computed({
get: () => props.showPayPwdForgetPopup,
set: (value: boolean) => emit('update:showPayPwdForgetPopup', value),
})
function openLoginPwdForget() {
emit('update:showLoginPwdPopup', false)
emit('update:showLoginPwdForgetPopup', true)
}
function openPayPwdForget() {
emit('update:showPayPwdPopup', false)
emit('update:showPayPwdForgetPopup', true)
}
</script>
<template>
<view>
<wd-popup
v-model="loginPwdPopupVisible"
:close-on-click-modal="false"
position="bottom"
custom-class="ios-popup-mask"
>
<view class="ios-dialog">
<view class="ios-dialog__header">
<view class="ios-dialog__title">{{ t('navbar-change-password') }}</view>
<view class="ios-dialog__close" @click="emit('update:showLoginPwdPopup', false)">×</view>
</view>
<view class="ios-dialog__body">
<wd-input
v-model.trim="loginPwdForm.oldLoginPwd"
no-border
show-password
clearable
:maxlength="20"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-old-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="loginPwdForm.newLoginPwd"
no-border
show-password
clearable
:maxlength="16"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="loginPwdForm.confirmPwd"
no-border
show-password
clearable
:maxlength="16"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password-again')"
/>
<view class="ios-dialog__link-row">
<text class="ios-dialog__link" @click="openLoginPwdForget">
{{ t('navbar-forget-password') }}?
</text>
</view>
</view>
<view class="ios-dialog__footer">
<wd-button custom-class="ios-dialog__btn ios-dialog__btn--primary" block @click="emit('submit-login-pwd')">
{{ t('common.confirm') }}
</wd-button>
</view>
</view>
</wd-popup>
<wd-popup
v-model="loginPwdForgetPopupVisible"
:close-on-click-modal="false"
position="bottom"
custom-class="ios-popup-mask"
>
<view class="ios-dialog">
<view class="ios-dialog__header">
<view class="ios-dialog__title">{{ t('navbar-forget-password') }}</view>
<view class="ios-dialog__close" @click="emit('update:showLoginPwdForgetPopup', false)">×</view>
</view>
<view class="ios-dialog__body">
<wd-input :model-value="loginPwdForgetForm.phone" disabled disabledColor="transparent" no-border />
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="loginPwdForgetForm.captcha"
style="color:#000 !important;"
no-border
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-verification-code')"
>
<template #suffix>
<wd-button :disabled="!!isSend" custom-class="ios-code-btn" @click="emit('request-login-forget-code')">
{{ isSend ? isSend + 'S' : t('common.obtain') }}
</wd-button>
</template>
</wd-input>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="loginPwdForgetForm.newLoginPwd"
style="color:#000 !important;"
no-border
show-password
clearable
:maxlength="20"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="loginPwdForgetForm.confirmPwd"
no-border
show-password
clearable
:maxlength="20"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password-again')"
/>
</view>
<view class="ios-dialog__footer">
<wd-button custom-class="ios-dialog__btn ios-dialog__btn--primary" block @click="emit('submit-login-pwd-forget')">
{{ t('common.confirm') }}
</wd-button>
</view>
</view>
</wd-popup>
<wd-popup
v-model="payPwdPopupVisible"
:close-on-click-modal="false"
position="bottom"
custom-class="ios-popup-mask"
>
<view class="ios-dialog">
<view class="ios-dialog__header">
<view class="ios-dialog__title">
{{ payPwdMode === 'set' ? t('navbar-set-payment-password') : t('navbar-change-payment-password') }}
</view>
<view class="ios-dialog__close" @click="emit('update:showPayPwdPopup', false)">×</view>
</view>
<view class="ios-dialog__body" v-if="payPwdMode === 'set'">
<wd-input :model-value="payPwdSetForm.phone" disabled disabledColor="transparent" no-border />
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdSetForm.captcha"
no-border
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-verification-code')"
>
<template #suffix>
<wd-button :disabled="!!isSend" custom-class="ios-code-btn" @click="emit('request-pay-set-code')">
{{ isSend ? isSend + 'S' : t('common.obtain') }}
</wd-button>
</template>
</wd-input>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdSetForm.payPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdSetForm.confirmPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password-again')"
/>
</view>
<view class="ios-dialog__body" v-else>
<wd-input
v-model.trim="payPwdChangeForm.oldPayPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-old-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdChangeForm.newPayPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdChangeForm.confirmPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password-again')"
/>
<view class="ios-dialog__link-row">
<text class="ios-dialog__link" @click="openPayPwdForget">
{{ t('navbar-forget-password') }}?
</text>
</view>
</view>
<view class="ios-dialog__footer">
<wd-button
custom-class="ios-dialog__btn ios-dialog__btn--primary"
block
@click="payPwdMode === 'set' ? emit('submit-pay-pwd-set') : emit('submit-pay-pwd-change')"
>
{{ t('common.confirm') }}
</wd-button>
</view>
</view>
</wd-popup>
<wd-popup
v-model="payPwdForgetPopupVisible"
:close-on-click-modal="false"
position="bottom"
custom-class="ios-popup-mask"
>
<view class="ios-dialog">
<view class="ios-dialog__header">
<view class="ios-dialog__title">{{ t('navbar-forget-payment-password') }}</view>
<view class="ios-dialog__close" @click="emit('update:showPayPwdForgetPopup', false)">×</view>
</view>
<view class="ios-dialog__body">
<wd-input :model-value="payPwdForgetForm.phone" disabled disabledColor="transparent" no-border />
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdForgetForm.captcha"
style="color:#000 !important;"
no-border
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-verification-code')"
>
<template #suffix>
<wd-button :disabled="!!isSend" custom-class="ios-code-btn" @click="emit('request-pay-forget-code')">
{{ isSend ? isSend + 'S' : t('common.obtain') }}
</wd-button>
</template>
</wd-input>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdForgetForm.payPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password')"
/>
<view class="ios-dialog__divider" />
<wd-input
v-model.trim="payPwdForgetForm.confirmPwd"
no-border
show-password
clearable
:maxlength="6"
:placeholder="t('pages-user.pay-password.input-placeholder.enter-new-password-again')"
/>
</view>
<view class="ios-dialog__footer">
<wd-button custom-class="ios-dialog__btn ios-dialog__btn--primary" block @click="emit('submit-pay-pwd-forget')">
{{ t('common.confirm') }}
</wd-button>
</view>
</view>
</wd-popup>
</view>
</template>
<style scoped lang="scss">
.ios-dialog {
width: 100vw;
max-height: 88vh;
background: #fff;
border-radius: 40rpx 40rpx 0 0;
overflow: hidden;
padding-bottom: env(safe-area-inset-bottom);
box-sizing: border-box;
}
.ios-dialog__header {
height: 96rpx;
padding: 0 28rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.ios-dialog__title {
text-align: center;
font-size: 36rpx;
line-height: 44rpx;
color: #1c1c1e;
font-weight: 600;
}
.ios-dialog__close {
position: absolute;
right: 28rpx;
top: 50%;
transform: translateY(-50%);
width: 56rpx;
height: 56rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 44rpx;
line-height: 44rpx;
color: #222;
}
.ios-dialog__body {
margin: 0 24rpx 12rpx;
background: #fff;
}
.ios-dialog__divider {
display: none;
}
.ios-dialog__link-row {
padding: 12rpx 8rpx 0;
display: flex;
justify-content: flex-end;
}
.ios-dialog__link {
font-size: 34rpx;
line-height: 34rpx;
color: #3c3c43;
font-weight: 500;
}
.ios-dialog__footer {
padding: 210rpx 24rpx 24rpx;
}
:deep(.ios-dialog__btn) {
height: 88rpx !important;
border-radius: 999rpx !important;
font-size: 34rpx !important;
font-weight: 600 !important;
}
:deep(.ios-dialog__btn--primary) {
background: #000 !important;
color: #fff !important;
}
:deep(.ios-code-btn) {
min-width: 92rpx !important;
height: 52rpx !important;
padding: 0 20rpx !important;
border-radius: 16rpx !important;
font-size: 26rpx !important;
font-weight: 400 !important;
line-height: 52rpx !important;
background: #000 !important;
color: #fff !important;
border: none !important;
}
:deep(.ios-code-btn.is-disabled) {
background: #8e8e93 !important;
color: #fff !important;
opacity: 1 !important;
}
:deep(.wd-input) {
height: 84rpx !important;
border: 1rpx solid #dedede !important;
border-radius: 24rpx !important;
padding: 0 24rpx !important;
box-sizing: border-box !important;
display: flex;
align-items: center;
margin-bottom: 14rpx;
}
:deep(.wd-input__inner) {
flex: 1;
min-width: 0;
border: none !important;
margin-bottom: 0 !important;
padding: 0 !important;
}
:deep(.wd-input__suffix) {
display: flex;
align-items: center;
flex-shrink: 0;
margin-left: 12rpx;
}
:deep(.ios-popup-mask) {
background: transparent !important;
}
:deep(.ios-popup-mask.wd-popup--bottom) {
border-radius: 40rpx 40rpx 0 0 !important;
}
</style>
+386 -66
View File
@@ -1,21 +1,260 @@
<script setup lang="ts">
import {useMessage} from "wot-design-uni";
import {useConfigStore, useUserStore} from "@/store";
import {conversionMobile} from "@/utils";
import {Agreement} from "@/constant/enums";
import ChooseLanguage from "./components/choose-language/choose-language.vue";
import Logout from "./components/log-out/log-out.vue";
import PasswordDialogs from "./components/password-dialogs/password-dialogs.vue";
import Config from "@/config";
import {appUserLogOffPost} from "@/service";
import {appUserLogOffPost, appUserEditLoginPwdPost, appUserForgetPwdPost} from "@/service";
import { setPayPwd, editPayPwd, forgetPayPwd } from "@/pages-user/service";
import { SmsType } from "@/constant/enums";
import { z } from "zod";
import useGetMsgCode from "@/hooks/useGetMsgCode";
const {t} = useI18n();
const {locale} = useI18n();
const userStore = useUserStore();
const configStore = useConfigStore()
const message = useMessage();
const currentVersion = ref(configStore.appVersion)
const chooseLanguageRef = ref<InstanceType<typeof ChooseLanguage>>()
const logoutRef = ref<InstanceType<typeof Logout>>()
const currentLanguageLabel = computed(() => (locale.value === 'en' ? 'English' : '中文'))
const { isSend, getMsgCode } = useGetMsgCode()
const showLoginPwdPopup = ref(false)
const showLoginPwdForgetPopup = ref(false)
const showPayPwdPopup = ref(false)
const showPayPwdForgetPopup = ref(false)
const payPwdMode = ref<'set' | 'change'>('set')
const loginPwdForm = ref({
oldLoginPwd: '',
newLoginPwd: '',
confirmPwd: '',
})
const loginPwdSchema = computed(() =>
z
.object({
oldLoginPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-old-password') }),
newLoginPwd: z
.string()
.min(8, { message: t('pages-user.pay-password.password-length-limit') })
.max(16, { message: t('pages-user.pay-password.password-length-limit') }),
confirmPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password-again') }),
})
.refine((data) => data.newLoginPwd === data.confirmPwd, {
path: ['confirmPwd'],
message: t('pages-user.pay-password.two-passwords-inconsistent'),
})
)
function validateBySchema(schema: z.ZodTypeAny, data: any) {
const res = schema.safeParse(data)
if (!res.success) {
const errors = res.error.flatten().fieldErrors
const first = Object.values(errors).find((arr) => Array.isArray(arr) && arr.length)?.[0]
if (first) uni.showToast({ title: String(first), icon: 'none' })
return false
}
return true
}
async function submitLoginPwd() {
if (!validateBySchema(loginPwdSchema.value, loginPwdForm.value)) return
await appUserEditLoginPwdPost({ body: { ...loginPwdForm.value } })
uni.showToast({ title: t('pages-user.password.change-password-successfully'), icon: 'none' })
showLoginPwdPopup.value = false
loginPwdForm.value = { oldLoginPwd: '', newLoginPwd: '', confirmPwd: '' }
setTimeout(() => {
userStore.clear()
uni.navigateTo({ url: Config.loginPath })
}, 1000)
}
const loginPwdForgetForm = ref({
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
confirmPwd: '',
newLoginPwd: '',
})
const loginPwdForgetSchema = computed(() =>
z
.object({
captcha: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-verification-code') }),
confirmPwd: z
.string()
.min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password') })
.regex(/^\d{6}$/, { message: t('pages-user.pay-password.enter-6-digit-password') }),
newLoginPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password-again') }),
})
.refine((data) => data.confirmPwd === data.newLoginPwd, {
path: ['newLoginPwd'],
message: t('pages-user.pay-password.two-passwords-inconsistent'),
})
)
function openLoginPwdPopup() {
showLoginPwdPopup.value = true
}
function openPayPwdPopup() {
payPwdMode.value = userStore.userInfo?.payPwd ? 'change' : 'set'
showPayPwdPopup.value = true
}
function requestLoginForgetCode() {
if (isSend.value > 0) return
getMsgCode({
type: SmsType.USER_FORGET_PASSWORD,
phone: loginPwdForgetForm.value.phone,
areaCode: loginPwdForgetForm.value.areaCode,
})
}
async function submitLoginPwdForget() {
if (!validateBySchema(loginPwdForgetSchema.value, loginPwdForgetForm.value)) return
await appUserForgetPwdPost({ body: { ...loginPwdForgetForm.value } })
uni.showToast({ title: t('pages-user.password.forget-password-successfully'), icon: 'none' })
await userStore.getUserInfo()
showLoginPwdForgetPopup.value = false
loginPwdForgetForm.value = {
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
confirmPwd: '',
newLoginPwd: '',
}
}
const payPwdSetForm = ref({
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
payPwd: '',
confirmPwd: '',
})
const payPwdSetSchema = computed(() =>
z
.object({
captcha: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-verification-code') }),
payPwd: z
.string()
.min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password') })
.regex(/^\d{6}$/, { message: t('pages-user.pay-password.enter-6-digit-password') }),
confirmPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password-again') }),
})
.refine((data) => data.payPwd === data.confirmPwd, {
path: ['confirmPwd'],
message: t('pages-user.pay-password.two-passwords-inconsistent'),
})
)
function requestPaySetCode() {
if (isSend.value > 0) return
getMsgCode({
type: SmsType.USER_SET_PAYMENT_PASSWORD,
phone: payPwdSetForm.value.phone,
areaCode: payPwdSetForm.value.areaCode,
})
}
async function submitPayPwdSet() {
if (!validateBySchema(payPwdSetSchema.value, payPwdSetForm.value)) return
await setPayPwd(payPwdSetForm.value as any)
uni.showToast({ title: t('pages-user.pay-password.set-payment-password-successfully'), icon: 'none' })
await userStore.getUserInfo()
showPayPwdPopup.value = false
payPwdSetForm.value = {
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
payPwd: '',
confirmPwd: '',
}
}
const payPwdChangeForm = ref({
oldPayPwd: '',
newPayPwd: '',
confirmPwd: '',
})
const payPwdChangeSchema = computed(() =>
z
.object({
oldPayPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-old-password') }),
newPayPwd: z
.string()
.min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password') })
.regex(/^\d{6}$/, { message: t('pages-user.pay-password.enter-6-digit-password') }),
confirmPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password-again') }),
})
.refine((data) => data.newPayPwd === data.confirmPwd, {
path: ['confirmPwd'],
message: t('pages-user.pay-password.two-passwords-inconsistent'),
})
)
async function submitPayPwdChange() {
if (!validateBySchema(payPwdChangeSchema.value, payPwdChangeForm.value)) return
await editPayPwd(payPwdChangeForm.value as any)
uni.showToast({ title: t('pages-user.pay-password.change-payment-password-successfully'), icon: 'none' })
await userStore.getUserInfo()
showPayPwdPopup.value = false
payPwdChangeForm.value = { oldPayPwd: '', newPayPwd: '', confirmPwd: '' }
}
const payPwdForgetForm = ref({
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
payPwd: '',
confirmPwd: '',
})
const payPwdForgetSchema = computed(() =>
z
.object({
captcha: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-verification-code') }),
payPwd: z
.string()
.min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password') })
.regex(/^\d{6}$/, { message: t('pages-user.pay-password.enter-6-digit-password') }),
confirmPwd: z.string().min(1, { message: t('pages-user.pay-password.input-placeholder.enter-new-password-again') }),
})
.refine((data) => data.payPwd === data.confirmPwd, {
path: ['confirmPwd'],
message: t('pages-user.pay-password.two-passwords-inconsistent'),
})
)
function requestPayForgetCode() {
if (isSend.value > 0) return
getMsgCode({
type: SmsType.USER_FORGET_PAYMENT_PASSWORD,
phone: payPwdForgetForm.value.phone,
areaCode: payPwdForgetForm.value.areaCode,
})
}
async function submitPayPwdForget() {
if (!validateBySchema(payPwdForgetSchema.value, payPwdForgetForm.value)) return
await forgetPayPwd(payPwdForgetForm.value as any)
uni.showToast({ title: t('pages-user.pay-password.forget-payment-password-successfully'), icon: 'none' })
await userStore.getUserInfo()
showPayPwdForgetPopup.value = false
payPwdForgetForm.value = {
areaCode: userStore.userInfo?.areaCode || '',
phone: userStore.userInfo?.phone || '',
captcha: '',
payPwd: '',
confirmPwd: '',
}
}
function handleChooseLanguage() {
if (chooseLanguageRef.value) {
@@ -30,10 +269,7 @@ function navigateTo(url: string) {
function handleSetOrUpdatePassword() {
if (userStore.userInfo?.payPwd) {
return navigateTo(`/pages-user/pages/pay-password/change/index`)
}
navigateTo(`/pages-user/pages/pay-password/set/index`)
openPayPwdPopup()
}
function handleLogout() {
@@ -74,76 +310,160 @@ function handleLogoutAccount() {
</script>
<template>
<navbar :title="t('navbar-settings')"/>
<view class="pt-20rpx">
<view class="text-30-bold bg-#fff">
<view
class="flex justify-between items-center border-bottom font-bold p-[40rpx+20rpx]"
@click="navigateTo('/pages-user/pages/password/change/index')"
>
<view>{{ t("pages-user.setting.modification") }}</view>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
</view>
<view class="setting-root">
<navbar :title="t('navbar-settings')"/>
<view class="setting-page">
<view class="setting-card">
<view
class="setting-row setting-row--border"
@click="openLoginPwdPopup"
>
<text class="setting-row__label">{{ t("pages-user.setting.modification") }}</text>
<image src="@img/chef/100202.png" class="setting-row__arrow"></image>
</view>
<view
@click="
handleSetOrUpdatePassword
"
class="flex justify-between items-center p-[40rpx+20rpx] border-bottom"
>
<view>{{ t("pages-user.setting.payPwd") }}</view>
<view class="flex items-center">
<text>{{ t('navbar-settings') }}</text>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
<view class="setting-row" @click="handleSetOrUpdatePassword">
<text class="setting-row__label">{{ t("pages-user.setting.payPwd") }}</text>
<view class="setting-row__right">
<text class="setting-row__value">{{ t('pages.mine.set') }}</text>
<image src="@img/chef/100202.png" class="setting-row__arrow"></image>
</view>
</view>
</view>
<view
class="flex justify-between items-center p-[40rpx+20rpx] border-bottom before:!bg-common"
@click="handleChooseLanguage"
>
<view>{{ t("pages-user.setting.language") }}</view>
<view class="flex items-center">
<text>{{ locale === 'en' ? 'English' : '中文'}}</text>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
<view class="setting-card setting-card--gap">
<view class="setting-row" @click="handleChooseLanguage">
<text class="setting-row__label">{{ t("pages-user.setting.language") }}</text>
<view class="setting-row__right">
<text class="setting-row__value">{{ currentLanguageLabel }}</text>
<image src="@img/chef/100202.png" class="setting-row__arrow"></image>
</view>
</view>
</view>
<view
class="flex justify-between items-center font-bold p-[40rpx+20rpx]"
@click="handleLogoutAccount"
>
<view>{{ t('pages-user.setting.cancelAccount') }}</view>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
<view class="setting-card setting-card--gap">
<view class="setting-row" @click="handleLogoutAccount">
<text class="setting-row__label setting-row__label--strong">{{ t('pages-user.setting.cancelAccount') }}</text>
<!-- <image src="@img/chef/100202.png" class="setting-row__arrow"></image> -->
</view>
</view>
<view class="logout-actions">
<wd-button custom-class="logout-btn" block @click="handleLogout">
{{ t('pages-user.setting.logout') }}
</wd-button>
</view>
</view>
<view class="px-30rpx mt-180rpx">
<wd-button
custom-class="!h-108rpx !text-36rpx !text-white !bg-#14181B !rounded-16rpx"
block
@click="handleLogout"
>
{{ t('pages-user.setting.logout') }}
</wd-button>
</view>
<choose-language ref="chooseLanguageRef"/>
<logout ref="logoutRef"/>
<password-dialogs
:t="t"
:is-send="isSend"
:pay-pwd-mode="payPwdMode"
:show-login-pwd-popup="showLoginPwdPopup"
:show-login-pwd-forget-popup="showLoginPwdForgetPopup"
:show-pay-pwd-popup="showPayPwdPopup"
:show-pay-pwd-forget-popup="showPayPwdForgetPopup"
:login-pwd-form="loginPwdForm"
:login-pwd-forget-form="loginPwdForgetForm"
:pay-pwd-set-form="payPwdSetForm"
:pay-pwd-change-form="payPwdChangeForm"
:pay-pwd-forget-form="payPwdForgetForm"
@update:show-login-pwd-popup="showLoginPwdPopup = $event"
@update:show-login-pwd-forget-popup="showLoginPwdForgetPopup = $event"
@update:show-pay-pwd-popup="showPayPwdPopup = $event"
@update:show-pay-pwd-forget-popup="showPayPwdForgetPopup = $event"
@submit-login-pwd="submitLoginPwd"
@submit-login-pwd-forget="submitLoginPwdForget"
@submit-pay-pwd-set="submitPayPwdSet"
@submit-pay-pwd-change="submitPayPwdChange"
@submit-pay-pwd-forget="submitPayPwdForget"
@request-login-forget-code="requestLoginForgetCode"
@request-pay-set-code="requestPaySetCode"
@request-pay-forget-code="requestPayForgetCode"
/>
</view>
</template>
<style scoped></style>
<style scoped lang="scss">
.setting-root {
min-height: 100vh;
background: #f5f5f5;
}
.setting-page {
padding: 20rpx 30rpx calc(160rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.setting-card {
background: #fff;
border-radius: 24rpx;
overflow: hidden;
}
.setting-card--gap {
margin-top: 24rpx;
}
.setting-row {
height: 102rpx;
padding: 0 28rpx;
display: flex;
align-items: center;
justify-content: space-between;
&--border {
border-bottom: 1rpx solid #ededed;
}
&__label {
font-size: 34rpx;
line-height: 34rpx;
color: #2c2c2c;
// font-weight: 700;
}
&__label--strong {
width: 100%;
text-align: center;
}
&__right {
display: flex;
align-items: center;
gap: 12rpx;
}
&__value {
font-size: 30rpx;
line-height: 30rpx;
color: #2c2c2c;
// font-weight: 600;
}
&__arrow {
width: 22rpx;
height: 30rpx;
flex-shrink: 0;
}
}
.logout-actions {
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: calc(36rpx + env(safe-area-inset-bottom));
z-index: 20;
}
:deep(.logout-btn) {
height: 108rpx !important;
border-radius: 22rpx !important;
background: #111 !important;
color: #fff !important;
font-size: 36rpx !important;
font-weight: 700 !important;
}
</style>
+27 -33
View File
@@ -1,14 +1,14 @@
<script setup lang="ts">
import Config from '@/config'
import {useUserStore} from '@/store'
import { useUserStore } from '@/store'
import type chooseImageVue from '@/components/choose-image/choose-image.vue'
import { appUserEditUserInfoPost } from '@/service'
import {debounce} from 'throttle-debounce';
import {upload} from '@/utils/upload/alioss'
import {EventEnum} from "@/constant/enums";
import { debounce } from 'throttle-debounce';
import { upload } from '@/utils/upload/alioss'
import { EventEnum } from "@/constant/enums";
const userStore = useUserStore()
const {t} = useI18n()
const { t } = useI18n()
const chooseImageRef = ref<InstanceType<typeof chooseImageVue> | null>(null)
@@ -18,16 +18,16 @@ const form = ref({
watch(
() => userStore.userInfo,
(newValue) => {
console.log('userInfo', newValue)
form.value = {
avatar: newValue?.avatar || '',
}
},
{
deep: true,
},
() => userStore.userInfo,
(newValue) => {
console.log('userInfo', newValue)
form.value = {
avatar: newValue?.avatar || '',
}
},
{
deep: true,
},
)
async function handleCropperAvatarSuccess(avatarUrl: string) {
@@ -61,13 +61,13 @@ async function submit() {
avatar: form.value.avatar,
}
})
await uni.showToast({title: t('common.prompt.save-successfully'), icon: 'none'})
await uni.showToast({ title: t('common.prompt.save-successfully'), icon: 'none' })
await userStore.getUserInfo()
} catch (e) {
}
}
const handleSubmit = debounce(Config.debounceLongTime, submit, {atBegin: true})
const handleSubmit = debounce(Config.debounceLongTime, submit, { atBegin: true })
function handleAddImg() {
chooseImageRef.value?.init()
@@ -91,7 +91,7 @@ function handlePreviewImage() {
function navigateTo(url: string) {
uni.navigateTo({url})
uni.navigateTo({ url })
}
onLoad(async () => {
@@ -119,37 +119,31 @@ onBackPress(() => {
</template>
</choose-image>
<view class="h-full flex flex-col">
<navbar :title="t('navbar-personal-information')"/>
<navbar :title="t('navbar-personal-information')" />
<view class="bg-#fff">
<view class="p-18rpx flex items-center justify-between border-bottom">
<view class="shrink-0 mr-20rpx text-30rpx text-primary">{{ t('pages-user.user-info.head-portrait') }}</view>
<view class="flex items-center" @click="handleAddImg">
<image :src="form.avatar" class="shrink-0 w-80rpx h-80rpx rounded-full"></image>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
<image src="@img/chef/100202.png" class="shrink-0 ml-16rpx w-22rpx h-30rpx"></image>
</view>
</view>
<view class="px-18rpx py-37rpx flex items-center justify-between"
@click="navigateTo('/pages-user/pages/edit-nickname/index')">
@click="navigateTo('/pages-user/pages/edit-nickname/index')">
<view class="shrink-0 mr-20rpx text-30rpx text-primary">{{ t('pages-user.user-info.nickname') }}</view>
<view class="flex-1 flex items-center justify-end text-30rpx text-primary">
<text>{{ `${userStore.userInfo.firstName} ${userStore.userInfo.surname}` }}</text>
<image
src="@img/chef/100202.png"
class="shrink-0 ml-16rpx w-22rpx h-30rpx"
></image>
<image src="@img/chef/100202.png" class="shrink-0 ml-16rpx w-22rpx h-30rpx"></image>
</view>
</view>
</view>
<view class="mt-318rpx px-30rpx">
<wd-button custom-class="!h-108rpx !text-36rpx font-bold !rounded-16rpx" block @click="handleSubmit">
{{ t('common.save') }}
</wd-button>
</view>
<view class="mt-318rpx px-30rpx" style="position: fixed; bottom: 30rpx; left: 0; right: 0; z-index: 100;">
<wd-button custom-class="!h-108rpx !text-36rpx font-bold !rounded-46rpx" block @click="handleSubmit">
{{ t('common.save') }}
</wd-button>
</view>
</view>
</template>