• vue2中常用组件封装、全局挂载及使用


    一、查询区组件封装

    属性与方法

    <!-- el-form属性
      1. hide-required-asterisk 是否隐藏必填字段的标签旁边的红色星号
      2. show-message 是否显示校验错误信息
      -->
    
      <!-- el-select
       属性no-data-text 选项为空时显示的文字,也可以使用slot="empty"设置
    
       触发事件,查看是否有配置事件,若有触发,若无不触发
    @change="
        formItemConfigure.change
          ? $emit(
              formItemConfigure.change,
              formData[formItemConfigure.key]
            )
          : ''
      " 
      -->
    
      <!-- el-option 
        属性:字典项或属性option字段。
        v-for="item in dict.type[formItemConfigure.dictName] || formItemConfigure.option" 
      -->
    
    
    查询区`展开收缩`效果:根据字段判断区域高度为`固定`的高度/高度为`auto`
    

    完整代码

    
    <template>
      <div class="baseSearch">
        <el-form
          :model="formData"
          ref="form"
          :class="[labelHidden ? 'labelHidden' : '']"
          :label-width="labelWidth ? labelWidth : 'auto'"
          :hide-required-asterisk="false"
          :show-message="false"
        >
          <!-- 根据字段isFold展示高度(收缩为minHeight,展开为高度auto) -->
          <el-row
            :gutter="20"
            :style="{ height: isFold ? mixHeight + 'px' : 'auto' }"
            class="control-search"
          >
            <div ref="collapseContainerRef">
              <el-col :span="leftSpan">
                <div
                  v-for="formItemConfigure in formItems"
                  :key="formItemConfigure.key"
                >
                  <el-col :span="8">
                    <el-form-item
                      :prop="formItemConfigure.key"
                      :label="formItemConfigure.label"
                    >
                      <!-- 输入框 -->
                      <el-input
                        v-if="formItemConfigure.type === 'input'"
                        :disabled="formItemConfigure.disabled"
                        :placeholder="formItemConfigure.placeholder"
                        v-model="formData[formItemConfigure.key]"
                        :maxlength="formItemConfigure.maxlength || 999"
                        style="width: 100%"
                      ></el-input>
    
                      <!-- 选择框 -->
                      <el-select
                        v-if="formItemConfigure.type === 'select'"
                        :disabled="formItemConfigure.disabled"
                        :placeholder="formItemConfigure.placeholder"
                        v-model="formData[formItemConfigure.key]"
                        :loading="formItemConfigure.isLoading || false"
                        :clearable="formItemConfigure.clearable"
                        filterable
                        style="width: 100%"
                        @change="
                          formItemConfigure.change
                            ? $emit(
                                formItemConfigure.change,
                                formData[formItemConfigure.key]
                              )
                            : ''
                        "
                        :no-data-text="
                          formItemConfigure.noDataText
                            ? formItemConfigure.noDataText
                            : ''
                        "
                      >
                        <el-option
                          v-for="item in dict.type[formItemConfigure.dictName] ||
                          formItemConfigure.option"
                          :key="item.value"
                          :label="item.label"
                          :value="item.value"
                        >
                        </el-option>
                      </el-select>
    
                      <!-- 数字 -->
                      <el-input-number
                        v-if="formItemConfigure.type === 'inputNumber'"
                        :disabled="formItemConfigure.disabled"
                        :placeholder="formItemConfigure.placeholder"
                        v-model="formData[formItemConfigure.key]"
                        :maxlength="formItemConfigure.maxlength || 999"
                      ></el-input-number>
    
                      <!-- 开始与结束日期 -->
                      <el-date-picker
                        v-if="formItemConfigure.type === 'daterange'"
                        :disabled="formItemConfigure.disabled"
                        v-model="formData[formItemConfigure.key]"
                        type="daterange"
                        range-separator="~"
                        start-placeholde="开始时间"
                        end-placeholde="结束时间"
                        value-format="yyyy-MM-dd"
                      >
                      </el-date-picker>
    
                      <!-- 开始与结束日期时间 -->
                      <el-date-picker
                        v-if="formItemConfigure.type === 'datetimerange'"
                        :disabled="formItemConfigure.disabled"
                        v-model="formData[formItemConfigure.key]"
                        type="datetimerange"
                        range-separator="~"
                        start-placeholde="开始日期"
                        end-placeholde="结束日期"
                        value-format="yyyy-MM-dd HH:mm:ss"
                        :default-time="['00:00:00', '23:59:59']"
                      >
                      </el-date-picker>
    
                      <!-- 日期(年月日) -->
                      <el-date-picker
                        v-if="formItemConfigure.type === 'daterangeYMD'"
                        :disabled="formItemConfigure.disabled"
                        v-model="formData[formItemConfigure.key]"
                        type="date"
                        value-format="yyyy-MM-dd"
                        :placeholder="'请输入' + formItemConfigure.label"
                      >
                      </el-date-picker>
                      <!-- 选择 -->
                      <el-radio-group
                        v-if="formItemConfigure.type === 'radiogroup'"
                        :disabled="formItemConfigure.disabled"
                        v-model="formData[formItemConfigure.key]"
                      >
                        <el-radio
                          v-for="item in formItemConfigure.radios"
                          :key="item.value"
                          :label="item.label"
                        >
                          {{ item.label }}
                        </el-radio>
                      </el-radio-group>
                      <!-- 组织树 -->
                      <treeselect
                        v-if="formItemConfigure.type === 'tree'"
                        v-model="formData[formItemConfigure.key]"
                        :options="treeData"
                        placeholder="请选择管理组织"
                        :title="treeLabel"
                        @select="(node) => tenantIdHandleSelect(node)"
                      >
                        <div
                          slot="option-label"
                          slot-scope="{
                            node,
                            shouldShowCount,
                            count,
                            labelClassName,
                            countClassName,
                          }"
                          :class="labelClassName"
                        >
                          <span :title="node.label">{{ node.label }}</span>
                        </div></treeselect
                      >
                    </el-form-item>
                  </el-col>
                </div>
              </el-col>
            </div>
            <el-col :span="rightSpan">
              <div>
                <el-button
                  type="primary"
                  size="mini"
                  @click="submitForm('ruleFrom')"
                  >查询</el-button
                >
                <el-button size="mini" plain @click="resetFrom">重置</el-button>
                <!-- 表单项超过三个时显示 展开/收缩 -->
                <template v-if="formItems.length > 3">
                  <el-button
                    size="small"
                    type="text"
                    v-if="isFold"
                    @click="handleToggleCollapse"
                  >
                    展开
                    <i class="el-icon-arrow-down el-icon--right"></i>
                  </el-button>
                  <el-button
                    size="small"
                    type="text"
                    v-else
                    @click="handleToggleCollapse"
                  >
                    收起
                    <i class="el-icon-arrow-up el-icon--right"></i>
                  </el-button>
                </template>
              </div>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </template>
    
    <script>
    import { deptTreeSelectBusiness } from "@/api/system/user";
    import Treeselect from "@riophae/vue-treeselect";
    import "@riophae/vue-treeselect/dist/vue-treeselect.css";
    
    import { getNextDate } from "@/utils/getDate";
     // 所有字典项(但会遇到一次性请求字典的问题,可改为登录后请求字典项在前端进行字典过滤)
    import { dicts } from "@/utils/table-columns-config";
    
    export default {
      name: "baseSearch",
      inheritAttrs: false,
      dicts: dicts,
      props: {
        // 默认收缩
        autoSpread: {
          type: Boolean,
          default: false,
        },
    
        // 最小折叠高度
        mixHeight: {
          type: Number,
          default: 45,
        },
    
        // 按钮风格,default-普通风格,simple-极简风格
        btnStyle: {
          type: String,
          default: "default",
          validator: (value) => ["default", "simple"].includes(value),
        },
    
        //左空间
        leftSpan: {
          type: Number,
          default: 19,
        },
        //右空间
        rightSpan: {
          type: Number,
          default: 5,
        },
        //对齐位置
        labelPosition: {
          type: String,
          default: "right",
        },
    
        autoReset: {
          type: Boolean,
          default: false,
        },
        // 表单项
        formItems: {
          type: Array,
          default: () => [],
        },
        labelHidden: {
          type: Boolean,
          default: false,
        },
        labelWidth: {
          type: String,
        },
      },
      components: { Treeselect },
      mounted() {
        this.defaultDateTime();
        this.getTreeData();
      },
      data() {
        return {
          // 是否折叠
          isFold: true,
          formData: {},
          treeData: [],
          treeLabel: "",
        };
      },
      watch: {
        // 监听展开收缩
        autoSpread: {
          handler(val) {
            this.isFold = !val;
          },
          immediate: true,
        },
      },
      methods: {
        // [折叠|展开]
        handleToggleCollapse() {
          this.isFold = !this.isFold;
          this.$nextTick(() => {
            // calcHeight方法是去计算查询区下面table的高度,动态设置table的高度,保证列表一屏显示全
            this.$emit("calcHeight");
          });
        },
        submitForm() {
          this.$refs.form.validate((valid) => {
            if (valid) {
              this.$emit("submit", this.formData);
              if (this.autoReset) this.resetForm();
            }
          });
        },
        defaultDateTime() {
          this.formData = {
            daterange: [getNextDate(-1), getNextDate(1)],
            datetimerange: [getNextDate(-1), getNextDate(1)],
          };
        },
        resetFrom() {
          this.defaultDateTime();
          const preserve = { isFold: this.isFold };
          Object.assign(this.$data, this.$options.data());
          Object.assign(this.$data, preserve);
          if (!this.isFold) {
            this.isFold = true;
          }
          this.$emit("reset");
        },
        //获取组织部门树  不含项目
        getTreeData() {
          deptTreeSelectBusiness().then((res) => {
            this.treeData = res.data;
          });
        },
        tenantIdHandleSelect(node) {
          this.treeLabel = node.label;
          console.log(node);
        },
      },
    };
    </script>
    <style lang="scss" scoped>
    .searchBtn {
      height: 34px;
      background: #2b8bfb;
      border-radius: 100px;
    }
    ::v-deep .searchBtn:hover,
    .searchBtn:focus {
      background: #46a6ff;
      border-color: #46a6ff;
      color: #ffffff;
    }
    ::v-deep .el-form-item__label:before {
      content: " " !important;
    }
    
    .control-search {
      overflow: hidden;
    }
    
    .el-form-item.el-form-item {
      margin-bottom: 12px;
    }
    </style>
    

    组件使用

    <template>
    	<baseSearch
              ref="baseSearch"
              :formItems="formItems"
              @reset="reset"
              @submit="handleSearch"
              @calcHeight="calcHeight"
            ></baseSearch>
    </template>
    
    // js中
    data() {
    	formItems: [
            {
              type: "input",
              label: "学号",
              key: "stuNo",
              placeholder: "请输入学号",
            },
            {
              type: "select",
              label: "学生名",
              key: "stuId",
              option: [], // 所有学生数据
              placeholder: "请选择学生",
            },
          ],
    }
    
    

    二、表格组件

    属性与方法

    <!-- 
      1. 动态表格高度:height="tableHeight"(像展开/收缩查询区后table的高度想要控制在一屏显示时就需要动态赋值)
      2. 表格中数据为空时显示"-"
        <template v-else #default="{ row }">
          <span>{{ row[item.key] || "-" }}</span>
        </template>  
      3. table列超出省略显示,鼠标移入显示全部的属性`tooltip`
    -->
    

    完整代码

    <!-- 表格 -->
    <template>
      <div>
        <div style="float: left; margin-bottom: 20px">
          <!-- 操作行 -->
          <slot name="btnList" />
        </div>
        <el-table
          ref="table"
          v-loading="loading"
          element-loading-text="数据加载中"
          :data="tableData"
          :row-key="id"
          highlight-current-row
          show-overflow-tooltip
          :pager-count="pagerCount"
          :row-class-name="rowClassName"
          :cell-style="{ padding: 0 }"
          :row-style="{ height: '39px' }"
          style="width: 100%"
          :height="tableHeight"
          @selection-change="handleSelectionChange"
          @select="select"
        >
          <el-table-column
            v-if="checkSelection"
            :reserve-selection="reserveSelection"
            type="selection"
            width="50"
            :selectable="checkSelectSet"
            fixed
          />
          <el-table-column
            v-if="order"
            label="序号"
            type="index"
            width="46"
            align="center"
            fixed
          />
          <el-table-column
            v-for="(item, index) in columns"
            :key="index"
            :align="item.align ? item.align : 'left'"
            :prop="item.key"
            :label="item.label"
            :min-width="item.minWidth"
            :fixed="item.fixed"
            show-overflow-tooltip
          >
            <template v-if="item.slot" #default="{ row, rowIndex }">
              <slot
                :name="item.slot"
                :row="row"
                :column="item"
                :index="rowIndex"
                class="btn"
              />
            </template>
    
            <template v-else #default="{ row }">
              <span>{{ row[item.key] || "-" }}</span>
            </template>
          </el-table-column>
        </el-table>
        <pagination
          v-show="total > 0 && !hidePagenation"
          :total="total"
          :page.sync="queryParams.pageNum"
          :page-sizes="[15, 25, 50, 100]"
          :limit.sync="queryParams.pageSize"
          @pagination="getList"
        />
      </div>
    </template>
    
    <script>
    import { arrDistinctByProp } from "@/utils/index";
    export default {
      components: {},
      props: {
        reserveSelection: { type: Boolean, default: false },
        id: {
          default: "id",
          type: String,
        },
        order: {
          type: Boolean,
          default: true,
        },
        tableData: {
          type: Array,
        },
        columns: {
          type: Array,
        },
        hidePagenation: {
          type: Boolean,
          default: false,
        },
        total: {
          type: Number,
          default: 0,
        },
        loading: {
          type: Boolean,
          default: false,
        },
        checkSelection: {
          type: Boolean,
          default: true,
        },
        queryParams: {
          type: Object,
          default: () => {
            return {
              pageNum: 1,
              pageSize: 10,
            };
          },
        },
        // 是否单选
        isLimit: {
          type: Boolean,
          default: false,
        },
        // 最大高度
        height: {
          type: String,
        },
        // 页码按钮的数量,当总页数超过该值时会折叠
        pagerCount: {
          type: Number,
          default: 7,
        },
        checkSelectSet: {
          type: Function,
          default: () => {
            return true;
          },
        },
        tableH: {
          type: String,
          default: "",
        },
      },
      watch: {
        tableH(val) {
          this.tableHeight = val;
        },
      },
      data() {
        return {
          tableHeight: "calc(100vh - 240px)",
        };
      },
    
      created() {},
      methods: {
        getList() {
          this.$emit("getList");
        },
    
        select(selection, row) {
          console.log(selection, row);
          if (this.isLimit) {
            // 清除 所有勾选项  单选
            this.$refs.table.clearSelection();
            this.$refs.table.toggleRowSelection(row, true);
          }
    
          // 当表格数据都没有被勾选的时候 就返回
          // 主要用于将当前勾选的表格状态清除
          if (selection.length === 0) return;
          // this.$emit("selectionChange", selection, row);
          // this.$refs.table.toggleRowSelection(row, true);
        },
        // 多选框选中数据
        handleSelectionChange(selection, row) {
          let selectionList = [];
          if (this.isLimit) {
            selectionList.push(selection[selection.length - 1]);
          } else {
            selectionList = selection;
          }
          //去重
          this.$emit("selectionChange", arrDistinctByProp(selectionList, this.id));
        },
        rowClassName(row, index) {
          row.row.index = row.rowIndex;
        },
      },
    };
    </script>
    

    组件使用

    // template中
    <baseTable
              :loading="loading"
              :columns="columns"
              :tableData="tableData"
              :total="total"
              :queryParams="queryParams"
              :tableH="tableH"
            >
              <template #action="record">
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="tableAction('update', record.row)"
                  >编辑</el-button
                >
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-delete"
                  @click="tipClick"
                  >删除</el-button
                >
              </template>
            </baseTable>
    
    // js中
    data() {
    	queryParams: {
            pageNum: 1,
            pageSize: 15,
            type: 0,
          },
    	columns: [
            {
              label: "学号",
              key: "stuNo",
              minWidth: 120,
            },
            {
              label: "学生名",
              key: "stuName",
              minWidth: 150,
            },
             {
              label: "操作",
              key: "action",
              fixed: "right",
              slot: "action",
              minWidth: 120,
              align: "center",
            },
            tableData: [], //表格数据
            total: 0,
    }
    
    
    // methods中
    计算table的高度
    calcHeight() {
          let height = this.$refs.baseSearch.$el.clientHeight;
          this.tableH = `calc(100vh - ${height}px - 245px)`;
    },
    

    三、表单组件

    属性与方法

     数据属性字段:disabled是否禁用、hidden是否显示该项。
    
    `el-autocomplete`远程搜索框:fetch-suggestions远程搜索的方法、
    	
       // 调用接口且过滤搜索
       querySearchAsync(val, cb) {
          // 这块是共用的列表查询接口,因此pageSize设置的很大
          let data = {
            pageNum: 1,
            pageSize: 10000,
            level: this.level,
            type: this.type,
            orgId: this.orgId,
            no: val,
          };
    
          this.dataArr = [];
          customerList(data).then((res) => {
            if (!res?.rows?.length) {
              cb([]);
              // 没有查询到做的一些重置操作
              this.baseData.forEach((item) => {
                Object.keys(item.baseFormModel).forEach((key) => {
                  if (filterArr.includes(key)) {
                    item.baseFormModel[key] = "";
                  }
                });
              });
              return;
            }
            for (let i of res.rows) {
              i.value = i.no; //组件中只认value字段,因此需要处理下数据
            }
            this.dataArr = res.rows;
    
            根据搜索条件过滤数据
            var results = val
              ? this.dataArr.filter(this.createStateFilter(val))
              : this.dataArr;
    
            cb(results);
          });
        },
        createStateFilter(val) {
          return (state) => {
            return state.value.toLowerCase().indexOf(val.toLowerCase()) === 0;
          };
        },
    

    完整代码

    <!-- 表单组件(新增/编辑) -->
    <template>
      <div>
        <el-form
          :model="formModel"
          :rules="rules"
          ref="baseForm"
          :label-width="'120px'"
        >
          <el-row :gutter="gutter">
            <div v-for="(item, index) in formData" :key="index">
              <el-col :span="item.span || 8" v-if="!item.hidden">
                <el-form-item :label="item.label" :prop="item.name">
                  <!-- 输入框 -->
                  <el-input
                    size="small"
                    v-if="item.type === 'input'"
                    v-model="formModel[item.name]"
                    :placeholder="item.disabled ? '' : '请输入' + item.label"
                    :disabled="item.disabled || !isUpdate"
                    style="width: 100%"
                  ></el-input>
                  <!-- 数字 -->
                  <el-input-number
                    size="small"
                    v-if="item.type === 'inputNumber'"
                    :min="item.min"
                    :max="item.max"
                    controls-position="right"
                    :placeholder="item.disabled ? '' : '请输入' + item.label"
                    v-model="formModel[item.name]"
                    style="width: 100%"
                  ></el-input-number>
                  <!-- 文本域 -->
                  <el-input
                    type="textarea"
                    :rows="item.row || 4"
                    v-if="item.type === 'textarea'"
                    v-model="formModel[item.name]"
                    :placeholder="item.disabled ? '' : '请输入' + item.label"
                    :disabled="item.disabled || !isUpdate"
                    :maxlength="item.maxLength || 500"
                    :show-word-limit="item.showLimit === false ? false : true"
                  ></el-input>
                  <!-- 单选框 -->
                  <el-radio-group
                    v-if="item.type === 'radio'"
                    v-model="formModel[item.name]"
                    :disabled="item.disabled || !isUpdate"
                    @input="radioChange"
                  >
                    <el-radio
                      v-for="list in dict.type[item.dictName]"
                      :key="list.value"
                      :label="list.value"
                    >
                      <!-- :style="{ marginTop: item.valueList.length > 3 ? '10px' : '0px' }" -->
                      {{ list.label }}
                    </el-radio>
                  </el-radio-group>
                  <!-- select选择器 -->
                  <el-select
                    size="small"
                    v-if="item.type === 'select'"
                    filterable
                    :disabled="item.disabled || !isUpdate"
                    v-model="formModel[item.name]"
                    :placeholder="item.disabled ? '' : '请选择' + item.label"
                    :multiple="item.multiple"
                    style="width: 100%"
                    @change="change(item.name, formModel[item.name])"
                  >
                    <el-option
                      v-for="list in dict.type[item.dictName] || item.option"
                      :key="list.value"
                      :label="list.label"
                      :value="list.value"
                    />
                  </el-select>
                  <!-- 日期 -->
                  <el-date-picker
                    size="small"
                    v-if="item.type === 'daterange'"
                    v-model="formModel[item.name]"
                    type="daterange"
                    range-separator="~"
                    :disabled="item.disabled || !isUpdate"
                    start-placeholde="开始时间"
                    end-placeholde="结束时间"
                    value-format="yyyy-MM-dd HH:mm:ss"
                    style="width: 100%"
                  >
                  </el-date-picker>
                  <!-- 日期(年月日) -->
                  <el-date-picker
                    size="small"
                    v-if="item.type === 'daterangeYMD'"
                    v-model="formModel[item.name]"
                    style="width: 100%"
                    type="date"
                    :disabled="item.disabled || !isUpdate"
                    value-format="yyyy-MM-dd"
                    :placeholder="item.disabled ? '' : '请输入' + item.label"
                  >
                  </el-date-picker>
                  <!-- 组织树 -->
                  <treeselect
                    style="margin-bottom: 0px"
                    v-if="item.type === 'tree'"
                    v-model="formModel[item.name]"
                    :options="treeData"
                    :placeholder="item.disabled ? '' : '请选择管理组织'"
                  >
                    <div
                      slot="option-label"
                      slot-scope="{
                        node,
                        shouldShowCount,
                        count,
                        labelClassName,
                        countClassName,
                      }"
                      :class="labelClassName"
                    >
                      <span :title="node.label">{{ node.label }}</span>
                    </div>
                  </treeselect>
                  <el-autocomplete
                    size="small"
                    v-if="item.type === 'autocomplete'"
                    style="width: 100%"
                    v-model="formModel[item.name]"
                    :fetch-suggestions="querySearchAsync"
                    :placeholder="item.disabled ? '' : '请输入' + item.label"
                    :disabled="item.disabled || !isUpdate"
                    @select="handleSelect"
                    @blur="handleBlur"
                  ></el-autocomplete>
                  <!-- 自定义 -->
                  <template v-if="item.type == 'slot'" #default>
                    <slot :name="item.name"></slot>
                  </template>
                </el-form-item>
              </el-col>
            </div>
          </el-row>
        </el-form>
      </div>
    </template>
    
    <script>
    import Treeselect from "@riophae/vue-treeselect";
    import "@riophae/vue-treeselect/dist/vue-treeselect.css";
    import { deleteEmpty } from "@/utils/index";
    import { dicts } from "@/utils/table-columns-config";
    export default {
      dicts: dicts,
      props: {
        formData: {
          type: Array,
        },
        formModel: {
          type: Object,
        },
        isUpdate: {
          type: Boolean,
          default: true,
        },
        rules: {
          type: Object,
        },
        inline: {
          type: Boolean,
          default: false,
        },
        gutter: {
          type: Number,
          default: 24,
        },
      },
      data() {
        return {
          treeData: [],
        };
      },
    
      components: { Treeselect },
      methods: {
        // 这种触发事件也可写入标签中
        radioChange(val) {
          this.$emit("radioChange", val);
        },
        querySearchAsync(val, cb) {
          this.$emit("query", val, cb);
        },
        handleSelect(val) {
          this.$emit("select", val);
        },
        handleBlur() {
          this.$emit("blur");
        },
        getData() {
          return deleteEmpty(this.formModel);
        },
        change(name, val) {
          this.$emit("selectChange", { name, val });
        },
        // 表单校验
        async validateHandle() {
          try {
            return await this.$refs.baseForm.validate();
          } catch (error) {
            return false;
          }
       	}
      },
    };
    </script>
    <style lang="scss" scoped>
    .el-form-item__label.long-label {
      display: block;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      word-wrap: break-word;
      max-width: 100%;
    }
    </style>
    

    组件使用

    // template
    // 动态遍历的多个表单
    // proTitle为每个表单的title组件
    <div v-for="(item, index) in baseData" :key="index">
              <proTitle
                :groupTitle="item.title"
                style="margin-top: 10px; font-size: 14px"
              />
              <baseForm
                :ref="item.ref"
                :formData="item.baseFormData"
                :formModel="item.baseFormModel"
                :rules="item.baseRules"
                @selectChange="selectChange"
              >
              </baseForm>
            </div>
    
    
    js中
    data() {
    	{
              title: "学生基本信息",
              ref: "form1",
              baseFormData: [
                {
                  label: "学号",
                  name: "stuNo",
                  type: "input",
                  disabled: true,
                },
                {
                  label: "学生姓名",
                  name: "stuId",
                  type: "select",
                  option: [],
                },
                {
                  label: "体育类型",
                  name: "type",
                  type: "select",
                  dictName: "ty_type", //字典code
                },
                {
                  label: "备注",
                  name: "remarks",
                  type: "textarea",
                  span: 24,
                },
              ],
              baseFormModel: {
                stuNo: "",
                stuId: "",
                type: "",
                remarks: "",
              },
              // 必填项校验规则
              baseRules: {
                stuNo: [
                  {
                    required: true,
                    trigger: "blur",
                    message: "请输入学号",
                  },
                ],
                stuId: [
                  {
                    required: true,
                    trigger: "blur",
                    message: "请选择学生姓名",
                  },
                ],
              },
            },
            {
              title: "学生父母信息",
              ref: "form2",
              baseFormData: [
                // ...
              ],
              baseFormModel: {
                // ...
              },
              baseRules: {
                // ...
              },
            },
    }
    
    js方法
    // 当编辑时给表单赋值,获取到接口数据后赋值
    this.baseData.forEach((item) => {
              Object.keys(item.baseFormModel).forEach((key) => {
                item.baseFormModel[key] = res.data[key];
              });
    });
    
    
    // 调用子组件表单校验方法中部分代码(非动态表单ref触发表单校验(ref="form"))
    let result = await this.$refs.form.validateHandle();
    if(result) {
    	 // 校验通过后的代码
    }
    

    四、树组件

    属性与方法

    <!-- 
     属性
      1. node-key 唯一标识的属性
      2. :props="treeProps"自定义字段
      3. :default-expand-all是否默认展开(`全部`4. :default-expanded-keys设置`某一层`展开,数据类型为需要展开的节点id数组集合。
         (若设置第一层展开,:default-expanded-keys="[treeData[0].id]"6. :filter-node-method搜索方法(异步树时采用查询接口的方式,会查的数据全些)
     方法
      @node-click单击当前节点(懒加载数据等操作)
    -->
    

    完整代码

    <template>
      <div class="common-tree">
        <el-tree
          ref="tree"
          node-key="id"
          :default-expanded-keys="expandedKeys"
          highlight-current
          :data="treeData"
          :props="treeProps"
          :expand-on-click-node="expandNode"
          :current-node-key="currentNodeKey"
          :filter-node-method="filterNode"
          :show-checkbox="showCheckbox"
          @node-click="nodeClick"
        />
      </div>
    </template>
    
    <script>
    export default {
      name: "CommonTree",
      props: {
        treeData: {
          type: Array,
          default: () => [],
        },
        expandNode: {
          type: Boolean,
          default: false,
        },
        showCheckbox: {
          type: Boolean,
          default: false,
        },
        currentNodeKey: {
          type: String,
          default: "1457",
        },
        defaultExpandAll: {
          type: Boolean,
          default: false,
        },
      },
      data() {
        return {
          // 自定义属性字段名
          treeProps: {
            children: "children",
            label: "label",
          },
        };
      },
      computed: {
        expandedKeys() {
          return this.treeData && this.treeData.length > 0
            ? [this.treeData[0].id]
            : [];
        },
      }
      methods: {
        // 模糊匹配
        filterTree(val) {
          this.$refs.tree.filter(val);
        },
        // 筛选节点
        filterNode(value, data) {
          if (!value) return true;
          return data.label?.indexOf(value) !== -1;
        },
        // 单击节点
        nodeClick(data) {
          this.$emit("nodeClick", data);
        },
      },
    };
    </script>
    
    <style scoped>
    .common-tree {
      height: calc(100vh - 240px);
      overflow: auto;
    }
    </style>
    

    组件使用

    <CommonTree
    	ref="treeRef"
        :treeData="treeData"
        @nodeClick="nodeClick"
    ></CommonTree>
    

    五、人员选择器组件

    属性与方法

    <!-- 
     属性
      1. v-model绑定是数据为`右侧数据的id数组`
      2. filterable开启搜索
      3. titles自定义title属性
     `左侧数据`字段:label为名称、key为唯一值
     方法
      @node-click单击当前节点(懒加载数据等操作)
    -->
    

    完整代码

    <template>
      <el-transfer
        v-model="selectData"
        :filterable="filterable"
        :titles="titles"
        :filter-placeholder="placeholder"
        :data="data"
      >
      </el-transfer>
    </template>
    
    <script>
    export default {
      props: {
        placeholder: {
          type: String,
          default: "请输入人员名称",
        },
        titles: {
          type: Array,
          default: () => {
            return ["未选择", "已选择"];
          },
        },
        filterable: {
          type: Boolean,
          default: true,
        },
        data: {
          type: Array,
          default: () => [],
        },
        memberIds: {
          type: Array,
          default: [],
        },
      },
      watch: {
        memberIds(newVal) {
          this.selectData = newVal;
        },
      },
      data() {
        return {
          selectData: [], //已选人员id数组
        };
      },
    };
    </script>
    
    <style>
    .el-transfer-panel {
      height: 500px;
    }
    </style>
    
    

    组件使用

    <Transfer ref="transferRef" :data="userList" />
    

    组件全局挂载

    // 在main.js中全局挂载(举个栗子)
    
    import baseForm from "@/components/base/baseForm.vue";
    Vue.component("baseForm", baseForm);
    
  • 相关阅读:
    五、04【Java IO模型】之字符流
    华为云应用中间件DCS系列—Redis实现(社交APP)实时评论
    [野火]STM32 F103 HAL库开发实战指南笔记总结
    在 Java 中检查空字符串或空白字符串
    汇编:寄存器/register,基础概念
    每日三题 10.19
    在Linux下安装MySQL
    Android端如何实现拉取RTSP/RTMP流并回调YUV/RGB数据然后注入轻量级RTSP服务?
    【精读系列】GloVe: Global Vectors for Word Representation
    leetcode Top100 (5) 盛最多水的容器
  • 原文地址:https://blog.csdn.net/wch19960518/article/details/139922106