2023-07-14
Vue
0
请注意,本文编写于 1032 天前,最后修改于 3 天前,其中某些信息可能已经过时。

目录

Vue3 案例
Vue2 案例

在我们的业务里,我们通常会二次封装一些高频业务组件。对于封装组件有一个大原则就是我们应该尽量保持原有组件的接口,除了我们需要封装的功能外,我们不应该改变原有组件的接口,即保持原有组件提供的接口如props,events,slots等不变。这里我们对element plus 的input组件做二次封装为例,Vue版本3.3.4 element-plus版本2.3.8

Vue3 案例

组件二次封装需要解决一下问题

  • 支持原组件所有的属性和方法:$attrs解决
  • 支持原组件的插槽
  • 方便获取源组件所有的方法

这里我们对element plus的input组件简单封住一下,就是作为演示用的 MyInput.vue

vue
<!-- --> <template> <div class="myinput"> <span class="label">{{label}}</span> <!-- 如果不希望过滤掉某些属性 可以直接使用 $attrs --> <el-input v-bind="filteredAttrs" :clearable="clearable" ref="input"> <template #[slotName]="slotProps" v-for="(slot, slotName) in $slots" > <slot :name="slotName" v-bind="slotProps"></slot> </template> </el-input> </div> </template> <script lang="ts" setup> import {useAttrs,computed,ref } from 'vue' import { ElInput } from 'element-plus' defineOptions({ name: 'MyInput' }) defineProps({ label: String, clearable: { type: Boolean, default: true, }, }); const input = ref(); defineExpose({ input }); const attrs = useAttrs() // 如果我们不希望透传某些属性比如class, 我们可以通过useAttrs来实现 const filteredAttrs = computed(() => { return { ...attrs, class: undefined }; }); </script> <style lang="scss" scoped> .myinput{ .label{ color: #333; } } </style>

上面使用defineExpose + ref的组合来获取源组件上的实例方法

在其他组件里面使用MyInput

vue
<template> <div class="about"> <MyInput v-model="inputvalue" label="myinput" @input="inputHandle" ref="myInput"> <template #prepend> <el-select v-model="select" placeholder="请选择" style="width: 115px"> <el-option label="HTTPS" value="1" /> <el-option label="HTTP" value="2" /> </el-select> </template> <template #append> <el-button :icon="Search" /> </template> </MyInput> {{ inputvalue }} </div> </template> <script lang="ts" setup> import { ref,onMounted } from 'vue' import { ElButton, ElSelect, ElOption } from 'element-plus' import { Search } from '@element-plus/icons-vue' import MyInput from '../components/myinputs/index.vue' const inputvalue = ref('') const select = ref('1') const myInput = ref() const inputHandle = (e: InputEvent) => { console.log(e) } onMounted(() => { console.log(myInput.value.input); myInput.value.input.focus() }) </script> <style> @media (min-width: 1024px) { .about { min-height: 100vh; display: flex; align-items: center; } } </style>

image.png 注意事项: ref定义名称时,不要和组件名称相似,否则会报错的

Vue2 案例

element ui DatePicker组件当type = "daterange"时,添加默认的的快捷键

