用户中心样式调整

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
+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>