19.前端工程化:Vue-cli3和Element-UI

lishihuan大约 25 分钟

19.前端工程化:Vue-cli3和Element-UI

自定义校验规则

<script>
export default {
  data() {
    // 验证邮箱的规则
    var checkEmail = (rule, value, cb) => {
      // 验证邮箱的正则表达式
      const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/

      if (regEmail.test(value)) {
        // 合法的邮箱
        return cb()
      }

      cb(new Error('请输入合法的邮箱'))
    }

    // 验证手机号的规则
    var checkMobile = (rule, value, cb) => {
      // 验证手机号的正则表达式
      const regMobile = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/

      if (regMobile.test(value)) {
        return cb()
      }
      cb(new Error('请输入合法的手机号'))
    }

    return {
      total: 0,
      // 控制添加用户对话框的显示与隐藏
      addDialogVisible: false,
      // 添加用户的表单数据
      addForm: {
        username: '',
        email: '',
        mobile: ''
      },
      // 添加表单的验证规则对象
      addFormRules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          {
            min: 3,
            max: 10,
            message: '用户名的长度在3~10个字符之间',
            trigger: 'blur'
          }
        ],

          // 自定义 邮箱校验规则
        email: [
          { required: true, message: '请输入邮箱', trigger: 'blur' },
          { validator: checkEmail, trigger: 'blur' }
        ],
          /// 自定义手机号验证
        mobile: [
          { required: true, message: '请输入手机号', trigger: 'blur' },
          { validator: checkMobile, trigger: 'blur' }
        ]
      }
    }
  },
  created() {
    
  },
  methods: {


  }
}
</script>

elementUi按需引入刷新页面显示message弹框的问题

这个是错误的引入方式,也就问题的根源

Vue.use(Message)// 关键点就是你全局引入

我们将上面的改为下面这种就完美的解决了问题

Vue.prototype.$message = Message;

组件的封装--自定义

1.弹窗显示详情

2.页面中组件的形式添加列表

12123
12123
image-20220426180045078
image-20220426180045078

element navBar和 sidebar

使用 path 作为 选中的tab的标识 https://blog.csdn.net/nanchen_J/article/details/122919711open in new windowhttps://www.jb51.net/article/237540.htmopen in new windowhttps://blog.csdn.net/weixin_41856395/article/details/110441057open in new windowhttps://huaweicloud.csdn.net/638f131edacf622b8df8ea82.htmopen in new windowhttps://blog.csdn.net/weixin_43180359/article/details/106213668open in new window

开发记录

1. 复选框 el-checkbox

说明,存在无法点击 通过 this.$set(this.form, 'dsRuleIds',dsRuleIds); 进行赋值

通过 v-model 数据绑定和默认选中,值对应的是 label

<el-checkbox-group v-model="form.dsRuleIds" size="small">
    <el-checkbox
                 v-for="item in dsBaseOrgRuleList"
                 :label="item.id+''"
                 :key="item.id"
                 border
                 >{{ item.name }}
    </el-checkbox>
</el-checkbox-group>
rules: {
    dsRuleIds: [
        { type: 'array', required: true, message: '请至少选择一个规则', trigger: 'change' }
    ],
},
// 初始化 选中的 规则
initCheckRule(type){
    var dsRuleIds=[];
    if(this.form.ruleIds){
        dsRuleIds=this.form.ruleIds.split(",");
    }
    if(type=='all'){// 说明是初始化时,需要全部默认选中
        this.dsBaseOrgRuleList.forEach(item=> {
            dsRuleIds.push(item.id+'');
        });
    };
    this.$set(this.form, 'dsRuleIds',dsRuleIds);
},

2.el-select 多选,初始化异常

<el-select filterable v-model="form.postIdArr" multiple placeholder="请选择岗位">
    <el-option
               v-for="item in postOptions"
               :key="item.postId"
               :label="item.postName"
               :value="item.postId"
               ></el-option>
</el-select>
</el-form-item>

var postIdArr = [];
this.form.postIds.split(',').forEach(item => {
	postIdArr.push(Number(item));
})
this.form.postIdArr = postIdArr;

3.el-select 联动中赋值前执行重置,导致数据无法选择

主要原因是 重置方式不对,,通过 this.$set(this.queryParams,'xx','') 来重置,这样就不会影响

image-20221101200842256
image-20221101200842256
 //获取线路GT
checkLine (node, instanceId) {
    this.form.lineId = node.id
    this.form.lineName = node.lineName
    // 执行重置,清空上一次的操作选中的数据(但是这样会导致 后面下拉选数据选择失效,通过 $forceUpdate 跟新)
    this.checkTowerNo({},'start');
    this.checkTowerNo({},'end')
    this.getTower(node.id)// 联动 GT
},
// 选中GT,赋值
    checkTowerNo (item,type) {
        if(type=='start'){// 起点
            this.form.startTowerId=item.id;
            this.form.startTowerNo=item.towerNo;
            this.form.startTowerName=item.towerName;
            this.form.startTowerOrderNum=item.orderNum;
        }else{
            // 终点
            this.form.endTowerId=item.id;
            this.form.endTowerNo=item.towerNo;
            this.form.endTowerName=item.towerName;
            this.form.endTowerOrderNum=item.orderNum;
        }
        this.$nextTick(() => {
            this.$forceUpdate()
        })
    },    

注: 如果数据少,可以通过 this.$set(this.form, 'dsRuleIds',dsRuleIds); 进行重新渲染 也可以

<el-select v-model="form.guarderType" placeholder="请选择护线员类型" clearable>
    <el-option
               v-for="dict in guarderTypeOptions"
               :key="dict.dictValue"
               :label="dict.dictLabel"
               :value="dict.dictValue"
               @click.native="refreshData('guarderType')"
               />
</el-select>
refreshData (field) {
    this.$refs.form.clearValidate(field)
    this.$nextTick(() => {
    	this.$forceUpdate()
    })
},

非空验证失效

明明已经输入数据/选择数据,但是非空校验还是无法通过

大概率是页面存在赋值导致的,在vue2中使用$set 进行赋值

 this.$refs.form.clearValidate(field)

4. Dropdown 下拉菜单 点击事件

<el-dropdown trigger="click" >
        <span class="el-dropdown-link">
          {{checkItem.name}}<i class="el-icon-arrow-down el-icon--right" style="color:#00FFFD;margin-left:11px" ></i>
        </span>
        <el-dropdown-menu slot="dropdown" >
          <el-dropdown-item v-for="(item, index) in list" @click.native="handleCommand(item)" :key="index">{{  item.name }}</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>

5. elementui表格固定列之后行错位解决

// 针对 通过fixed 固定 按钮列,数据加载时导致 错位问题解决
updateDom(){
    this.$nextTick(() => {
        this.$refs.tableRef.doLayout();
    });
},

