以下是使用 Vue 3 和 Element Plus 重新编写的 Select 组件:

完整重写代码

vue

<template>
  <el-select
    :style="{ width: width }"
    v-model="currentValue"
    :placeholder="placeholder"
    :loading="loading"
    filterable
    remote
    :remote-method="handleRemoteQuery"
    :clearable="clearable"
    :disabled="disabled"
    @change="handleChange"
  >
    <el-option
      v-for="item in options"
      :key="item.id"
      :label="item.name"
      :value="item.id"
      :disabled="item.disabled"
    >
      <span style="float: left">{{ item.name }}</span>
    </el-option>
  </el-select>
</template>

<script setup>
import { ref, watch, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'

const props = defineProps({
  modelValue: {
    type: [String, Number, Array],
    required: true
  },
  width: {
    type: String,
    default: '60%'
  },
  clearable: {
    type: Boolean,
    default: true
  },
  disabled: {
    type: Boolean,
    default: false
  },
  orderId: {
    type: [String, Number],
    default: null
  },
  placeholder: {
    type: String,
    default: '请选择'
  },
  // 函数属性使用 camelCase 命名
  fnChange: {
    type: Function,
    default: null
  },
  handleSelectItem: {
    type: Function,
    default: null
  }
})

const emit = defineEmits(['update:modelValue', 'change'])

const loading = ref(false)
const options = ref([])
const disOpts = ref([])

// 使用 computed 处理双向绑定
const currentValue = computed({
  get: () => props.modelValue,
  set: (value) => {
    emit('update:modelValue', value)
  }
})

// 监听 orderId 变化
watch(() => props.orderId, (newVal, oldVal) => {
  if (newVal !== oldVal) {
    currentValue.value = ''
    refresh()
  }
}, { deep: true })

// 远程搜索方法
const handleRemoteQuery = (query) => {
  if (query) {
    refresh(query)
  } else {
    refresh()
  }
}

// 选择变化处理
const handleChange = (val) => {
  emit('change', val)
  
  if (props.fnChange) {
    props.fnChange(val)
  }
  
  if (props.handleSelectItem) {
    props.handleSelectItem(val)
  }
}

// 刷新数据方法
const refresh = async (param = '') => {
  loading.value = true
  
  try {
    // 这里替换为您的实际 API 调用
    const response = await $api.dongxw.OrderLine.query({
      orderBys: 'id|desc',
      param: {
        orderId: props.orderId,
        isDeleted: false,
        customerOrderCode: param || undefined
      }
    })
    
    options.value = response.data || []
  } catch (error) {
    console.error('获取数据失败:', error)
    ElMessage.error('获取数据失败')
    options.value = []
  } finally {
    loading.value = false
  }
}

// 提供外部调用的方法
const remoteQryOrder = (param) => {
  refresh(param)
}

// 组件挂载时初始化
onMounted(() => {
  refresh()
})

// 暴露方法给父组件
defineExpose({
  remoteQryOrder,
  refresh
})
</script>

<style scoped>
/* 组件样式 */
:deep(.el-select-dropdown__item span) {
  width: 140px;
  text-align: left;
  display: inline-block;
}
</style>

使用示例

在父组件中使用

vue

<template>
  <div class="container">
    <h3>订单选择器</h3>
    
    <OrderSelect
      v-model="selectedOrder"
      :order-id="currentOrderId"
      :width="'300px'"
      :clearable="true"
      placeholder="请选择订单行"
      @change="handleOrderChange"
    />
    
    <div style="margin-top: 20px;">
      <p>当前选中的值: {{ selectedOrder }}</p>
      <el-button @click="changeOrderId">切换订单ID</el-button>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import OrderSelect from '@/components/OrderSelect.vue'

const selectedOrder = ref('')
const currentOrderId = ref(123)

const handleOrderChange = (value) => {
  console.log('订单选择变化:', value)
  // 这里可以处理选择变化后的逻辑
}

const changeOrderId = () => {
  currentOrderId.value = currentOrderId.value === 123 ? 456 : 123
}
</script>

<style scoped>
.container {
  padding: 20px;
}
</style>

主要改进点

1. 使用 Composition API

  • 使用 setup 语法糖

  • 使用 ref 和 computed 响应式变量

  • 使用 watch 监听属性变化

2. 正确的双向绑定

  • 使用 modelValue 和 update:modelValue 替代 Vue 2 的 value 和 input

  • 使用 computed 处理双向绑定

3. 更好的 TypeScript 支持

  • 明确的类型定义

  • 更好的类型推断

4. 现代化的样式处理

  • 使用 scoped 样式

  • 使用 :deep() 选择器处理子组件样式

5. 错误处理

  • 添加了 try-catch 错误处理

  • 使用 ElMessage 显示错误信息

6. 组件暴露方法

  • 使用 defineExpose 暴露方法给父组件

  • 保持与原有组件相同的接口

注意事项

  1. API 调用:确保 $api.dongxw.OrderLine.query 在 Vue 3 中正确配置

  2. 样式穿透:使用 :deep() 替代 Vue 2 的 /deep/ 或 ::v-deep

  3. 事件处理:使用新的 emits 系统替代 Vue 2 的 $emit

  4. 生命周期:使用 onMounted 替代 created

这样的重写保持了原有功能,同时利用了 Vue 3 的新特性,代码更加简洁和现代化。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