Vue3.0 根據(jù)JSON對(duì)象生成指定form表單

在平常開發(fā)中,form表單是寫的最多且最繁瑣的一個(gè)功能。為了避免代碼冗余,我把form表單抽出來當(dāng)做一個(gè)組件并且根據(jù)數(shù)據(jù)來動(dòng)態(tài)生成基本表單

現(xiàn)在只寫了input和select 后續(xù)會(huì)加上更多,且優(yōu)化雙向綁定的代碼

用到的技術(shù)

1.Vue3基礎(chǔ)知識(shí)
2.Element-plus組件

定義formTemplate

判斷是否需要布局(el-col)
判斷是否需要form包裹(el-form-item)
根據(jù)傳遞的參數(shù)來生成指定組件(components):比如輸入框 下拉框等

注意:使用的時(shí)候需要全局注冊(cè)一下指定組件 具體看 倒數(shù)第二步 全局注冊(cè)組件

formTemplate.vue

<template>
  <el-col v-if="isCol" :span="colSpan">
    <div v-if="isForm">
      <el-form-item :prop="prop" :label="label">
        <component
          :is="isCom"
          :config="config"
          v-model="templateValue"
          @change="change"
        />
      </el-form-item>
    </div>
    <div v-else>
      <component
        :is="isCom"
        :config="config"
        v-model="templateValue"
        @change="change"
      />
    </div>
  </el-col>
  <div v-else>
    <div v-if="isForm">
      <el-form-item :prop="prop" :label="label">
        <component
          :is="isCom"
          :config="config"
          v-model="templateValue"
          @change="change"
        />
      </el-form-item>
    </div>
    <div v-else>
      <component
        :is="isCom"
        :config="config"
        v-model="templateValue"
        @change="change"
      />
    </div>
  </div>
</template>

<script setup>
import { watch, ref } from "vue";

/**
 * @author BlackKey
 * @params modelValue 雙向綁定值
 * @params colSpan 布局占位
 * @params isCol 是否啟用布局
 * @params isForm 是否用form包裹
 * @params label 標(biāo)題
 * @params prop model的鍵名
 * @params isCom 需要的生成的組件
 * @params config 組件對(duì)應(yīng)的配置文件
 * @desc  form表單默認(rèn)格式
 */

const { colSpan, isCol, isForm, label, prop, isCom, modelValue, config } =
  defineProps({
    colSpan: {
      type: Number,
      default: 6,
    },
    isCol: {
      type: Boolean,
      default: true,
    },
    isForm: {
      type: Boolean,
      default: true,
    },
    label: {
      type: String,
      default: "",
    },
    prop: {
      type: String,
      default: "",
    },
    isCom: {
      type: String,
      default: "wx-input",
    },
    modelValue: {
      type: [String, Number, Object, Array],
      default: "",
    },
    config: {
      type: Object,
      default: () => ({}),
    },
  });
const templateValue = ref(modelValue);
const emit = defineEmits(["change"]);
const change = (data) => {
  emit("change", data);
};
</script>

<style lang="less" scoped></style>

默認(rèn)輸入框組件

wxInput.vue

<template>
  <el-input
    v-model="inputValue"
    :placeholder="placeholder"
    :clearable="clearable"
    :disabled="disabled"
    :type="type"
    :maxlength="maxlength"
    :autosize="autosize"
  />
</template>

<script setup>
import { watch, ref } from "vue";
/**
 * @author BlackKey
 * @params modelValue 雙向綁定值
 * @params placeholder 輸入框占位符文本
 * @params type 輸入框類型
 * @params maxlength 最大長度
 * @params disabled 是否禁用
 * @params size 輸入框尺寸
 * @params autosize textarea 高度是否自適應(yīng),僅 type 為 'textarea' 時(shí)生效。
 * @params clearable 是否展示清空按鈕
 * @desc  輸入框
 */

const { config, modelValue } = defineProps({
  config: {
    type: Object,
    default: () => ({}),
  },
  modelValue: {
    type: [String, Number, Object, Array],
    default: "",
  },
});
const {
  placeholder = "",
  type = "text",
  maxlength = "",
  disabled = false,
  size = "",
  autosize = false,
  clearable = true,
} = config;

const emit = defineEmits(["change"]);
const inputValue = ref(modelValue);
watch(
  () => inputValue.value,
  (data) => {
    change(data);
  }
);
const change = (data) => {
  emit("change", data);
};
</script>

默認(rèn)下拉框組件

為了防止枚舉寫一遍 options又寫一遍的情況 我把枚舉值引入進(jìn)來了 (baseSelectSource)
如果枚舉寫了,就直接傳dataType即可

meta.js

const sex = [
  {
    label: "男",
    value: "man",
  },
  {
    label: "女",
    value: "woman",
  },
];

export const baseSelectSource = {
  sex,
};

wxSelect.vue

<template>
  <el-select
    v-model="selectValue"
    :clearable="clearable"
    :placeholder="placeholder"
    :disabled="disabled"
    :size="size"
  >
    <el-option
      v-for="item of (options.length && options) || source || []"
      :label="item[label]"
      :value="item[value]"
    ></el-option>
  </el-select>
</template>