6. 复选框指定value

<el-checkbox-group v-model="proStatusList">
    <li><el-checkbox label="1">未开始</el-checkbox> </li>
    <li><el-checkbox label="2" >建设中</el-checkbox> </li>
    <li><el-checkbox label="3">已完成</el-checkbox> </li>
</el-checkbox-group>

7. el-tree

el-tree默认选中并高亮open in new window

default-expanded-keysdefault-checked-keys 区分开 主要是 考虑,在没用默认选中下,可以指定打开需要 的父节点

场景: 人员选择,如果如果没用默认选中,则 打开当前所在部门的 节点 (人员组织树)

<el-tree ref="treeRef" class="elTree" :data="treeData" :props="defaultProps" :expand-on-click-node="false"
         :show-checkbox="checkbox" // 是否多选
         :default-expand-all="expandAll"  //是否默认展开所有节点	
         node-key="id" //搭配 default-expanded-keys和 default-checked-keys 指定 唯一标识
         :highlight-current="true" // 是否高亮选中,不指定 再单选场景下,无法正常显示默认选中的节点
         :default-expanded-keys="expandedTreeKeys"  /// 默认需要展开的节点
         :default-checked-keys="defaultTreeKeys" // 默认需要 选中的节点
         @node-click="handleNodeClick"/>
/** 查询部门下拉树结构 */
            getTreeselect () {
                userTreeData({}).then(response => {
                  	this.treeDataAll = response.data; // 针对后期会进行搜索,所以将 初始查询的记录备份
                    this.treeData = JSON.parse(JSON.stringify(this.treeDataAll));
                    this.$nextTick(() => {
                        this.initZteeData();
                    })
                })
            },
 initZteeData() {
                if (this.selectNodeId && this.treeData.length > 0) { //  说明需要默认选中
                    this.defaultTreeKeys = this.selectNodeId.split(',');
                    this.$refs.treeRef.setCurrentKey(this.defaultTreeKeys)
                }
            },
  • 完整
<template>
    <CustomPopup ref="custompopup" title="人员选择" :notFooter="notFooter" :pop_width="pop_width"
                 @sonHandleCallback="sonHandleCallback">
        <template #content>
            <div class="whp100 tree">
                <!-- 头部搜索-->
                <div class="head-search">
                    <el-row>
                        <el-col :span="18">
                            <el-input placeholder="请输入内容" prefix-icon="el-icon-search" v-model="inputSearch"
                                      clearable></el-input>
                        </el-col>
                        <el-col :span="6">
                            <div class="df_center">
                                <el-tooltip class="item" effect="dark" content="清空" placement="top-start">
                                    <el-button type="danger" icon="el-icon-delete" circle @click="clean"></el-button>
                                </el-tooltip>
                                <el-tooltip class="item" effect="dark" content="重置" placement="top-start">
                                    <el-button type="success" icon="el-icon-refresh-right" circle
                                               @click="reset"></el-button>
                                </el-tooltip>
                            </div>

                        </el-col>
                    </el-row>
                </div>
                <!--树结构-->
                <div class="tree-content">
                    <el-tree ref="treeRef" class="elTree" :data="treeData" :props="defaultProps"
                             :expand-on-click-node="false"
                             :show-checkbox="checkbox" :default-expand-all="expandAll" node-key="id"
                             :highlight-current="true"
                             :default-expanded-keys="expandedTreeKeys" :default-checked-keys="defaultTreeKeys"
                             :filter-node-method="filterNode"
                             @node-click="handleNodeClick"/>
                </div>
            </div>

        </template>
    </CustomPopup>
</template>

<script>
    import CustomPopup from '@/components/custom-popup/CustomPopup.vue'
    import { userTreeData } from '@/api/system/user'

    export default {
        /**
         * 说明: 人员选中器
         */
        name: 'userSelector',
        components: { CustomPopup },
        props: {
            cancleTitle: { type: String, default: '取消', },
            sureTitle: { type: String, default: '确定', },
            title: { type: String, default: '弹窗', },
            handleType: {/* 考虑 弹窗,除了 总共 3个情况,确定,取消,刷新 */
                type: String,
                default: 'cancel',
            },
            notFooter: {/*是否显示底部按钮 ,默认显示底部按钮 如果不显示 则 传递 notFooter=not_footer*/
                type: String,
                default: '',
            },
            pop_width: {/* 支持自定义窗口尺寸 */
                type: String,
                default: ' 670px',
            },
            checkbox: {/* 是否 多选,默认 单选 */
                type: Boolean,
                default: false,
            },
            selectNodeId: {/* 默认选择 */
                type: String,
                default: ''
            },
        },
        data () {
            return {
                expandAll: false, // 树组件是否默认展开
                inputSearch: '',
                defaultTreeKeys: [],
                expandedTreeKeys: [],
                // 部门树选项
                treeData: [],
                defaultProps: { children: 'children', label: 'label' },
            }
        },
        // 初始化页面完成后
        mounted () {

        },
        created () {
            this.loadData()
        },
        methods: {
            loadData () {
                this.getTreeselect()
            },
            /** 查询部门下拉树结构 */
            getTreeselect () {
                userTreeData({}).then(response => {
                    // 构造父节点
                    const userData = {
                        id: 0,
                        nodeId: 'all_0',// 唯一标识
                        pid: -1,
                        label: '全部', //名称
                        type: '0', //层级 1、部门 2、人员
                        children: response.data
                    }
                    this.treeData = [userData]
                    this.$nextTick(() => {
                        this.initZteeData()
                    })
                })
            },
            initZteeData () {
                if (this.selectNodeId && this.treeData.length > 0) { //  说明需要默认选中
                    this.defaultTreeKeys = this.selectNodeId.split(',') //  默认选中 的节点
                    this.expandedTreeKeys = this.defaultTreeKeys// 需要展开的 节点
                } else { // 没用默认选中 则 展示 当前所在部门的 节点
                    const companyId = this.$store.state.user.companyId
                    this.expandedTreeKeys = companyId.toString().split(',')
                    this.defaultTreeKeys=[];
                }
                this.$refs.treeRef.setCurrentKey(this.expandedTreeKeys)
            },
            // 搜索 实现 模糊匹配
            filterNode (value, data) {
                if (!value) return true
                return data.label.indexOf(value) !== -1
            },
            // 节点单击事件 -- 针对 单选  直接关闭弹窗
            handleNodeClick (data) {
                console.log(data)
            },
            sonHandleCallback (item) {
                let ids = []
                let names = []
                if (item.type == 'submit') { // 提交
                    const checkedNodes = this.$refs.treeRef.getCheckedNodes(true, true) // 去掉 父节点
                    this.resultProcessing(checkedNodes)
                } else {
                    this.$emit('sonHandleCallback', item)
                }
            },
            /**
             * 返回值处理
             *  客户需求:如果部门是全选的,则 展示的时候 不显示具体的一个个人名称,而以 所在展示所在部门
             *  例如:钢结构协会(12人)-全选   +  测试部门(3人)-西西A 则返回值 为 “钢结构协会,西西A”
             */
            resultProcessing (checkedNodes) {
                const groupMap = this.dataGroup(checkedNodes) // 人员按照 部门分组
                const treeData = JSON.parse(JSON.stringify(this.treeData[0].children))// 取出所有部门
                let idArr = []
                let nameArr = []
                let labelArr = []
                for (var pid in groupMap) {
                    var item = groupMap[pid]
                    const p_node = this.nodeCheck(treeData, pid)
                    if (item.ids.length == p_node.children.length) { // 说明当前部门全选了
                        labelArr.push(p_node.label)
                    } else {
                        labelArr.push(...item.names)
                    }
                    idArr.push(...item.ids)
                    nameArr.push(...item.names)
                }
                const itemObj = {
                    type: 'getUserTreeData',
                    item: {
                        ids: idArr.join(','),
                        names: nameArr.join(','),
                        labels: labelArr.join(',')
                    }
                }
                this.$emit('sonHandleCallback', itemObj)
            },
            /**
             * 人员 分组
             *  为了校验 每个部门人员是否全选,需要对选中的数据进行分组(按照 部门)
             * @param checkedNodes: 选中的节点 数组
             */
            dataGroup (checkedNodes) {
                let groupMap = {}
                checkedNodes.forEach(item => {
                    const key = item.pid
                    if (!(key in groupMap)) {
                        // groupMap[item.pid] = { pid: key, list: [] }
                        groupMap[item.pid] = { pid: key, ids: [], names: [] }
                    }
                    // groupMap[key].list.push(item)
                    groupMap[key].ids.push(item.id)
                    groupMap[key].names.push(item.label)
                })
                return groupMap
            },
            // 验证 部门 是否全选
            nodeCheck (data, pid) {
                let p_node = {}
                data.some(item => {
                    if (item.nodeId == pid) {
                        p_node = item
                        return true
                    }
                })
                return p_node;
            },
            // 清除
            clean () {
                this.$refs.treeRef.setCheckedKeys([])
            },
            // 重置
            reset () {
                this.$refs.treeRef.setCheckedKeys(this.selectNodeId.split(','));
            },
        },
        watch: {
            // 通过监听 ,触发 filter-node-method
            inputSearch (val) {
                this.$refs.treeRef.filter(val)
            }
        },

    }
