BrandModelPicker.vue 10.1 KB
<template>
  <view>
    <!-- 品牌选择弹框 -->
    <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>
          <nut-button size="small" type="primary" @click="closePicker">关闭</nut-button>
        </view>
        <view class="brand-container">
          <scroll-view
            class="brand-content"
            :scroll-y="true"
            :style="{ height: contentHeight + 'rpx' }"
            ref="brandContentRef"
          >
            <view class="brand-list">
              <view
                v-for="brand in allBrands"
                :key="brand.value"
                class="brand-item"
                @click="selectBrand(brand)"
              >
                <view class="brand-info">
                  <view class="brand-icon">
                    <text class="brand-initial">{{ brand.text.charAt(0) }}</text>
                  </view>
                  <text class="brand-name">{{ brand.text }}</text>
                </view>
                <Right class="brand-arrow" />
              </view>
            </view>
          </scroll-view>
        </view>
      </view>
    </nut-popup>

    <!-- 型号选择弹框 -->
    <nut-popup v-model:visible="modelPickerVisible" position="bottom" :style="{ height: '50%' }">
      <view class="model-picker">
        <view class="picker-header">
          <nut-button size="small" @click="goBackToBrandPicker">返回</nut-button>
          <text class="picker-title">选择{{ selectedBrandName }}型号</text>
          <nut-button size="small" type="primary" @click="closePicker">关闭</nut-button>
        </view>
        <view class="model-list">
          <view
            v-for="model in currentBrandModels"
            :key="model.value"
            class="model-item"
            @click="selectModel(model)"
          >
            <text class="model-name">{{ model.text }}</text>
          </view>
        </view>
      </view>
    </nut-popup>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { Right } from '@nutui/icons-vue-taro'

// 定义事件
const emit = defineEmits(['confirm', 'cancel'])

// 响应式数据
const brandPickerVisible = ref(false)
const modelPickerVisible = ref(false)
const selectedBrandName = ref('')
const currentBrandModels = ref([])
const contentHeight = ref(800) // 默认高度 rpx

const allBrands = ref([
  { text: '爱玛', value: 'aima' },
  { text: '雅迪', value: 'yadi' },
  { text: '小牛', value: 'xiaoniu' },
  { text: '台铃', value: 'tailing' },
  { text: '绿源', value: 'lvyuan' },
  { text: '立马', value: 'lima' },
  { text: '新日', value: 'xinri' },
  { text: '宗申', value: 'zongshen' },
  { text: '奇瑞', value: 'qirui' },
  { text: '比德文', value: 'bidewen' },
  { text: '欧派', value: 'oupai' },
  { text: '捷安特', value: 'jiante' },
  { text: '美利达', value: 'meilida' },
  { text: '凤凰', value: 'fenghuang' },
  { text: '永久', value: 'yongjiu' },
  { text: '飞鸽', value: 'feige' },
  { text: '小刀', value: 'xiaodao' },
  { text: '速派奇', value: 'supaiqi' },
  { text: '金箭', value: 'jinjian' },
  { text: '森蓝', value: 'senlan' }
])

// 品牌型号映射
const brandModelMap = ref({
  'aima': [
    { text: 'A1 Pro', value: 'aima_a1_pro' },
    { text: 'A2 Max', value: 'aima_a2_max' },
    { text: 'A3 Plus', value: 'aima_a3_plus' },
    { text: 'A4 Elite', value: 'aima_a4_elite' }
  ],
  'yadi': [
    { text: 'Y1 智享版', value: 'yadi_y1_smart' },
    { text: 'Y2 豪华版', value: 'yadi_y2_luxury' },
    { text: 'Y3 运动版', value: 'yadi_y3_sport' },
    { text: 'Y4 旗舰版', value: 'yadi_y4_flagship' }
  ],
  'xiaoniu': [
    { text: 'N1S', value: 'xiaoniu_n1s' },
    { text: 'N-GT', value: 'xiaoniu_ngt' },
    { text: 'NGT Pro', value: 'xiaoniu_ngt_pro' },
    { text: 'U1 Pro', value: 'xiaoniu_u1_pro' }
  ],
  'tailing': [
    { text: 'T1 经典版', value: 'tailing_t1_classic' },
    { text: 'T2 时尚版', value: 'tailing_t2_fashion' },
    { text: 'T3 豪华版', value: 'tailing_t3_luxury' }
  ],
  'lvyuan': [
    { text: 'L1 标准版', value: 'lvyuan_l1_standard' },
    { text: 'L2 升级版', value: 'lvyuan_l2_upgrade' },
    { text: 'L3 旗舰版', value: 'lvyuan_l3_flagship' }
  ],
  'lima': [
    { text: 'LM-1', value: 'lima_lm1' },
    { text: 'LM-2', value: 'lima_lm2' },
    { text: 'LM-3 Pro', value: 'lima_lm3_pro' }
  ],
  'xinri': [
    { text: 'XR-1', value: 'xinri_xr1' },
    { text: 'XR-2 Plus', value: 'xinri_xr2_plus' },
    { text: 'XR-3 Max', value: 'xinri_xr3_max' }
  ],
  'zongshen': [
    { text: 'ZS-1', value: 'zongshen_zs1' },
    { text: 'ZS-2 Pro', value: 'zongshen_zs2_pro' }
  ],
  'qirui': [
    { text: 'QR-1', value: 'qirui_qr1' },
    { text: 'QR-2 智能版', value: 'qirui_qr2_smart' }
  ],
  'bidewen': [
    { text: 'BD-1', value: 'bidewen_bd1' },
    { text: 'BD-2 Plus', value: 'bidewen_bd2_plus' }
  ],
  'oupai': [
    { text: 'OP-1', value: 'oupai_op1' },
    { text: 'OP-2 Pro', value: 'oupai_op2_pro' }
  ],
  'jiante': [
    { text: 'JT-1', value: 'jiante_jt1' },
    { text: 'JT-2 运动版', value: 'jiante_jt2_sport' }
  ],
  'meilida': [
    { text: 'MD-1', value: 'meilida_md1' },
    { text: 'MD-2 Pro', value: 'meilida_md2_pro' }
  ],
  'fenghuang': [
    { text: 'FH-1 经典', value: 'fenghuang_fh1_classic' },
    { text: 'FH-2 现代', value: 'fenghuang_fh2_modern' }
  ],
  'yongjiu': [
    { text: 'YJ-1', value: 'yongjiu_yj1' },
    { text: 'YJ-2 Plus', value: 'yongjiu_yj2_plus' }
  ],
  'feige': [
    { text: 'FG-1', value: 'feige_fg1' },
    { text: 'FG-2 Pro', value: 'feige_fg2_pro' }
  ],
  'xiaodao': [
    { text: 'XD-1', value: 'xiaodao_xd1' },
    { text: 'XD-2 Max', value: 'xiaodao_xd2_max' }
  ],
  'supaiqi': [
    { text: 'SP-1', value: 'supaiqi_sp1' },
    { text: 'SP-2 Pro', value: 'supaiqi_sp2_pro' }
  ],
  'jinjian': [
    { text: 'JJ-1', value: 'jinjian_jj1' },
    { text: 'JJ-2 Plus', value: 'jinjian_jj2_plus' }
  ],
  'senlan': [
    { text: 'SL-1', value: 'senlan_sl1' },
    { text: 'SL-2 Pro', value: 'senlan_sl2_pro' }
  ]
})