<script setup>
import { watch, ref } from "vue";
import { baseSelectSource } from "@/meta";
/**
 * @author BlackKey
 * @params modelValue 雙向綁定值
 * @params placeholder 輸入框占位符文本
 * @params disabled 是否禁用
 * @params size 輸入框尺寸
 * @params clearable 是否展示清空按鈕
 * @params options 下拉框的值
 * @params dataType 預(yù)先設(shè)定好的枚舉
 * @params labelKey 下拉框labelKey
 * @params valueKey 下拉框valueKey
 * @desc  下拉框
 */

const { config, modelValue } = defineProps({
  config: {
    type: Object,
    default: () => ({}),
  },
  modelValue: {
    type: [String, Number, Object, Array],
    default: "",
  },
});
const {
  placeholder = "",
  disabled = false,
  size = "",
  clearable = true,
  options = [],
  dataType = "",
  labelKey = "label",
  valueKet = "value",
} = config;

// 如果dateType有值 代表下拉框取枚舉值 從meta里面獲取數(shù)組用來展示下拉框
const source = ref([]);
if (dataType) {
  source.value = baseSelectSource[dataType];
}

const emit = defineEmits(["change"]);
// 接收labelKey和valueKey且綁定
const label = ref(labelKey);
const value = ref(valueKet);
const selectValue = ref(modelValue);
watch(
  () => selectValue.value,
  (data) => {
    change(data);
  }
);
const change = (data) => {
  emit("change", data);
};
</script>

全局注冊(cè)組件

使用該組件的時(shí)候如果不全局注冊(cè)需要的組件 這樣會(huì)導(dǎo)致生成失敗
registerComponent.js

import FormTemplate from "@/components/FormTemplate/index.vue";
import wxInput from "@/components/FormTemplate/components/wxInput.vue";
import wxSelect from "@/components/FormTemplate/components/wxSelect.vue";

export default function install(app) {
  app.component("wx-form", FormTemplate);
  app.component("wx-input", wxInput);
  app.component("wx-select", wxSelect);
}

注意:記得在main里面引入registerComponent.js文件 否則注冊(cè)失敗

main.js

import { createApp } from "vue";
import App from "./App.vue";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import "@/assets/css/index.less";
import registerComponent from "./registerComponent";

const app = createApp(App);

app.use(registerComponent);
app.use(ElementPlus);
app.mount("#app");

使用方法

index.vue

注意:使用時(shí)記得用el-form和el-row包裹一下 因?yàn)槲以O(shè)置的默認(rèn)使用布局和form包裹住

<template>
  <el-form :model="form" ref="formRef" label-width="80px">
    <el-row>
      <wx-form
        v-for="item in formTemplate"
        :label="item.label"
        :config="item.config"
        :isCom="item.isCom"
        @change="item.modelValue = $event"
        v-model="item.modelValue"
        ></wx-form>
      <el-col :span="24">
        <div class="btn">
          <el-button type="primary" @click="search">查詢</el-button>
        </div>
      </el-col>
    </el-row>
  </el-form>
</template>

<script setup>
  import { reactive, toRefs, ref } from "vue";
  const userData = reactive({
    form: { name: "", account: "", tel: "", sex: "" },
  });
  const { form } = { ...toRefs(userData) };

  const formTemplate = ref([
    {
      label: "昵稱",
      modelValue: form.value.name,
      prop: "name",
      config: {
        placeholder: "請(qǐng)輸入昵稱",
      },
    },
    {
      label: "賬號(hào)",
      modelValue: form.value.account,
      prop: "account",
      config: {
        placeholder: "請(qǐng)輸入賬號(hào)",
      },
    },
    {
      label: "手機(jī)",
      modelValue: form.value.tel,
      prop: "tel",
      config: {
        placeholder: "請(qǐng)輸入手機(jī)",
      },
    },
    {
      label: "性別",
      modelValue: form.value.sex,
      isCom: "wx-select",
      prop: "sex",
      config: {
        placeholder: "請(qǐng)選擇性別",
        dataType: "sex",
      },
    },
  ]);

  // 查詢
  const search = () => {
    formTemplate.value.forEach((val) => {
      form.value[val.prop] = val.modelValue;
    });
    console.log(form.value);
  };
</script>

<style lang="less" scoped>
  .btn {
    text-align: right;
  }
</style>

效果圖

效果圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 注意:升級(jí)前,node版本需要在8.9的版本以上! 本文目錄: 為什么要升級(jí)框架? 主要模塊升級(jí)對(duì)應(yīng)版本 vue2...
    繪夢_閱讀 9,702評(píng)論 0 2
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 簡單小巧、漸進(jìn)式、功能強(qiáng)大的技術(shù)棧 1.1.2 為什么學(xué)習(xí)...
    蔡華鵬閱讀 3,510評(píng)論 0 3
  • Vue.js(讀音/vju?/, 類似于 view)是一個(gè)構(gòu)建數(shù)據(jù)驅(qū)動(dòng)的web 界面的漸進(jìn)式框架。Vue.js的目...
    xingyunfuhao閱讀 795評(píng)論 0 0
  • Vue學(xué)習(xí)框架 1 Vue 2 Vue框架 3 Vue問題 4 Vue生命周期 參考:https://cn.vue...
    司空洛一閱讀 558評(píng)論 0 0
  • 正在更新中... 聲明:所有文章都是轉(zhuǎn)載整理的,只是為了自己學(xué)習(xí),方便自己觀看,如有侵權(quán),請(qǐng)立即聯(lián)系我,謝謝~ V...
    是河兔兔啊閱讀 4,658評(píng)論 0 1

友情鏈接更多精彩內(nèi)容