</script>

<style lang="scss" scoped>
    .head-search {
        height: 50px;
        width: 100%;
    }

    .tree-content {
        height: calc(100% - 50px); /* 减去 head-search 高度*/
        overflow: auto;
    }
</style>
  • 调用
   
<!--弹窗,显示 详情-->
<userSelector v-if="popupshow" :selectNodeId="form.userIds" ref="userSelectorRef" title="人员选择" @sonHandleCallback="sonHandleCallback" :checkbox="true"    pop_width="450px" />
   
   selectUser(){
          this.popupshow = true;
      },
      sonHandleCallback(data){
          console.log(data)
          this.popupshow = false;// 关闭弹窗
          if(data.type=='close'){
              //this.popupshow = false;// 关闭弹窗
          }else if(data.type=='getUserTreeData'){
              this.form.userIds = data.item.ids;
              this.form.userLabel = data.item.labels;
          }
      }

el-tree 默认选中失效

:default-checked-keys="defaultCheckedKeys" 通过这个属性可能有时会失效

 this.$nextTick(() => {
        this.$refs.tree.setCheckedKeys(checkIds);
      });
  • demo
<template>
  <div class="tag-tree-wrap">
    <div class="tag-tree-search flex-center">
      <el-input
        class="tag-tree-input"
        clearable
        placeholder="请输入分类名称检索"
        v-model="inputSearch"
        suffix-icon="el-icon-search"
        size="mini"
      />
    </div>
    <el-tree
      :data="treeOptions"
      :expand-on-click-node="false"
      :filter-node-method="filterNode"
      ref="tree"
      :default-expand-all="expandAll"
      node-key="nodeId"
      :default-checked-keys="defaultCheckedKeys"
      :show-checkbox="checkbox"
      @node-click="handleNodeClick"
      @check="handleCheck"
      class="tag-tree"
      ><!--   :props="defaultProps" -->
      <div class="custom-tree-node" slot-scope="{ node, data }">
        <span class="flex-row">
          <i class="iconfont" v-if="data.picUrl" :class="'icon-' + data.picUrl"></i>
          <span style="font-size: 14px">{{ node.label }}</span>
        </span>
      </div>
    </el-tree>
  </div>
</template>

<script>
/* tagTree*/
import { getTreeLabelList } from "@/api/sample/aiTagCategory";