// 计算内容高度
const calculateContentHeight = () => {
  // 弹框总高度 - 头部高度
  const popupHeight = 1200 // rpx
  const headerHeight = 120 // rpx (头部标题和按钮的高度)
  contentHeight.value = popupHeight - headerHeight
}

// 显示品牌选择器
const show = () => {
  calculateContentHeight() // 计算内容高度
  brandPickerVisible.value = true
}

// 选择品牌
const selectBrand = (brand) => {
  selectedBrandName.value = brand.text
  currentBrandModels.value = brandModelMap.value[brand.value] || []
  brandPickerVisible.value = false
  modelPickerVisible.value = true
}

// 选择型号
const selectModel = (model) => {
  const result = {
    brand: selectedBrandName.value,
    model: model.text,
    brandValue: selectedBrandName.value,
    modelValue: model.value
  }

  // 发送确认事件
  emit('confirm', result)

  // 关闭所有弹框
  closeAllPickers()
}

// 返回品牌选择
const goBackToBrandPicker = () => {
  modelPickerVisible.value = false
  brandPickerVisible.value = true
}

// 关闭选择器
const closePicker = () => {
  closeAllPickers()
  emit('cancel')
}

// 关闭所有弹框
const closeAllPickers = () => {
  brandPickerVisible.value = false
  modelPickerVisible.value = false
  selectedBrandName.value = ''
  currentBrandModels.value = []
}

// 暴露方法给父组件
defineExpose({
  show,
  close: closeAllPickers
})
</script>

<style lang="less">
.brand-model-picker {
  padding: 20rpx;
  height: 100%;
  display: flex;
  flex-direction: column;

  .picker-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx 0;
    border-bottom: 1rpx solid #f0f0f0;
    margin-bottom: 20rpx;

    .picker-title {
      font-size: 32rpx;
      font-weight: 600;
      color: #333;
    }
  }

  .brand-container {
    flex: 1;
    height: 100%;

    .brand-content {
      background-color: #f8f9fa;

      .brand-list {
        padding: 20rpx;

        .brand-item {
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 24rpx 30rpx;
          margin-bottom: 16rpx;
          background-color: white;
          border-radius: 16rpx;
          box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
          transition: all 0.3s ease;

          &:active {
            transform: scale(0.98);
            box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
          }

          &:last-child {
            margin-bottom: 0;
          }

          .brand-info {
            display: flex;
            align-items: center;

            .brand-icon {
              width: 80rpx;
              height: 80rpx;
              border-radius: 40rpx;
              background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
              display: flex;
              align-items: center;
              justify-content: center;
              margin-right: 24rpx;

              .brand-initial {
                font-size: 32rpx;
                color: white;
                font-weight: bold;
              }
            }

            .brand-name {
              font-size: 32rpx;
              color: #333;
              font-weight: 500;
            }
          }

          .brand-arrow {
            width: 32rpx;
            height: 32rpx;
            color: #ccc;
          }
        }
      }
    }
  }
}

.model-picker {
  padding: 20rpx;
  height: 100%;
  display: flex;
  flex-direction: column;

  .picker-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx 0;
    border-bottom: 1rpx solid #f0f0f0;
    margin-bottom: 20rpx;

    .picker-title {
      font-size: 32rpx;
      font-weight: 600;
      color: #333;
    }
  }

  .model-list {
    flex: 1;
    overflow-y: auto;

    .model-item {
      display: flex;
      align-items: center;
      padding: 30rpx 20rpx;
      border-bottom: 1rpx solid #f8f8f8;
      transition: background-color 0.2s;

      &:active {
        background-color: #f5f5f5;
      }

      .model-name {
        font-size: 30rpx;
        color: #333;
      }
    }
  }
}
</style>