vue3 setup学习之[mixin]

1 mixin 使用场景

针对一般的后台管理系统,会在大量的页面中出现搜索查询功能,此时如果需要封装这些表单和查询条件,则需要采用mixin原理来实现了。

2 mixin 如何实现

由于vue3 setup中没有像vue2中的mixins: []混入,因此需要手动实现它 见代码清单form-table.d.ts文件。

3 mixin 如何使用

在页面中改如何使用它呢,在页面中先引入解构后,在组件中即可使用混入的封装的方法和对象

<script setup lang="ts" name="systemuser">
....
//引入
import FormTableMixin from '@/mixins/form-table.d'
//使用
const service = async (params:object) => {
  const { data } = await postListDataRequest('users',params);
  return {
    data: data.list,
    total: data.totalCount,
  }
}
const {form,  table,  params} = FormTableMixin(service,{});
....
</script>
<template>
  <div class="systemuser">
    <!-- 搜索区 -->
    <PageSearch
      :config="searchConfig"
      :form="form"
    ></PageSearch>
  </div>
</template>
4 form-table.d.ts代码清单
/********* form-table.d.ts文件 ********/
import { ref, reactive, onMounted, computed } from 'vue';
export default function FormTableMixin(service, defaultProps) {
  const form = reactive({
    value: null||{},
    onInput: function(a) {},
    onSubmit: function() {}
  });
  const table = reactive({
    data: [],
    pagination: {
      offset: 1,
      size: 15,
      total: 0,
      ...defaultProps,
    },
  });
  // 计算属性
  const params = computed(() => {
    const { pagination } = table;
    return {
      ...form.value,
      offset: pagination.offset,
      size: pagination.size,
    };
  });
  // 表单提交事件
  form.onSubmit = () => {
    table.pagination.offset = 1;
    getList();
  };
  // 表单输入事件
  form.onInput = (val) => {
    form.value = val;
  };
  // 分页变化事件
  const onChange = (pagination) => {
    Object.assign(table.pagination, pagination);
    getList()
  };
  const getList = async () => {
    const { data = [], total = 0 } = (await service.call(this, params.value, this)) || {};
    table.data = data;
    if (table.pagination) {
      table.pagination.total = total
    }
  }
  // 返回响应式数据和方法
  return {
    form,
    table,
    getList,
    params,
    onChange,
  };
}
5 PageSearch.ts代码清单
<script setup lang='ts'>
import { ref } from 'vue'
import BaseFormItem from './form-item.vue'
import { ElForm } from 'element-plus';
const props = defineProps({
  form: {
    type: Object,
    onInput: {
      type: Function
    },
    onSubmit: {
      type: Function
    },
    value: {
      type: Object
    }
  },
  config: {
    type: Array,
    required: true
  }
});

const emit = defineEmits(['submit']);
const searchForm = ref<InstanceType<typeof ElForm> | null>(null);

function initValues() {
  if (props.form) {
    props.form.value = props.config.reduce((acc: any, item: any) => {
      acc[item.prop] = item.value
      return acc
    }, {})
  }
}

const submit = () => {
  const submitValues = { ...props.form?.value };
  if (props.form?.onSubmit) {
    props.form.onSubmit(submitValues)
  }
  emit('submit', submitValues)
}

const reset = () => {
  searchForm.value?.resetFields()
  submit()
}

initValues();

defineExpose({
  searchForm,
  submit,
  reset
})

</script>
<template>
  <div class='common-search'>
    <el-form ref="searchForm" :model="form.value" inline @submit.native.prevent>
      <BaseFormItem
        v-for="item in config"
        class="base-search__form-item"
        v-model="form.value[item.prop]"
        :key="item.prop"
        v-bind="item"
      />
      <slot name='footer'>
        <el-form-item class='common-search__footer'>
          <span @click='reset'>重置</span>
          <span @click='submit'>搜索</span>
        </el-form-item>
      </slot>
    </el-form>
  </div>
</template>

<style scoped lang="less">
.common-search__footer {
  span {
    display: inline-block;
    text-align: center;
    border-radius: 1px;
    width: 136px;
    height: 36px;
    line-height: 36px;
    cursor: pointer;

    &:first-child {
      border: 1px solid blue;
      color: blue;
      margin-right: 10px;
    }

    &:last-child {
      background: blue;
      color: white;
      border: 1px solid transparent;
    }
  }
}
::v-deep {
  .base-search__form-item {
    margin-right: 40px;
  }

  .el-input {
    width: 260px;
  }
  .el-select {
    width: 260px;
  }
}
</style>
6 BaseFormItem.ts代码清单
<template>
  <el-form-item size="large" :label="label ? `${label}` : ''" :prop="prop">
    <component :is="El[`el-${component}`]" v-model="currentValue" v-bind="options">
      <template v-if="component === 'select'">
        <el-option
          v-for="(item, index) in selectOptions"
          :key="index"
          :label="item.label"
          :value="item.value"
        ></el-option>
      </template>
    </component>
  </el-form-item>
</template>

<script setup lang="ts">
import { defineProps, defineEmits, computed, useAttrs } from 'vue';
import {ElInput,ElSelect,ElDatePicker} from 'element-plus';
const El = {
  'el-input': ElInput,
  'el-select': ElSelect,
  'el-date-picker': ElDatePicker
}
// 定义属性
const props = defineProps({
  modelValue: null,
  label: String,
  prop: {
    type: String,
    required: true,
  },
  component: {
    type: String,
    required: true,
  },
});
// 定义事件
const emit = defineEmits(['update:modelValue']);
// 定义默认选项映射
const defaultOptionMap:any = {
  input: {
    placeholder: (label:string) => `请输入${label}`,
  },
  select: {
    placeholder: (label:string) => `请选择${label}`,
  },
  'date-picker': {
    placeholder: (label:string) => `请选择${label}`,
    rangeSeparator: '至'
  },
};
// 计算属性
const currentValue = computed({
  get() {
    return props.modelValue;
  },
  set(val) {
    emit('update:modelValue', val);
  },
});
const options = computed(() => {
  const attrs = { ...useAttrs() };
  const defaultOption = getDefaultOption();
  return {
    ...defaultOption,
    clearable: true,
    ...attrs,
  };
});
const selectOptions = computed(() => {
  const attrsOptions = useAttrs().options;
  return typeof attrsOptions === 'function' ? attrsOptions() : attrsOptions;
});
// 方法
function getDefaultOption() {
  const type = useAttrs().type;
  const key = type ? `${props.component}-${type}` : props.component;
  const defaultOption = defaultOptionMap[key] || defaultOptionMap[props.component] || {};
  let placeholder = defaultOption.placeholder;

  if (placeholder) {
    placeholder = '';
  }

  return { ...defaultOption, placeholder };
}
</script>

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值