export default {
  name: "tagTree",
  components: {},
  props: {
    type: {
      type: [String, Number],
      default: "1", // 1:标注标签,2:数据标签
    },
    expandAll: {
      type: Boolean,
      default: true,
    },
    value: {
      // 这里的 value 就是我们自定义的
      type: [String, Array],
      default: "",
    },
  },
  data() {
    return {
      inputSearch: "",
      treeOptions: [],

      checkbox: true,
      checkbox_check: false, // 复选模式下 选中数据
      nodeList: [], // 选中
      defaultCheckedKeys: [], // 默认选中
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },
  // 初始化页面完成后
  mounted() {},
  created() {
    this.loadData();
  },
  watch: {
    inputSearch(val) {
      this.$refs.tree.filter(val);
    },
    nodeList: {
      deep: true,
      handler(val) {
        this.$emit("updateValue", this.nodeList);
      },
    },
  },
  emits: ["updateValue"],
  methods: {
    async loadData() {
      // 获取树的选选项
      getTreeLabelList({
        type: this.type,
      }).then((result) => {
        if (result.code == 0) {
          this.treeOptions = result.data;
        } else {
          this.msgError(result.msg);
        }
      });
    },

    // updateNodeListByData(data) {
    //   data.forEach((item) => {
    //     if (this.defaultCheckedKeys.indexOf(item) < 0) {
    //       this.defaultCheckedKeys.push(item);
    //     } else {
    //       this.defaultCheckedKeys.splice(this.defaultCheckedKeys.indexOf(item), 1);
    //     }
    //   });
    //
    //   const reslut = this.flattenTree(this.treeOptions);
    //   const arr = reslut.filter((item) => data.indexOf(item.nodeId) >= 0);
    //
    //   this.nodeList = [...new Set([...arr], [...this.nodeList])];
    //
    //   this.$refs.tree.setCheckedNodes([...this.nodeList]);
    // },

    flattenTree(tree, parent = {}, result = []) {
      tree.forEach((node) => {
        const newNode = { ...parent, ...node };
        delete newNode.children;
        result.push(newNode);
        if (node.children) {
          this.flattenTree(node.children, newNode, result);
        }
      });
      return result;
    },

    // 默认选中
    defaultSelect(checkIds = []) {
      console.log(checkIds)
      // 移除setTimeout,直接设置
      // this.defaultCheckedKeys = checkIds; //checkIds.map(id => 'data:'+ id);
      // // 手动触发树的选择状态更新
      this.$nextTick(() => {
        this.$refs.tree.setCheckedKeys(checkIds);
      });
    },
    filterNode(value, data) {
      if (!value) return true;
      const name = data.name || data.label;
      return name.indexOf(value) !== -1;
    },

    // 递归的方式,目前暂时不用这样实现
    // updateSelectedData() {
    //   const checkedKeys = this.$refs.tree.getCheckedKeys(true); // 获取所有选中节点(包括半选)
    //   this.selectedData = this.collectSelectedNodes(this.treeOptions, checkedKeys);
    //   console.log(this.selectedData)
    // },
    // collectSelectedNodes(data, checkedKeys) {
    //   let result = [];
    //   function traverse(nodes) {
    //     nodes.forEach(node => {
    //       if (checkedKeys.includes(node.id)) {
    //         if (node.typeTree === "data") {
    //           // 只收集 typeTree 为 "data" 的节点
    //           result.push({ ...node });
    //         }
    //       }
    //       if (node.children && node.children.length) {
    //         traverse(node.children);
    //       }
    //     });
    //   }
    //   traverse(data);
    //   return result;
    // },

    // 节点单击事件  目前没实现
    handleNodeClick(data) {
      if (this.checkbox) {
        // 复选不走单击事件,否则增加页面处理逻辑
        return;
      }
      this.nodeList = [];
      this.nodeList.push(data);
      this.sendParent();
    },
    /**
     * 针对复选框、
     * 确认
     */
    affirmTree() {
      this.nodeList = this.getCheckNodes();
      this.sendParent();
    },
    /**
     *  针对复选框
     * 点击复选框,验证是否 选择 节点,从而决定是否显示底部确认按钮
     * 当复选框被点击的时候触发
     * @param
     */
    handleCheck() {
      this.affirmTree();
    },
    /**
     * 过滤出需要的子节点,目前只过滤出 typeTree 为 "data" 的节点
     * @returns {D[]}
     */
    getCheckNodes() {
      return this.$refs.tree.getCheckedNodes().filter((node) => node.typeTree === "data");
    },
    /**
     * 回调:点击树回调
     *  目前主要是2个类型 :refresh 刷新
     *                    search:执行搜素
     * @param type
     */
    sendParent() {
      this.$emit("sonHandleCallback", this.nodeList); // 调用父页面刷新
    },
  },
};
</script>

<style scoped lang="scss">
.tag-tree-wrap {
  height: 350px;
  max-height: 30%;
  overflow: hidden;
  .tag-tree-search {
    width: 100%;
    height: 50px;
    .tag-tree-input {
      width: 100%;
    }
  }
  .tag-tree {
    height: calc(100% - 50px);
    overflow-y: auto;
  }
}
</style>

  • 调用
<template>           
<el-popover popper-class="popoverBorder filterPopoverClass" placement="bottom"
            width="300" trigger="click" :visible-arrow="false"  >
    <tag-tree type="2" ref="tagDataTreeRef" @sonHandleCallback="selectDataTag" />
    <i class="iconfont icon-shujubiaoqian1"  slot="reference" @click="addDataTagTree" v-if="isDataTagDisabled"></i>
    </el-popover>
</template>
<script>
    export default {
        computed: {
            // 控制 数据标签的添加。目前只有在 标注模式和审批模式下才有
            isDataTagDisabled(){
                return ['1','2'].indexOf(this.modeType) !== -1; // 说明不在编辑模式下,禁止操作
            }
        },
        methods: {
            // 数据标签回调
            selectDataTag(data){
                this.dataList = data;
            },
            // 数据标签 弹窗
            addDataTagTree(){
                console.log(this.dataList)
                this.$refs.tagDataTreeRef.defaultSelect(this.dataList.map(item=>item.id));
            },
        }
    }


</script>

elementUI的el-tree添加title属性

<el-tree ref="myTree" :data="treeData"  @node-click="handleNodeClick">
    <span slot-scope="{ node }" :title="node.label" class="long_omit" style="width: 100%;">
        <span class="long_omit" > {{ node.label }} </span>
    </span>
</el-tree>

自定义图标

说明: 无法直接使用node

<el-tree ref="myTree" :data="treeData"  @node-click="handleNodeClick">
    <span slot-scope="{ node,data }" :title="node.label" class="long_omit" style="width: 100%;">
        <span class="long_omit df_ac ">
            <img class="tree-icon" src="../../../../assets/imgs/gc.png" alt="" v-if="data.type == '01'" />
            <img class="tree-icon" src="../../../../assets/imgs/gjg.png" alt="" v-if="data.type == '02'" />
            {{ node.label }}
        </span>
    </span>
</el-tree>

复选功能开发


下拉内容弹窗在最上面,出现遮挡了

需要指定:popper-append-to-body="false"

<el-select ref="elSelect" :popper-append-to-body="false" class="table-search-select" v-model="form.sex" placeholder="请选择性别" clearable 
           filterable>
    <el-option v-for="dict in sexOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue" 
               @click.native="checkSexItem(dict)"></el-option>
</el-select>

8. el-selsct下拉菜单 样式无法修改

大概率是 挂在的节点body,导致 使用::v-deep/deep/ 样式都不生效

解决: 通过设置:popper-append-to-body="false"字段,使el-select挂载在当前页面,即可进行选中修改样式。

<el-form-item label="类型">
    <el-select v-model="form.type" size="mini" :popper-append-to-body="false" placeholder="请选择" @change="getDate($event)">
    	<el-option v-for="item in type" :key="item.value" :label="item.label" :value="item.value"> </el-option>
	</el-select>
</el-form-item>

9. 赋值后无法修改

存在一种场景,使用了this.$set 还是无法生效,则可以试试,在form 赋值的时候,对该字段进行赋默认值(后端查询的时候没有该字段,需要默认赋值,从而data才能监听)

this.$set(this.form, towerName, '需要赋值的值')

10. 走马灯 手动切换

image-20230216152015435
image-20230216152015435
<el-select class="input-search select pa" v-model="checkItemId" :popper-append-to-body="false">
            <el-option v-for="(item,index) in porjOptions" :key="item.itemsId" :label="item.itemsName"
                       :value="item.itemsId" @click.native="selectChange(index)"></el-option>
        </el-select>
        <!-- 曲线 -->
        <div class="mapTBox-container">
            <div class="containerT-back" v-if="loadingData">
                <el-carousel ref="carouselRef" trigger="click" style="width: 100%; height: 100%" height="100%" loop :interval="8000"
                             :initial-index="checkChartIndex"  @setActiveItem="setActiveItem" @change="changeItem">
                    <el-carousel-item class="swiper" v-for="(item, indexx) in porjOptions" :key="'charItem'+indexx" :name="indexx+''" >
                        <div class="chart">
                            <iframe v-show="item.url" id="iframe" ref="iframe" :src="item.url" frameborder="0"
                                    width="100%" height="100%" scrolling="no"></iframe>
                        </div>
                    </el-carousel-item>
                </el-carousel>
            </div>
        </div>
selectChange (index) {
    this.checkChartIndex = index;
    this.setActiveItem(index);
},
setActiveItem (index) {
    console.log('setActiveItem')
    this.$refs.carouselRef.setActiveItem(index);
},
changeItem (index) {
    console.log('changeItem')
    this.checkChartIndex = index;
},

15. table

右侧按钮列固定不生效

需要给 <el-table-column width="180"> 指定宽度

表格换行

.table .table-content{
  .el-table{
    /*内容区域超长换行*/
    .cell {
      white-space: nowrap;
    }
    /*表格头部 不换行*/
    thead .cell{
      white-space: normal;
    }
}

开启提示

<el-table-column label="处理说明" align="center" :show-overflow-tooltip="true" prop="dealRemarks" />

表格按钮栏,宽度自适应

再混入中写,支持全局

export default {
    data () {
        return {
            leftBtnWidth: '120' // 右侧 按钮栏固定,计算宽度
        }
    },
    mounted () {
        this.$bus.$on('resetQuery', name => {
                if (name === this.$options.name) {
                    try {
                        this.resetQuery()
                    } catch (e) {
                    }
                    try {
                        this.resetAllQuery()
                    } catch (e) {
                    }
                }
            }
        )
    },
    methods: {
       
         /**
         * 右侧 按钮栏 宽度 自适应
         * 1. 需要指定 <el-table ref="tableBox1" />
         * 2. 数据加载完成后,调用该方法
         * 3. <el-table-column label="操作" fixed="right" align="center" class-name="handle-column small-padding fixed-width" :width="leftBtnWidth" >
         */
        setLeftBtnAutoWidth () {
            this.$nextTick(() => {
                const tds = document.querySelectorAll('td.handle-column>.cell')
                if (tds && tds.length) {
                    this.leftBtnWidth = Math.max(...[...tds].map(v => {
                        //console.log(v);
                        let opDiv = v.children[0]
                        if (opDiv.children && opDiv.children.length > 0) {
                            let width = 0
                            let btns = opDiv.children
                            for (var i = 0; i < btns.length; i++) {
                                width = width + btns[i].offsetWidth + 15
                            }
                            return width
                        }
                        return 120
                    }))
                    this.$refs.tableBox1.doLayout()
                }
            })
        },
    }
}

<el-table v-loading="loading" :data="InfoList"
                      header-cell-class-name="table-header-cell" style="width: 100%;" height="100%"
                      @selection-change="handleSelectionChange" ref="tableBox1"
                      cell-class-name="table-cell-class" :row-key="(row) => { return row.id }">
                <el-table-column type="selection" width="55" align="center" :selectable="showUpdateBtn"/>
             
                <el-table-column label="序号" width="70" type="index" align="center"/>
                <el-table-column label="设备类型" prop="typeName" width="180" :show-overflow-tooltip="true" align="center"/>
                <el-table-column label="出厂编号" prop="materialCode" width="180" :show-overflow-tooltip="true" align="center"/>
       
                <el-table-column label="标定文件" prop="certificateFileName" align="center" width="100" :show-overflow-tooltip="true" />
                <el-table-column label="设备状态" :show-overflow-tooltip="true" align="center" width="100">
                    <template slot-scope="scope">
                        <span class="state-tag" :class="'effeState_'+scope.row.effeState">
                            {{scope.row.effeStateName}}
                        </span>
                    </template>
                </el-table-column>
                <el-table-column label="操作" fixed="right" align="center" 
                                 class-name="handle-column small-padding fixed-width" :width="leftBtnWidth" >
                    <template slot-scope="scope">
                        <div class="operating-button">
                            <el-button type="text" class="cz-btn" icon="el-icon-document"
                                       @click="showInfo(scope.row)"> 查看
                            </el-button>
                            <!-- 修改限制: 从未检定过的设备且不在审核中 || 超期且不在审核中的设备  -->
                            <el-button type="text" class="cz-btn" icon="el-icon-edit" @click="updateInfo(scope.row)"
                                       v-if="showUpdateBtn(scope.row)"> 修改
                            </el-button>
                            <!-- 暂存的设备可以删除  -->
                            <el-button type="text" class="cz-btn" icon="el-icon-delete"
                                       v-if="scope.row.effeState == '00' && scope.row.shState != '1'" @click="handleDelete(scope.row)"> 删除
                            </el-button>
                        </div>

                    </template>
                </el-table-column>
            </el-table>

复选

<el-table ref="tableBox1" style="width: 100%" height="100%" header-cell-class-name="table-header-cell"
                class="table-content-text" v-loading="loading" :data="dataList" :row-key="getRowKey"
                    @select="rowSelection" @select-all="selectAll">
                    </el-table>
        // 获取数据
        getList() {
            this.loading = true;
            listJcSzclRecord(this.queryParams).then(res => {
              this.dataList = res.rows;
                this.$nextTick(() => {
                  this.dataList.forEach(item => {
                      item.szclRecordId = item.id
                      delete item.id;
                      this.initCheckItem(item)
                  })
                });
                this.total = res.total;
                this.tableRerendering();
                this.loading = false;
              console.log(this.dataList)
            })
        },
      initCheckItem(item){
        if(this.parentIds.includes(item.szclRecordId)){
          item.check = true;
          this.$refs.tableBox1.toggleRowSelection(item, true);
          this.selectData.push(item);
        }else{
          item.check = false;
        }
      },
      
      
              // 翻页勾选
        getRowKey(row){
            return row.szclRecordId;
        },
        // 手动勾选数据行
        rowSelection(selection, row){
          row.check = !row.check;
          if(row.check){
            //todo选中项若在未选中项集合内,则删除
            this.parentIds.push(row.szclRecordId);
            this.selectData.push(row)
          }else {
            //todo未选中项不在未选中项集合内,则添加
            const indexx = this.parentIds.findIndex(id=>row.szclRecordId==id)
            if (indexx!=-1) {
              this.parentIds.splice(indexx,1)
              this.selectData.splice(indexx,1)
            }
          }

        },
        // 手动勾选全选
        selectAll(selection){
          if(selection.length > 0) {//全选,从未勾选集合里删除
            selection.forEach(item => {
              if(this.parentIds.includes(item.szclRecordId)){//todo在未选中集合里,删除
                this.parentIds.push(item.szclRecordId);
                this.selectData.push(item)
              }
            });
          }else{//todo取消全选,添加到未勾选集合
            this.dataList.forEach(item => {
              const indexx = this.parentIds.findIndex(id=>item.szclRecordId==id)
              if (indexx!=-1) {
                this.parentIds.splice(indexx,1)
                this.selectData.splice(indexx,1)
              }
            });
          }
        },

16. 表单设置 :inline="true" 会导致布局 尺寸无法铺满

<el-row>
   <el-col :span="6">xxxxx  </el-col>
</el-row>
1677469808383
1677469808383
    .el-select, .el-input {
        width: 100%!important;
    }
    .el-form-item {
        margin-bottom: 30px;
        margin-right: 30px;
    }

17. select 下拉选定义click事件

 <el-select v-model="form.contactsList" multiple placeholder="请选择联系人" >
                                    <el-option v-for="item in userOptions" :key="item.userId" :label="item.userName" :value="item.userId" @click.native="checkUser(item)">
                                    </el-option>
                                </el-select>

18. 下拉选多选 记录id和name

<!-- 初始化 form.jcUserIdArr=[] -->
<el-form-item label="检测人员" prop="jcUserIdArr">
    <el-select v-model="form.jcUserIdArr" placeholder="请选择检测人员" clearable filterable multiple
               style="width:100%">
        <el-option v-for="item in personList" :key="item.id"
                   :label="item.name" :value="item.id"></el-option>
        <!--@click.native="checkTower(item)"-->
    </el-select>
</el-form-item>

数据回显

initUserSelect () {
    var jcUserIdArr = []
    if (this.form.jcUserIds) {
        this.form.jcUserIds.split(',').forEach(item => {
            jcUserIdArr.push(Number(item))
        })
    }
    this.$set(this.form, "jcUserIdArr", jcUserIdArr);
},

保存时调用

// 保存时 拿到 人员名称
getUserName (ids) {
    // vID 是一个数组,循环获取name,根据id
    if (!ids && ids.length === 0) {
        return
    }//这里多选的时候获取name和单选不一样 单选是对象 多选是数组所以...
    let nameArr = []
    ids.forEach(id => {
        let obj = this.personList.find((item) => {
            return item.id === id
        })
        if (obj) {
            nameArr.push(obj.name)
        }
    })
    this.form.jcUserNames = nameArr.join();
    this.form.jcUserIds = this.form.jcUserIdArr.join();
},

自定义弹出的头部样式

<el-dialog :visible.sync="dialogVisible">
       <div slot="title" class="header-title" :style="{'background': theme.pageTitleBgColor, 'color': 'white'}">
           <div style="padding:15px 20px;">编辑框</div>
       </div>
          ......

身份证校验

  data () {
    let idCardValidity = (rule, code, callback) => {
      var city = { 11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古", 21: "辽宁", 22: "吉林", 23: "黑龙江 ", 31: "上海", 32: "江苏", 33: "浙江", 34: "安徽", 35: "福建", 36: "江西", 37: "山东", 41: "河南", 42: "湖北 ", 43: "湖南", 44: "广东", 45: "广西", 46: "海南", 50: "重庆", 51: "四川", 52: "贵州", 53: "云南", 54: "西藏 ", 61: "陕西", 62: "甘肃", 63: "青海", 64: "宁夏", 65: "新疆", 71: "台湾", 81: "香港", 82: "澳门", 91: "国外 " };
      var tip = ""
      var pass = true
 
      if (!code || !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i.test(code)) {
        tip = "身份证号格式错误"
        pass = false;
      } else if (!city[code.substr(0, 2)]) {
        tip = "地址编码错误"
        pass = false
      } else {
        // 18位身份证需要验证最后一位校验位
        if (code.length === 18) {
          code = code.split('')
          // ∑(ai×Wi)(mod 11)
          // 加权因子
          var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
          // 校验位
          var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]
          var sum = 0
          var ai = 0
          var wi = 0
          for (var i = 0; i < 17; i++) {
            ai = code[i]
            wi = factor[i]
            sum += ai * wi
          }
          var last = parity[sum % 11];
          if (parity[sum % 11] != code[17]) {
            tip = "校验位错误"
            pass = false
          }
        }
      }
      if (!pass) {
        callback(new Error(tip))
      } else {
        callback()
      }
      // if (!pass) alert(tip)
      // return pass
    }
    return {
     
      personInfo: {
        idCard: "",
      },
      rules: {
        idCard: [{ required: true, message: '请输入证件号', trigger: 'blur' },
          {
            pattern: /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
            message: '请输入正确的证件号'
          },
          { validator: idCardValidity, trigger: 'blur' }],
      }
    }
  },

确认框

this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.$message({
            type: 'success',
            message: '删除成功!'
          });
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });          
        });

