hookehuyr

feat(认证): 添加车辆认证页面及功能

实现车辆认证页面,包括图片上传、表单填写和提交功能
将样式从vue文件拆分到单独的less文件
更新路由配置添加认证页面
修复认证跳转链接错误
/*
* @Date: 2025-06-28 10:33:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-02 17:15:42
* @LastEditTime: 2025-07-02 18:16:09
* @FilePath: /jgdl/src/app.config.js
* @Description: 文件描述
*/
......@@ -15,6 +15,7 @@ export default {
'pages/editProfile/index',
'pages/register/index',
'pages/authCar/index',
'pages/setAuthCar/index',
'pages/auth/index',
],
subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-02 17:36:43
* @LastEditTime: 2025-07-02 18:15:19
* @FilePath: /jgdl/src/pages/authCar/index.vue
* @Description: 认证车源
-->
......@@ -107,6 +107,7 @@
</template>
<script setup>
import Taro from '@tarojs/taro'
import { ref, computed, onMounted } from 'vue'
import { Check, RectRight, Addfollow, HeartFill } from '@nutui/icons-vue-taro'
import './index.less'
......@@ -189,9 +190,8 @@ const scrollStyle = computed(() => {
* 处理认证按钮点击
*/
const handleAuth = () => {
showToast('跳转到认证页面', 'success')
// TODO: 跳转到认证页面
// Taro.navigateTo({ url: '/pages/auth/index' })
Taro.navigateTo({ url: '/pages/setAuthCar/index' })
}
/**
......
......@@ -141,7 +141,7 @@
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import { RectLeft, Camera, Right } from '@nutui/icons-vue-taro'
import { RectLeft, Right } from '@nutui/icons-vue-taro'
import './index.less'
// 主题配置
......
.sell-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.form-container {
padding: 0 32rpx;
margin-bottom: 2rem;
}
.form-section {
background-color: #ffffff;
border-radius: 24rpx;
padding: 32rpx;
margin: 24rpx 0;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #111827;
margin-bottom: 32rpx;
display: block;
}
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
margin-top: 24rpx;
}
.upload-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
}
.upload-button {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed #d1d5db;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9fafb;
transition: all 0.3s ease;
}
.upload-button:active {
background-color: #f3f4f6;
border-color: #f97316;
}
.upload-icon {
font-size: 48rpx;
color: #9ca3af;
}
.upload-label {
font-size: 24rpx;
color: #6b7280;
text-align: center;
}
.image-preview {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: #ef4444;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16rpx;
z-index: 10;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
}
.delete-icon {
font-size: 16rpx;
color: white;
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0;
border-bottom: 1rpx solid #f3f4f6;
}
.form-item:last-child {
border-bottom: none;
}
.form-item-left {
display: flex;
align-items: center;
}
.form-icon {
margin-right: 16rpx;
color: #9ca3af;
}
.form-label {
font-size: 28rpx;
color: #374151;
}
.form-item-right {
display: flex;
align-items: center;
}
.form-value {
font-size: 28rpx;
color: #9ca3af;
margin-right: 16rpx;
}
.arrow-icon {
color: #9ca3af;
}
.form-item-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 24rpx 0;
}
.unit {
font-size: 28rpx;
color: #9ca3af;
margin-left: 16rpx;
}
.price-symbol {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
margin-right: 8rpx;
}
.price-input {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.market-price-symbol {
font-size: 28rpx;
color: #9ca3af;
margin-right: 8rpx;
}
.market-price-input {
font-size: 28rpx;
color: #9ca3af;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
padding: 24rpx 32rpx;
border-top: 1rpx solid #f3f4f6;
z-index: 100;
}
// NutUI组件样式覆盖
:deep(.nut-form-item) {
padding: 0;
margin-bottom: 0;
border-bottom: 1rpx solid #f3f4f6;
}
// :deep(.nut-form-item:last-child) {
// border-bottom: none;
// }
:deep(.nut-form-item__body) {
padding: 32rpx 0;
}
:deep(.nut-form-item__label) {
font-size: 28rpx;
color: #374151;
margin-right: 32rpx;
}
:deep(.nut-input) {
text-align: right;
}
:deep(.nut-input__input) {
font-size: 28rpx;
color: #374151;
text-align: right;
}
:deep(.nut-textarea) {
border: 1rpx solid #e5e7eb;
border-radius: 16rpx;
padding: 24rpx;
}
:deep(.nut-textarea__textarea) {
font-size: 28rpx;
color: #374151;
line-height: 1.5;
}
:deep(.nut-uploader) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview-img) {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
object-fit: cover;
}
:deep(.nut-uploader__upload) {
width: 160rpx;
height: 160rpx;
}
:deep(.nut-uploader__input) {
width: 100%;
height: 100%;
}
:deep(.nut-uploader__preview-delete) {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
}
:deep(.nut-picker__toolbar) {
padding: 24rpx 32rpx;
}
:deep(.nut-picker__cancel) {
color: #9ca3af;
}
:deep(.nut-picker__confirm) {
color: #f97316;
}
:deep(.nut-picker__title) {
font-size: 32rpx;
font-weight: 600;
}
......@@ -296,6 +296,7 @@ import { ref, reactive } from 'vue'
import { Plus, Right, Location, RectLeft, Close } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
// import BASE_URL from '@/utils/config';
import './index.less'
const themeVars = ref({
navbarBackground: '#fb923c',
......@@ -761,310 +762,3 @@ const validateForm = () => {
return true
}
</script>
<style lang="less">
.sell-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.form-container {
padding: 0 32rpx;
margin-bottom: 2rem;
}
.form-section {
background-color: #ffffff;
border-radius: 24rpx;
padding: 32rpx;
margin: 24rpx 0;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #111827;
margin-bottom: 32rpx;
display: block;
}
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
margin-top: 24rpx;
}
.upload-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
}
.upload-button {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed #d1d5db;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9fafb;
transition: all 0.3s ease;
}
.upload-button:active {
background-color: #f3f4f6;
border-color: #f97316;
}
.upload-icon {
font-size: 48rpx;
color: #9ca3af;
}
.upload-label {
font-size: 24rpx;
color: #6b7280;
text-align: center;
}
.image-preview {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: #ef4444;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16rpx;
z-index: 10;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
}
.delete-icon {
font-size: 16rpx;
color: white;
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0;
border-bottom: 1rpx solid #f3f4f6;
}
.form-item:last-child {
border-bottom: none;
}
.form-item-left {
display: flex;
align-items: center;
}
.form-icon {
margin-right: 16rpx;
color: #9ca3af;
}
.form-label {
font-size: 28rpx;
color: #374151;
}
.form-item-right {
display: flex;
align-items: center;
}
.form-value {
font-size: 28rpx;
color: #9ca3af;
margin-right: 16rpx;
}
.arrow-icon {
color: #9ca3af;
}
.form-item-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 24rpx 0;
}
.unit {
font-size: 28rpx;
color: #9ca3af;
margin-left: 16rpx;
}
.price-symbol {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
margin-right: 8rpx;
}
.price-input {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.market-price-symbol {
font-size: 28rpx;
color: #9ca3af;
margin-right: 8rpx;
}
.market-price-input {
font-size: 28rpx;
color: #9ca3af;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
padding: 24rpx 32rpx;
border-top: 1rpx solid #f3f4f6;
z-index: 100;
}
// NutUI组件样式覆盖
:deep(.nut-form-item) {
padding: 0;
margin-bottom: 0;
border-bottom: 1rpx solid #f3f4f6;
}
// :deep(.nut-form-item:last-child) {
// border-bottom: none;
// }
:deep(.nut-form-item__body) {
padding: 32rpx 0;
}
:deep(.nut-form-item__label) {
font-size: 28rpx;
color: #374151;
margin-right: 32rpx;
}
:deep(.nut-input) {
text-align: right;
}
:deep(.nut-input__input) {
font-size: 28rpx;
color: #374151;
text-align: right;
}
:deep(.nut-textarea) {
border: 1rpx solid #e5e7eb;
border-radius: 16rpx;
padding: 24rpx;
}
:deep(.nut-textarea__textarea) {
font-size: 28rpx;
color: #374151;
line-height: 1.5;
}
:deep(.nut-uploader) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview-img) {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
object-fit: cover;
}
:deep(.nut-uploader__upload) {
width: 160rpx;
height: 160rpx;
}
:deep(.nut-uploader__input) {
width: 100%;
height: 100%;
}
:deep(.nut-uploader__preview-delete) {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
}
:deep(.nut-picker__toolbar) {
padding: 24rpx 32rpx;
}
:deep(.nut-picker__cancel) {
color: #9ca3af;
}
:deep(.nut-picker__confirm) {
color: #f97316;
}
:deep(.nut-picker__title) {
font-size: 32rpx;
font-weight: 600;
}
</style>
......
/*
* @Date: 2025-07-02 17:52:43
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-02 17:53:28
* @FilePath: /jgdl/src/pages/setAuthCar/index.config.js
* @Description: 文件描述
*/
export default {
navigationBarTitleText: '申请认证',
usingComponents: {
},
}
/* 申请认证页面样式 */
.auth-car-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 200rpx;
}
.form-container {
padding: 20rpx 32rpx;
margin-bottom: 2rem;
}
.form-section {
background-color: #ffffff;
border-radius: 24rpx;
padding: 32rpx;
margin: 24rpx 0;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #111827;
margin-bottom: 32rpx;
display: block;
}
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
margin-top: 24rpx;
}
.upload-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
}
.upload-button {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed #d1d5db;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9fafb;
transition: all 0.3s ease;
}
.upload-button:active {
background-color: #f3f4f6;
border-color: #f97316;
}
.upload-icon {
font-size: 48rpx;
color: #9ca3af;
}
.upload-label {
font-size: 24rpx;
color: #6b7280;
text-align: center;
}
.image-preview {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: #ef4444;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16rpx;
z-index: 10;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
}
.delete-icon {
font-size: 16rpx;
color: white;
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0;
border-bottom: 1rpx solid #f3f4f6;
}
.form-item:last-child {
border-bottom: none;
}
.form-item-left {
display: flex;
align-items: center;
}
.form-icon {
margin-right: 16rpx;
color: #9ca3af;
}
.form-label {
font-size: 28rpx;
color: #374151;
}
.form-item-right {
display: flex;
align-items: center;
}
.form-value {
font-size: 28rpx;
color: #9ca3af;
margin-right: 16rpx;
}
.arrow-icon {
color: #9ca3af;
}
.form-item-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 24rpx 0;
}
.unit {
font-size: 28rpx;
color: #9ca3af;
margin-left: 16rpx;
}
.price-symbol {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
margin-right: 8rpx;
}
.price-input {
font-size: 32rpx;
color: #f97316;
font-weight: 600;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.market-price-symbol {
font-size: 28rpx;
color: #9ca3af;
margin-right: 8rpx;
}
.market-price-input {
font-size: 28rpx;
color: #9ca3af;
text-align: right;
border: none;
outline: none;
background: transparent;
width: 160rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
padding: 24rpx 32rpx;
border-top: 1rpx solid #f3f4f6;
z-index: 100;
}
// NutUI组件样式覆盖
:deep(.nut-form-item) {
padding: 0;
margin-bottom: 0;
border-bottom: 1rpx solid #f3f4f6;
}
// :deep(.nut-form-item:last-child) {
// border-bottom: none;
// }
:deep(.nut-form-item__body) {
padding: 32rpx 0;
}
:deep(.nut-form-item__label) {
font-size: 28rpx;
color: #374151;
margin-right: 32rpx;
}
:deep(.nut-input) {
text-align: right;
}
:deep(.nut-input__input) {
font-size: 28rpx;
color: #374151;
text-align: right;
}
:deep(.nut-textarea) {
border: 1rpx solid #e5e7eb;
border-radius: 16rpx;
padding: 24rpx;
}
:deep(.nut-textarea__textarea) {
font-size: 28rpx;
color: #374151;
line-height: 1.5;
}
:deep(.nut-uploader) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview) {
margin-bottom: 0;
}
:deep(.nut-uploader__preview-img) {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
object-fit: cover;
}
:deep(.nut-uploader__upload) {
width: 160rpx;
height: 160rpx;
}
:deep(.nut-uploader__input) {
width: 100%;
height: 100%;
}
:deep(.nut-uploader__preview-delete) {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
}
:deep(.nut-picker__toolbar) {
padding: 24rpx 32rpx;
}
:deep(.nut-picker__cancel) {
color: #9ca3af;
}
:deep(.nut-picker__confirm) {
color: #f97316;
}
:deep(.nut-picker__title) {
font-size: 32rpx;
font-weight: 600;
}
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-02 18:23:27
* @FilePath: /jgdl/src/pages/setAuthCar/index.vue
* @Description: 申请认证
-->
<template>
<view class="auth-car-page">
<!-- 表单内容 -->
<view class="form-container">
<!-- 车辆照片上传 -->
<view class="form-section">
<text class="section-title">添加车辆照片</text>
<view class="upload-grid">
<!-- 正面照 -->
<view class="upload-item">
<!-- 上传按钮 -->
<view v-if="!uploadedImages.front" class="upload-button" @click="triggerUpload('front')">
<Plus class="upload-icon" />
</view>
<!-- 图片预览 -->
<view v-else class="image-preview" @click="previewImage(uploadedImages.front)">
<image :src="uploadedImages.front" class="preview-image" mode="aspectFill" />
<view class="delete-btn" @click.stop="deleteImage('front')">
<Close class="delete-icon" />
</view>
</view>
<text class="upload-label">正面照</text>
</view>
<!-- 左侧照 -->
<view class="upload-item">
<!-- 上传按钮 -->
<view v-if="!uploadedImages.left" class="upload-button" @click="triggerUpload('left')">
<Plus class="upload-icon" />
</view>
<!-- 图片预览 -->
<view v-else class="image-preview" @click="previewImage(uploadedImages.left)">
<image :src="uploadedImages.left" class="preview-image" mode="aspectFill" />
<view class="delete-btn" @click.stop="deleteImage('left')">
<Close class="delete-icon" />
</view>
</view>
<text class="upload-label">左侧照</text>
</view>
<!-- 右侧照 -->
<view class="upload-item">
<!-- 上传按钮 -->
<view v-if="!uploadedImages.right" class="upload-button" @click="triggerUpload('right')">
<Plus class="upload-icon" />
</view>
<!-- 图片预览 -->
<view v-else class="image-preview" @click="previewImage(uploadedImages.right)">
<image :src="uploadedImages.right" class="preview-image" mode="aspectFill" />
<view class="delete-btn" @click.stop="deleteImage('right')">
<Close class="delete-icon" />
</view>
</view>
<text class="upload-label">右侧照</text>
</view>
<!-- 其他 -->
<view class="upload-item">
<!-- 上传按钮 -->
<view v-if="!uploadedImages.other" class="upload-button" @click="triggerUpload('other')">
<Plus class="upload-icon" />
</view>
<!-- 图片预览 -->
<view v-else class="image-preview" @click="previewImage(uploadedImages.other)">
<image :src="uploadedImages.other" class="preview-image" mode="aspectFill" />
<view class="delete-btn" @click.stop="deleteImage('other')">
<Close class="delete-icon" />
</view>
</view>
<text class="upload-label">其他</text>
</view>
</view>
<!-- 图片预览组件 -->
<nut-image-preview v-model:show="previewVisible" :images="previewImages" :init-no="previewIndex" />
</view>
<!-- 车辆详情表单 -->
<nut-form ref="formRef" :model-value="formData">
<view class="form-section">
<!-- 车型品牌 -->
<nut-form-item label-position="top" label="车型品牌" prop="brand" required :rules="[{ required: true, message: '请选择车型品牌' }]">
<view class="form-item-content" @click="showBrandPicker">
<text class="form-value">{{ formData.brand || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 车辆型号 -->
<nut-form-item label-position="top" label="车辆型号" prop="model" required :rules="[{ required: true, message: '请选择车辆型号' }]">
<view class="form-item-content" @click="showModelPicker">
<text class="form-value">{{ formData.model || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 续航里程 -->
<nut-form-item label="续航里程" prop="range" required :rules="[{ required: true, message: '请输入续航里程' }]">
<nut-input v-model="formData.range" placeholder="60" type="number" input-align="right">
<template #right>
<text class="unit">公里</text>
</template>
</nut-input>
</nut-form-item>
<!-- 最高时速 -->
<nut-form-item label="最高时速" prop="maxSpeed" required :rules="[{ required: true, message: '请输入最高时速' }]">
<nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right">
<template #right>
<text class="unit">km/h</text>
</template>
</nut-input>
</nut-form-item>
</view>
</nut-form>
<!-- 车辆描述 -->
<view class="form-section">
<text class="section-title">车辆描述</text>
<nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" :rows="4" show-word-limit />
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-actions">
<nut-button color="#f97316" size="large" block @click="onSubmit">
提交申请
</nut-button>
</view>
<!-- 选择器弹窗 -->
<!-- 品牌选择 -->
<nut-popup v-model:visible="brandPickerVisible" position="bottom">
<nut-picker v-model="brandValue" :columns="brandOptions" title="选择车型品牌" @confirm="onBrandConfirm" @cancel="brandPickerVisible = false" />
</nut-popup>
<!-- 型号选择 -->
<nut-popup v-model:visible="modelPickerVisible" position="bottom">
<nut-picker v-model="modelValue" :columns="modelOptions" title="选择车辆型号" @confirm="onModelConfirm" @cancel="modelPickerVisible = false" />
</nut-popup>
</view>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { Plus, Right, RectLeft, Close } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
import './index.less'
const themeVars = ref({
navbarBackground: '#fb923c',
navbarColor: '#ffffff',
})
// 已上传图片的URL
const uploadedImages = reactive({
front: '',
left: '',
right: '',
other: ''
})
// 图片预览相关
const previewVisible = ref(false)
const previewImages = ref([])
const previewIndex = ref(0)
// 表单数据
const formData = reactive({
brand: '',
model: '',
range: '',
maxSpeed: '',
description: ''
})
// 选择器显示状态
const brandPickerVisible = ref(false)
const modelPickerVisible = ref(false)
// 选择器值
const brandValue = ref([])
const modelValue = ref([])
// 选择器选项数据
const brandOptions = ref([
{ text: '小牛电动', value: '小牛电动' },
{ text: '雅迪', value: '雅迪' },
{ text: '爱玛', value: '爱玛' },
{ text: '台铃', value: '台铃' },
{ text: '绿源', value: '绿源' },
{ text: '新日', value: '新日' },
{ text: '立马', value: '立马' },
{ text: '其他', value: '其他' }
])
const modelOptions = ref([
{ text: 'NGT', value: 'NGT' },
{ text: 'UQi+', value: 'UQi+' },
{ text: 'Gova G2', value: 'Gova G2' },
{ text: 'MQi+', value: 'MQi+' },
{ text: 'NQi GTS', value: 'NQi GTS' },
{ text: 'RQi', value: 'RQi' },
{ text: '其他型号', value: '其他型号' }
])
/**
* 返回上一页
*/
const goBack = () => {
Taro.navigateBack()
}
/**
* 触发图片上传
* @param {String} type - 图片类型 (front/left/right/other)
*/
const triggerUpload = (type) => {
Taro.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
const tempFilePath = res.tempFilePaths[0]
uploadImage(tempFilePath, type)
},
fail: function () {
Taro.showToast({
title: '选择图片失败',
icon: 'none'
})
}
})
}
/**
* 上传图片到服务器
* @param {String} filePath - 图片文件路径
* @param {String} type - 图片类型 (front/left/right/other)
*/
const uploadImage = (filePath, type) => {
// 显示上传中提示
Taro.showLoading({
title: '上传中',
mask: true
})
// 模拟上传成功(实际项目中替换为真实的上传逻辑)
setTimeout(() => {
Taro.hideLoading()
// 模拟服务器返回的图片URL
const mockImageUrl = filePath // 在实际项目中,这里应该是服务器返回的URL
// 更新对应类型的图片
uploadedImages[type] = mockImageUrl
Taro.showToast({
title: '上传成功',
icon: 'success'
})
}, 1500)
}
/**
* 删除图片
* @param {String} type - 图片类型
*/
const deleteImage = (type) => {
uploadedImages[type] = ''
Taro.showToast({
title: '删除成功',
icon: 'success'
})
}
/**
* 预览图片
* @param {String} imageUrl - 图片URL
*/
const previewImage = (imageUrl) => {
previewImages.value = [imageUrl]
previewIndex.value = 0
previewVisible.value = true
}
/**
* 显示品牌选择器
*/
const showBrandPicker = () => {
brandPickerVisible.value = true
}
/**
* 显示型号选择器
*/
const showModelPicker = () => {
modelPickerVisible.value = true
}
/**
* 品牌选择确认
* @param {Object} options - 选择的选项
*/
const onBrandConfirm = (options) => {
formData.brand = options.selectedOptions[0]?.text || ''
brandPickerVisible.value = false
}
/**
* 型号选择确认
* @param {Object} options - 选择的选项
*/
const onModelConfirm = (options) => {
formData.model = options.selectedOptions[0]?.text || ''
modelPickerVisible.value = false
}
/**
* 提交申请
*/
const onSubmit = () => {
// 表单验证
if (!formData.brand) {
Taro.showToast({
title: '请选择车型品牌',
icon: 'none'
})
return
}
if (!formData.model) {
Taro.showToast({
title: '请选择车辆型号',
icon: 'none'
})
return
}
if (!formData.range) {
Taro.showToast({
title: '请输入续航里程',
icon: 'none'
})
return
}
if (!formData.maxSpeed) {
Taro.showToast({
title: '请输入最高时速',
icon: 'none'
})
return
}
// 检查是否至少上传了一张照片
const hasImage = Object.values(uploadedImages).some(img => img)
if (!hasImage) {
Taro.showToast({
title: '请至少上传一张车辆照片',
icon: 'none'
})
return
}
// 提交数据
const submitData = {
...formData,
images: uploadedImages
}
Taro.showLoading({
title: '提交中',
mask: true
})
// 模拟提交成功
setTimeout(() => {
Taro.hideLoading()
Taro.showToast({
title: '申请提交成功',
icon: 'success'
})
// 返回上一页
setTimeout(() => {
Taro.navigateBack()
}, 1500)
}, 2000)
// 实际项目中的API调用
// submitAuthApplication(submitData)
}
</script>
<script>
export default {
name: "setAuthCarPage",
};
</script>