vue
<template> <!-- 透传所有属性、事件、插槽,完全兼容原生 el-date-picker --> <component :is="OriginalDatePicker" v-bind="$attrs" v-on="$listeners" :type="type" :pickerOptions="mergeedPickerOptions" v-model="currentValue" ref="datePicker" class="enganced-datepicker" > <!-- 透传插槽 --> <template v-for="(_, slot) in $scopedSlots" #[slot]="slotProps"> <slot :name="slot" v-bind="slotProps" /> </template> </component> </template> <script> // 关键:直接引入 ElementUI 原生日期组件构造函数 import { DatePicker } from 'element-ui' const selectDateByShortcutKeys = flag => { const set_time_f = function (dte, yr, mn, dy, h, m, s, ms) { yr ? dte.setFullYear(yr) : null; mn != null && mn >= 0 ? dte.setMonth(mn) : null; dy != null && dy >= 0 ? dte.setDate(dy) : null; h >= 0 ? dte.setHours(h) : null; m >= 0 ? dte.setMinutes(m) : null; s >= 0 ? dte.setSeconds(s) : null; ms >= 0 ? dte.setMilliseconds(ms) : null; return dte; }; let sDay = new Date(), eDay = new Date(); switch (flag) { case '-day': // 昨天 set_time_f(sDay, null, null, sDay.getDate() - 1, 0, 0, 0, 0); set_time_f(eDay, null, null, eDay.getDate() - 1, 23, 59, 59, 0); break; case 'day': // 今天 set_time_f(sDay, null, null, null, 0, 0, 0, 0); set_time_f(eDay, null, null, null, 23, 59, 59, 0); break; case '-weekday': // 上周 sDay = set_time_f( new Date( sDay.getTime() - 86400000 * (sDay.getDay() === 0 ? 7 + 6 : sDay.getDay() + 6) ), null, null, null, 0, 0, 0, 0 ); eDay = set_time_f( new Date( eDay.getTime() - 86400000 * (eDay.getDay() === 0 ? 7 : eDay.getDay()) ), null, null, null, 23, 59, 59, 0 ); break; case 'weekday': // 本周 sDay = set_time_f( new Date( sDay.getTime() - 86400000 * -(1 - (sDay.getDay() === 0 ? 7 : sDay.getDay())) ), null, null, null, 0, 0, 0, 0 ); eDay = set_time_f( new Date( eDay.getTime() + 86400000 * (7 - (eDay.getDay() === 0 ? 7 : eDay.getDay())) ), null, null, null, 23, 59, 59, 0 ); break; case '-month': // 上月 set_time_f(sDay, null, sDay.getMonth() - 1, 1, 0, 0, 0, 0); set_time_f(eDay, null, null, 0, 23, 59, 59, 0); break; case 'month': // 本月 set_time_f(sDay, null, null, 1, 0, 0, 0, 0); set_time_f(eDay, null, eDay.getMonth() + 1, 0, 23, 59, 59, 0); break; case '-year': // 去年 set_time_f(sDay, sDay.getFullYear() - 1, 0, 1, 0, 0, 0, 0); set_time_f(eDay, eDay.getFullYear() - 1, 11, 31, 23, 59, 59, 0); break; case 'year': // 本年 set_time_f(sDay, null, 0, 1, 0, 0, 0, 0); set_time_f(eDay, null, 11, 31, 23, 59, 59, 0); break; case '-7day': // 过去7天 sDay = new Date(sDay.getTime() - 7 * 86400000); break; case '-14day': // 过去14天 sDay = new Date(sDay.getTime() - 14 * 86400000); break; case '-15day': // 过去15天 sDay = new Date(sDay.getTime() - 15 * 86400000); break; case '-30day': // 过去30天 sDay = new Date(sDay.getTime() - 30 * 86400000); break; // 算上今天的过去n天 start case '-6day': // 过去7天 sDay = new Date(sDay.getTime() - 6 * 86400000); break; case '-13day': // 过去14天 sDay = new Date(sDay.getTime() - 13 * 86400000); break; case '-14day': // 过去15天 sDay = new Date(sDay.getTime() - 14 * 86400000); break; case '-29day': // 过去30天 sDay = new Date(sDay.getTime() - 29 * 86400000); break; // 算上今天的过去n天 end case '-1day': // 过去24小时 sDay = new Date(sDay.getTime() - 86400000); break; case '-hour': // 过去1小时 set_time_f(sDay, null, null, null, sDay.getHours() - 1, 0, 0, 0); set_time_f(eDay, null, null, null, eDay.getHours() - 1, 59, 59, 0); break; case 'hour': // 这1小时 set_time_f(sDay, null, null, null, sDay.getHours(), 0, 0, 0); set_time_f(eDay, null, null, null, eDay.getHours(), 59, 59, 0); break; case '-12hour': // 过去12小时 sDay = new Date(sDay.getTime() - 0.5 * 86400000); break; case '-24hour': // 过去24小时 sDay = new Date(sDay.getTime() - 86400000); break; case 'clear': // 不选 return; default: return; } return [sDay, eDay]; }; // 昨天、今天、上周、本周、上月、本月、去年、本年、过去7天、过去30天 const defaultShutKeys = [ { text: '昨天', flag: '-day' }, { text: '今天', flag: 'day' }, { text: '上周', flag: '-weekday' }, { text: '本周', flag: 'weekday' }, { text: '上月', flag: '-month' }, { text: '本月', flag: 'month' }, { text: '去年', flag: '-year' }, { text: '今年', flag: 'year' }, { text: '过去7天', flag: '-6day' }, { text: '过去30天', flag: '-29day' } ]; /** * 增强版 DatePicker 日期选择器 * 功能:type=daterange 时自动添加默认快捷键,无侵入、完全兼容原生组件 */ export default { name: 'ElDatePicker', props: { // 接收用户自定义的快捷键(可选) // shortcuts: { // type: Array, // default: () => [] // }, // 接收原生 type 属性,用于判断是否为日期范围选择 type: { type: String, default: '' }, pickerOptions: { type: Object, default: () => ({}) }, value: [String, Date, Array], }, data: () => { return { enhancePickerOptions: {} }; }, computed: { // 内部使用原生组件,不会递归 OriginalDatePicker() { return DatePicker }, /** * 合并最终使用的快捷键 * 规则: * 1. 非日期范围选择 → 使用用户传入的快捷键(无则为空) * 2. 日期范围选择 + 用户未传快捷键 → 使用默认快捷键 * 3. 日期范围选择 + 用户传了快捷键 → 使用用户自定义快捷键 */ mergedShortcuts() { // 非日期范围选择,直接返回用户传入的快捷键 if (this.type !== 'daterange') { return this.shortcuts; } // 日期范围选择:用户有自定义快捷键 → 优先使用 if (this.shortcuts && this.shortcuts.length > 0) { return this.shortcuts; } // ====================== 默认固定快捷键(可自行修改)====================== return [ { text: '前两周', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 14); // 14天 picker.$emit('pick', [start, end]); } }, { text: '过去一月', onClick(picker) { const end = new Date(); const start = new Date(); start.setMonth(start.getMonth() - 1); picker.$emit('pick', [start, end]); } }, { text: '过去三个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setMonth(start.getMonth() - 3); picker.$emit('pick', [start, end]); } }, { text: '今天', onClick(picker) { const date = new Date(); picker.$emit('pick', [date, date]); } }, { text: '本月', onClick(picker) { const start = new Date(); start.setDate(1); // 当月第一天 const end = new Date(); picker.$emit('pick', [start, end]); } } ]; }, mergeedPickerOptions() { let mergeShotcus = null; const defaultShortcuts = defaultShutKeys.map(item => { return { text: item.text, onClick(picker) { console.log(picker, selectDateByShortcutKeys(item.flag)); picker.$emit('pick', selectDateByShortcutKeys(item.flag)); } }; }); if (['datetimerange', 'daterange', 'monthrange'].includes(this.type) && (!this.pickerOptions.shortcuts || this.pickerOptions.shortcuts.length === 0)) { mergeShotcus = defaultShortcuts; }else{ mergeShotcus = this.pickerOptions.shortcuts; } return { ...this.pickerOptions, shortcuts: mergeShotcus }; }, currentValue: { get() { return this.value }, set(val) { this.$emit('input', val) } } }, // 暴露原生组件实例,支持调用原生方法(如 focus、blur 等) methods: { focus() { this.$refs.datePicker.focus(); }, blur() { this.$refs.datePicker.blur(); } } }; </script>

在main.js中引入组件,替换element ui原来的datepicker组件

js
import Vue from 'vue' import Element from 'element-ui' import './assets/styles/element-variables.scss' import EnhancedDatePicker from './components/element/datePicker.vue' Vue.use(Element, { size: Cookies.get('size') || 'medium', // set element-ui default size }) Vue.component('ElDatePicker',EnhancedDatePicker)

其它地方不用修改,对原代码无影响

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:千寻

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!