el-select

<el-option v-for="item in porjOptions" :key="item.itemsId" :label="item.itemsName" :value="item.indexx" ></el-option>

下拉选明明有数据,但是提示 item.itemsId 不存在,大概率是因为组装的数据不对,例如:数据出现一项是 undefined

同时 例如 数据 for循环的数组 用 类似 arr[index].child 这样的数据格式都可能出现,这样的警告

el-table

1) 自定义class

<template>
  <el-table :data="tableData" :table-class-name="tableClassName">
    <!-- 表格列定义 -->
  </el-table>
</template>

computed: {
  tableClassName() {
    if (this.isDarkMode) {
      return 'dark-table';
    } else {
      return 'light-table';
    }
  }
}
    /deep/.dark-table {
        color: rgb(238, 118, 13);
    }

el-date-picker 选择开始和结束时间

<el-date-picker v-model="createTimeValue" type="daterange" value-format="yyyy-MM-dd" range-separator="" start-placeholder="开始日期"
                end-placeholder="结束日期" @change="startAndEnd">
</el-date-picker>
if (this.createTimeValue && this.createTimeValue.length > 0) {
    this.queryParams.createTimeBegin = this.createTimeValue[0]
    this.queryParams.createTimeEnd = this.createTimeValue[1]
} else {
    this.queryParams.createTimeBegin = undefined
    this.queryParams.createTimeEnd = undefined
}
<if test="signInTimeStart != null  and signInTimeStart != ''">
    and  date_format(a.sign_in_time,'%Y-%m-%d')  <![CDATA[ >= ]]>date_format(#{signInTimeStart},'%Y-%m-%d')
</if>
<if test="signInTimeEnd != null and signInTimeEnd != '' ">
    and    date_format(a.sign_in_time,'%Y-%m-%d')  <![CDATA[ <= ]]> date_format(#{signInTimeEnd},'%Y-%m-%d')
</if>

table 复选框

  <el-table style="width: 100%" height="100%" header-cell-class-name="table-header-cell"
                      class="table-content-text" :row-class-name="tableRowClassName"
                      @selection-change="handleSelectionChange" ref="multipleTable"
                      v-loading="loading" :data="dataList"> <!--@row-click="showDetails"-->
                <el-table-column type="selection" width="55" align="center" v-if="isCheck" :selectable="isSelectable"/>
      
       </el-table>
            // 添加自定义class   是否异常  1:正常;2:可疑数据;3:不合格 对数据打标签
            tableRowClassName ({ row, rowIndex }) {
                return 'warnFlag-' + row.warnFlag
            },
            // 多选框选中数据
            handleSelectionChange (selection) {
                console.log(selection)
                this.ids = selection.map(item => item.id)
                this.warnMsgArr = selection.map(item => item.warnMsgArr)
                this.warnMsgArr = Array.from(new Set(selection
                    .filter(item => item.warnMsg)
                    .map(item => item.warnMsg))
                )
                this.single = selection.length != 1
                this.multiple = !selection.length
            },
            
          // 控制是否给操作复选框      是否异常,1:正常;2:可疑数据;3:不合格
            isSelectable (row) {
                return row.warnFlag == 2
            },

单选

<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%"
      @row-click="singleElection"
      highlight-current-row
    >
      <el-table-column align="center" width="55" label="选择">
        <template slot-scope="scope">
          <!-- 可以手动的修改label的值,从而控制选择哪一项 -->
          <el-radio class="radio" v-model="templateSelection" :label="scope.row.id"
            >&nbsp;</el-radio
          >
        </template>
      </el-table-column>
      <el-table-column align="center" prop="id" label="编号" width="80"> </el-table-column>
      <el-table-column align="center" prop="date" label="日期" width="150"> </el-table-column>
      <el-table-column align="center" prop="name" label="姓名" width="80"> </el-table-column>
      <el-table-column align="center" prop="address" label="地址" width="250"></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [
        {
          id: "0001",
          date: "2016-05-02",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1518 弄",
        },
        {
          id: "0002",
          date: "2016-05-04",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1517 弄",
        },
        {
          id: "0003",
          date: "2016-05-01",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1519 弄",
        },
        {
          id: "0004",
          date: "2016-05-03",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1516 弄",
        },
      ],
      //   当前选择的行的id
      templateSelection: "",
      //   当前选择的行的数据
      checkList: [],
    }
  },
  methods: {
    singleElection(row) {
      this.templateSelection = row.id
      this.checkList = this.tableData.filter((item) => item.id === row.id)
      console.log(`该行的编号为${row.id}`)
    },
  },
}
</script>


