hookehuyr

feat(品牌型号选择器): 添加品牌型号搜索功能并优化选择流程

- 在BrandModelPicker组件中添加搜索功能,支持按品牌名称过滤
- 将品牌和型号选择合并为单一流程,简化用户操作
- 更新setAuthCar页面使用新的品牌型号选择器组件
- 移除旧的独立品牌和型号选择器
......@@ -4,9 +4,18 @@
<nut-popup v-model:visible="brandPickerVisible" position="bottom" :style="{ height: '80%' }">
<view class="brand-model-picker">
<view class="picker-header">
<text class="picker-title">选择品牌</text>
<text class="picker-title">选择品牌型号</text>
<nut-button size="small" type="primary" @click="closePicker">关闭</nut-button>
</view>
<view class="search-container">
<nut-searchbar
v-model="searchKeyword"
placeholder="搜索品牌名称"
shape="round"
background="transparent"
class="search-bar"
/>
</view>
<view class="brand-container">
<scroll-view
class="brand-content"
......@@ -16,7 +25,7 @@
>
<view class="brand-list">
<view
v-for="brand in allBrands"
v-for="brand in filteredBrands"
:key="brand.value"
class="brand-item"
@click="selectBrand(brand)"
......@@ -59,7 +68,7 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { Right } from '@nutui/icons-vue-taro'
// 定义事件
......@@ -71,6 +80,7 @@ const modelPickerVisible = ref(false)
const selectedBrandName = ref('')
const currentBrandModels = ref([])
const contentHeight = ref(800) // 默认高度 rpx
const searchKeyword = ref('') // 搜索关键词
const allBrands = ref([
{ text: '爱玛', value: 'aima' },
......@@ -189,6 +199,16 @@ const brandModelMap = ref({
]
})
// 过滤后的品牌列表
const filteredBrands = computed(() => {
if (!searchKeyword.value.trim()) {
return allBrands.value
}
return allBrands.value.filter(brand =>
brand.text.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
})
// 计算内容高度
const calculateContentHeight = () => {
// 弹框总高度 - 头部高度
......@@ -200,6 +220,7 @@ const calculateContentHeight = () => {
// 显示品牌选择器
const show = () => {
calculateContentHeight() // 计算内容高度
searchKeyword.value = '' // 清空搜索关键词
brandPickerVisible.value = true
}
......@@ -276,6 +297,58 @@ defineExpose({
}
}
.search-container {
padding: 0 0 24rpx 0;
margin-bottom: 20rpx;
.search-bar {
// background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 20rpx;
padding: 4rpx;
// box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.15);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 12rpx 32rpx rgba(102, 126, 234, 0.25);
transform: translateY(-2rpx);
}
:deep(.nut-searchbar__search-input) {
background-color: white;
border-radius: 16rpx;
border: none;
padding: 24rpx 20rpx;
font-size: 28rpx;
color: #333;
transition: all 0.3s ease;
&::placeholder {
color: #999;
font-weight: 400;
}
&:focus {
background-color: #fafbfc;
box-shadow: inset 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
}
:deep(.nut-searchbar__search-icon) {
color: #666;
width: 32rpx;
height: 32rpx;
}
:deep(.nut-searchbar__input-clear) {
color: #ccc;
&:hover {
color: #999;
}
}
}
}
.brand-container {
flex: 1;
height: 100%;
......
......@@ -722,11 +722,11 @@ const onBrandModelConfirm = (result) => {
// 设置组合字段用于表单验证
formData.brandModel = `${result.brand} ${result.model}`
Taro.showToast({
title: `已选择 ${result.brand} ${result.model}`,
icon: 'success',
duration: 2000
})
// Taro.showToast({
// title: `已选择 ${result.brand} ${result.model}`,
// icon: 'success',
// duration: 2000
// })
}
/**
......
......@@ -86,20 +86,13 @@
<!-- 车辆详情表单 -->
<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>
<!-- 品牌型号选择 -->
<nut-form-item label-position="top" label="品牌型号选择" prop="brandModel" required
:rules="[{ required: true, message: '请选择品牌型号' }]">
<view class="form-item-content" @click="showBrandModelPicker">
<text class="form-value">
{{ formData.brand && formData.model ? `${formData.brand} ${formData.model}` : '请选择品牌型号' }}
</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
......@@ -140,18 +133,12 @@
</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>
<!-- 品牌型号选择器 -->
<BrandModelPicker
ref="brandModelPickerRef"
@confirm="onBrandModelConfirm"
@cancel="onBrandModelCancel"
/>
</view>
</template>
......@@ -161,6 +148,7 @@ import { Plus, Right, Close } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
import './index.less'
import BASE_URL from '@/utils/config';
import BrandModelPicker from '@/components/BrandModelPicker.vue'
// 获取页面参数
const instance = Taro.getCurrentInstance()
......@@ -189,40 +177,14 @@ const closePreview = () => {
const formData = reactive({
brand: '',
model: '',
brandModel: '', // 品牌型号组合字段,用于表单验证
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 brandModelPickerRef = ref(null)
......@@ -325,35 +287,33 @@ const previewImage = (imageUrl) => {
}
/**
* 显示品牌选择器
*/
const showBrandPicker = () => {
brandPickerVisible.value = true
}
/**
* 显示型号选择器
* 显示品牌型号选择器
*/
const showModelPicker = () => {
modelPickerVisible.value = true
const showBrandModelPicker = () => {
brandModelPickerRef.value?.show()
}
/**
* 品牌选择确认
* @param {Object} options - 选择的选项
* 品牌型号选择确认
* @param {Object} data - 选择的品牌和型号数据
*/
const onBrandConfirm = (options) => {
formData.brand = options.selectedOptions[0]?.text || ''
brandPickerVisible.value = false
const onBrandModelConfirm = (data) => {
formData.brand = data.brand
formData.model = data.model
formData.brandModel = `${data.brand} ${data.model}` // 设置组合字段用于表单验证
// Taro.showToast({
// title: `已选择 ${data.brand} ${data.model}`,
// icon: 'success',
// duration: 2000
// })
}
/**
* 型号选择确认
* @param {Object} options - 选择的选项
* 品牌型号选择取消回调
*/
const onModelConfirm = (options) => {
formData.model = options.selectedOptions[0]?.text || ''
modelPickerVisible.value = false
const onBrandModelCancel = () => {
// 可以在这里处理取消逻辑
}
/**
......@@ -361,17 +321,9 @@ const onModelConfirm = (options) => {
*/
const onSubmit = () => {
// 表单验证
if (!formData.brand) {
Taro.showToast({
title: '请选择车型品牌',
icon: 'none'
})
return
}
if (!formData.model) {
if (!formData.brandModel) {
Taro.showToast({
title: '请选择车辆型号',
title: '请选择品牌型号',
icon: 'none'
})
return
......@@ -475,6 +427,7 @@ const loadAuthData = async () => {
Object.assign(formData, {
brand: mockAuthData.brand,
model: mockAuthData.model,
brandModel: `${mockAuthData.brand} ${mockAuthData.model}`, // 设置组合字段
range: mockAuthData.range,
maxSpeed: mockAuthData.maxSpeed,
description: mockAuthData.description
......