el-image列表图片预览可通过 previewSrcList 开启预览大图的功能

https://blog.csdn.net/m0_60459392/article/details/130522977open in new window

<el-image class="img-list-item" :src="item.url"  :zoom-rate="1.2" :preview-src-list="filteredProducts" fit="cover"/>
        computed: {
            // 拿到 当前 任务id下的 激光点云图片
            computedImgs () {
                return this.dyImgArr
            },
            // 构造 激光点云 图片 集合,给放大用
            filteredProducts () {
                return this.dyImgArr.map(item => item.url)
            }
        },

按钮操作二次确认

popover

桩基实验删除 zjjcTestJzList

<el-tooltip class="item confirm-btn" effect="dark" content="删除" placement="top" v-if="isShowDele(scope.row)">
    <el-popover placement="top" width="160"  v-model="visible">
        <p>确定删除吗?</p>
        <div style="text-align: right; margin: 0">
            <el-button size="mini" type="text" @click.stop="visible = false">取消</el-button>
            <el-button type="primary" size="mini" @click.stop="deleteItem(scope.row)">确定</el-button>
        </div>
        <el-button slot="reference" class="cz-btn" type="text" @click.stop
                   icon="el-icon-delete"></el-button>
    </el-popover>
</el-tooltip>

popconfirm

确认回调 onConfirm & confirm

目前不确定那个版本用的是onConfirm

element-u@2.15.6 版本用的是 confirm

image-20230706133443053
image-20230706133443053
<el-popconfirm confirm-button-text='确定' cancel-button-text='取消' icon="el-icon-info" icon-color="red" :key="scope.$index"
               :ref="'popconfirm' + scope.$index"
               title="是否确认修改?"  v-if="scope.row.status==1 || scope.row.status==2" class="list-btn"
               @onConfirm="lose(scope.row)">
    <el-button slot="reference"  type="text" @click.stop="closePopover(scope.$index)" icon="el-icon-edit">删除</el-button>
</el-popconfirm>

// 弹出时先关闭其他Popover
closePopover (index) {
    this.zjjcPersonList.forEach((item, indexx) => {
        if (indexx != index && this.$refs[`popconfirm${indexx}`]) {
            this.$refs[`popconfirm${indexx}`].cancel();
        }
    })
}

确认对话框

https://element.eleme.cn/#/zh-CN/component/message-boxopen in new window

 this.$alert('<strong>这是 <i>HTML</i> 片段</strong>', 'HTML 片段', {
          dangerouslyUseHTMLString: true
        });
this.$confirm('检测到未保存的内容,是否在离开页面前保存修改?', '确认信息', {
    distinguishCancelAndClose: true,
    confirmButtonText: '保存',
    cancelButtonText: '放弃修改'
}).then(() => {
    this.$message({
        type: 'info',
        message: '保存修改'
    });
}).catch(action => {
    this.$message({
        type: 'info',
        message: action === 'cancel'
        ? '放弃保存并离开页面'
        : '停留在当前页面'
    })
});

附件上传

日期选择器

两个 el-date-picker 组件来选择起始时间和结束时间,控制开始时间不得大于结束时间

<template>
  <div>
    <el-date-picker v-model="startTime" :picker-options="startPickerOptions" ></el-date-picker>
    <el-date-picker v-model="endTime" :picker-options="endPickerOptions"></el-date-picker>
      <!-- 格式化时间,此时 下面的 判断需要将 日期转为 date,否则无法比较 数值
      	<el-date-picker v-model="endTime" :picker-options="endPickerOptions" value-format="yyyy-MM-dd"></el-date-picker> 
	  -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      startTime: null,
      endTime: null,
      startPickerOptions: {
        disabledDate: this.disabledStartDate // 设置第一个时间选择器的禁用日期函数
      },
      endPickerOptions: {
        disabledDate: this.disabledEndDate // 设置第二个时间选择器的禁用日期函数
      }
    };
  },
  methods: {
    disabledStartDate(date) {
      // 第一个时间选择器的禁用日期函数,禁止选择大于等于第二个时间选择器的时间
      if (this.endTime) {
        return date >= this.endTime;
      }
      return false;
    },
    disabledEndDate(date) {
      // 第二个时间选择器的禁用日期函数,禁止选择小于等于第一个时间选择器的时间
      if (this.startTime) {
          // 如果指定了 value-format="yyyy-MM-dd" 此时 返回的日期是string类型,则比对时   date <= new Date(this.startTime);
        return date <= this.startTime; 
      }
      return false;
    }
  }
};
</script>

表单校验-列表

需要注意的是

  • <el-form :model="form"> 所以列表中使用的参数 是 form.expandList 否则无法表达校验

  • :prop="'expandList.' + scope.$index + '.operator'" :rules="getValidationRules(scope.row)" 注意这里对prop的定义

image-20230814165252153
image-20230814165252153
<el-form ref="form" :model="form" :rules="rules" label-width="80px" label-position="top">
    <el-table :data="form.expandList" style="width: 100%; margin-top: 25px;" class="list-form">
        <el-table-column label="设备参数" class-name="required-fields">
            <template slot-scope="scope">
<div class="parNameRequired" :class="{required:scope.row.required}">{{scope.row.parName}}</div>
            </template>
        </el-table-column>
        <el-table-column label="参数数值" prop="operator">
            <template slot-scope="scope">
<el-form-item   :prop="'expandList.' + scope.$index + '.operator'"  :rules="getValidationRules(scope.row)">
    <el-input  v-model="scope.row.operator" style="width: 80%" placeholder="请输入...." />
                </el-form-item>
            </template>
        </el-table-column>
    </el-table>
</el-form>
<script>
    export default {
        data () {
            return {
                // 表单参数
                form: {
                    expandList: []
                },
            },
        },
            methods: {
                getValidationRules(row) {
                    const rules = [];
                    if (row.required) {
                        rules.push({
                            required: true,
                            message: '该字段为必填项',
                            trigger: 'blur' // 触发校验的事件,可以根据需要修改
                        });
                    }
                    return rules;
                },
            }    

        }
</script>


<style lang="scss" scoped>
    .list-form{
        /* 必填 添加 红色*  */
        .required-fields{
            .required:before{
                content: '*';
                color: #ff4949;
                margin-right: 0.020833rem
            }
        }
        /deep/ .el-form-item__content{
            display: flex;
        }
        /deep/ .el-form-item__error {
            top: 50%;
            right: 0;
            left: auto;
            transform: translateY(-50%);
        }
    }

demo2

注意这里的 prop 路径需要与 form 模型中的结构对应

例如 form.emgSiteRoleRecList[index].userId ===> :prop="'emgSiteRoleRecList.'+ index + '.userId'"

<el-col :span="24">
              <el-form-item label="现场情况说明" prop="description">
                <el-input v-model="form.description" type="textarea" placeholder="请输入现场情况说明" maxlength="1000"
                          rows="4"/>
              </el-form-item>
            </el-col>

            <el-col :span="24" >
              <table class="custom-table" border>
                <tr>
                  <th>角色</th>
                  <th>紧急联系人</th>
                  <th>紧急联系人-电话</th>
                </tr>
                <tr v-for="(item, index) in form.emgSiteRoleRecList" :key="index">
                  <td>{{ item.roleName }}</td>
                  <td class="w">
                    <el-form-item :prop="'emgSiteRoleRecList.'+ index + '.userId'" :rules="rules.userId">
                      <treeselect :flat="true" v-model="item.userId" :options="userList" :disable-branch-nodes="true"
                                  :show-count="true" @select="checkUser($event, index)" placeholder="请选择紧急联系人"/>
                    </el-form-item>

                  </td>
                  <td>
                    <el-form-item :prop="'emgSiteRoleRecList.'+ index + '.userPhone'" :rules="rules.userPhone">
                      <el-input v-model="item.userPhone" placeholder="请输入紧急联系人电话" maxlength="11"/>
                    </el-form-item>
                  </td>
                </tr>
              </table>
            </el-col>

循环生成多个el-table-column

<template>
  <el-table :data="tableData">
    <el-table-column v-for="column in columns" :key="column.prop" :label="column.label" :prop="column.prop"></el-table-column>
  </el-table>
</template>
 
<script>
export default {
  data() {
    return {
      tableData: [
        { name: 'John', age: 30, city: 'New York' },
        { name: 'Jane', age: 25, city: 'London' },
        { name: 'Tom', age: 35, city: 'Tokyo' }
      ],
      columns: [
        { label: '姓名', prop: 'name' },
        { label: '年龄', prop: 'age' },
        { label: '城市', prop: 'city' }
      ]
    };
  }
};
</script>

el-date-picker选择日期后视图不更新的问题

通过对input 时间进行刷新,直接触发搜索功能,不用额外处理

或者在input 中重新赋值,或者调用this.$forceUpdate()

<el-date-picker v-model="queryParams.year" type="year" placeholder="计划年度"  format="yyyy" value-format="yyyy"
                                class="table-search-picker"  @input="handleQuery"></el-date-picker>

列表实现复选功能

index.vue

34556
34556

popover提示框

image-20240920171039504
image-20240920171039504
<el-checkbox-group v-model="confirmationItem">
    <el-tooltip placement="top" v-for="item in confirmationItemOptions">
        <div slot="content" style="white-space: pre-line;">{{item.title}}</div> <!-- style="white-space: pre-line;" 这步是核心 -->
        <el-checkbox :label="item.label" ></el-checkbox>
    </el-tooltip>
</el-checkbox-group>
const item2 ={
    label: "是否根据补偿总额对谈判结果签字确认",
    title:  "1.在月度清障计划内,补偿总额在5万元/单(不含5万元),站区主任须视情况参与谈判过程,对谈判结果签字确认。\n" +
            "2.在月度清障计划内,补偿总额超过5万元/单-10万元/单(不含10万元)的,分公司管理部门人员及站区主任须参与谈判过程,并对谈判结果签字确认。\n" +
            "3.在月度清障计划内,补偿总额超过10万元/单以上的,应急抢修中心清障管理专责、分公司管理部门人员及站区主任须参与谈判过程,并对谈判结果签字确认。\n ",
}
this.confirmationItemOptions.push(item2)

异常

1. <el-empty> 使用报错

known custom element: <el-empty> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

更新版本,使用2.15.6及以上的版本

npm i element-ui@2.15.6 -S

2. el-table下添加select 弹窗无法正常显示

  .el-table .cell {
    overflow: inherit;
  }

3. tip提示

tooltip里面的内容必须用标签包裹的

另外里面只能有一个标签

<el-tooltip class="item" effect="dark" :content="item.tagName" placement="top-start">
	<span>ID:#{{ index + 1 }} {{ item.tagName }}</span>
	<span>xxx</span><!-- 该标签是不生效的 -->
</el-tooltip>