封装el-table为可编辑表格

博客围绕前端Vue代码展开,提到可编辑表格未通过校验时,父组件不可提交表单,提交按钮需设置disabled,未通过时input框可编辑。还指出父组件传值给子组件,子组件数据依赖该值时无法正常更新,可利用v-if判断,数据拿回后再渲染子组件。

在这里插入图片描述

代码

<template>
  <div class="edit-table">
    <el-button
      ref="button"
      type="primary"
      size="medium"
      @click.native.prevent="handleAddRow"
    >
      <i class="el-icon-plus"></i>
      添加
    </el-button>
    <el-form :rules="rules" :model="form" ref="ruleForm">
      <el-table
        id="dialog-table"
        :data="form.tableData"
        size="small"
        class="table-wrap"
        @cell-mouse-enter="handleCellEnter"
        @cell-mouse-leave="handleCellLeave"
        @cell-click="handleCellClick"
      >
        <el-table-column prop="categoryValue" label="码表值" width="300">
          <template slot-scope="scope">
            <el-form-item
              :prop="'tableData.' + scope.$index + '.categoryValue'"
              :rules="rules.categoryValue"
            >
              <el-input
                ref="categoryValueInput"
                class="item__input"
                v-model="scope.row.categoryValue"
                placeholder="请输入内容"
                @blur="handleSaveRow(scope.row)"
                maxlength="50"
                show-word-limit
              ></el-input>
            </el-form-item>
            <div ref="categoryValueTxt" class="item__txt l-box-start-center">
              {{ scope.row.categoryValue }}
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="categoryKey" label="码表项" width="300">
          <div class="item" slot-scope="scope">
            <el-form-item
              :prop="'tableData.' + scope.$index + '.categoryKey'"
              :rules="rules.categoryKey"
            >
              <el-input
                ref="categoryKeyInput"
                class="item__input"
                v-model="scope.row.categoryKey"
                placeholder="请输入内容"
                @blur="handleSaveRow(scope.row)"
                maxlength="50"
                show-word-limit
              ></el-input>
            </el-form-item>
            <div ref="categoryKeyTxt" class="item__txt l-box-start-center">
              {{ scope.row.categoryKey }}
            </div>
          </div>
        </el-table-column>
        <el-table-column prop="notValid" label="有效标识">
          <div class="item" slot-scope="scope">
            <el-select
              class="item__input select__input l-box-start-center"
              popper-class="edittable-select"
              v-model="scope.row.notValid"
              placeholder="请选择"
              @blur="handleSaveRow(scope.row)"
            >
              <el-option
                v-for="item in validOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              </el-option>
            </el-select>
            <div class="item__txt l-box-start-center">
              {{ validLabel(scope.row.notValid) }}
            </div>
          </div>
        </el-table-column>
        <el-table-column label="操作" width="100">
          <template slot-scope="scope">
            <el-button
              class="edit-table-btn-oper"
              @click.native.prevent="handleDeleteRow(scope.$index, scope.row)"
              type="text"
              size="small"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
    </el-form>
  </div>
</template>

<script>
export default {
  name: 'EditTable',
  props: {
    categoryList: {
      type: Array,
      default: () => [],
    },
    isEditMode: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      // 下拉选项
      validOptions: [
        {
          value: '00',
          label: '有效',
        },
        {
          value: '01',
          label: '无效',
        },
      ],
      // 需要编辑的属性
      editProp: ['categoryKey', 'categoryValue', 'notValid'],
      clickCellMap: {}, // 记录当前编辑行所有cell的id,用于失焦时取消编辑状态
      changeCnt: 0,
      form: {
        tableData: _.cloneDeep(this.categoryList),
      },
      rules: {
        categoryValue: [
          {
            required: true,
            message: '码表值不能为空',
            trigger: 'blur',
          },
        ],
        categoryKey: [
          {
            required: true,
            message: '码表项不能为空',
            trigger: 'blur',
          },
        ],
      },
    }
  },
  computed: {
    validLabel() {
      return (val) => {
        if (!val) {
          return '有效'
        }
        return this.validOptions.find((o) => o.value === val).label
      }
    },
  },
  created() {
    // this.init()
  },
  methods: {
    init() {
      // this.$nextTick(() => {
      //   console.log(this.form.tableData)
      // })
    },
    /** 鼠标移入cell */
    handleCellEnter(row, column, cell, event) {
      const property = column.property
      if (this.editProp.includes(property)) {
        cell.querySelector('.item__txt').classList.add('item__txt--hover')
      }
    },
    /** 鼠标移出cell */
    handleCellLeave(row, column, cell, event) {
      const property = column.property
      if (this.editProp.includes(property)) {
        cell.querySelector('.item__txt').classList.remove('item__txt--hover')
      }
    },
    /** 鼠标点击cell */
    handleCellClick(row, column, cell, event) {
      const property = column.property
      if (this.editProp.includes(property)) {
        // 保存cell
        this.saveCellClick(row, cell)
        this.setEditable(cell)
        // 下拉选框点击展开
        let selectCell = cell.querySelector('.item__input.select__input')
        selectCell && selectCell.__vue__.toggleMenu()
      }
    },
    /** 保存数据(失焦时触发) */
    handleSaveRow(curRow) {
      this.$refs.ruleForm.validate((valid, notValidObj) => {
        // 根据校验情况设置按钮状态
        this.$emit('setBtnDisabled', !valid)
        // 若当前行通过校验,则取消编辑状态
        const notValidRowIdx = Object.keys(notValidObj).map(
          (key) => key.match(/tableData\.(\S*)\.category/)[1]
        )
        const curRowValid = !notValidRowIdx.some(
          (idx) => idx == curRow.id.replace('row', '')
        )
        curRowValid && this.deleteCellClick(curRow.id)
      })
    },
    /** 保存进入编辑的cell */
    saveCellClick(row, cell) {
      const id = row.id
      if (this.clickCellMap[id] == undefined) {
        this.clickCellMap[id] = [cell]
        return
      }
      if (!this.clickCellMap[id].includes(cell)) {
        this.clickCellMap[id].push(cell)
      }
    },
    /** 删除取消编辑状态的cell */
    deleteCellClick(rowId) {
      // 取消当前行所有cell的编辑状态
      if (this.clickCellMap[rowId]) {
        this.clickCellMap[rowId].forEach((cell) => {
          this.cancelEditable(cell)
        })
        delete this.clickCellMap[rowId]
      }
    },
    /** 添加数据(点击添加触发) */
    handleAddRow() {
      const lastItemIndex = this.form.tableData.length
      const item = {
        id: `row${lastItemIndex}`, // 初始化,保存到后台会自动生成id
        categoryKey: '',
        categoryValue: '',
        notValid: '00',
      }
      this.form.tableData.push(item)
      this.$nextTick(() => {
        // 记录编辑状态
        const tableDom = document.getElementById('dialog-table')
        const rowDomList = tableDom.getElementsByClassName('el-table__row')
        const cell = rowDomList[lastItemIndex].querySelector('td')
        this.saveCellClick(item, cell)
        // 聚焦
        this.$refs.categoryValueTxt.style.display = 'none'
        this.$refs.categoryValueInput.$el.style.display = 'flex'
        this.$refs.categoryValueInput.$refs.input.focus()
      })
    },
    /** 删除数据(点击删除触发) */
    handleDeleteRow(index, row) {
      this.form.tableData.splice(index, 1)
      this.deleteCellClick(row.id)
      // 待界面更新后再进行验证
      this.$nextTick(() => {
        this.$refs.ruleForm.validate((valid) => {
          if (!valid) return
          // 列表不为空时, 确定按钮设为可用
          !_.isEmpty(this.categoryList) && this.$emit('setBtnDisabled', false)
        })
      })
    },
    /** 进入编辑状态 */
    setEditable(cell) {
      cell.querySelector('.item__txt').style.display = 'none'
      cell.querySelector('.item__input').style.display = 'flex'
      cell.querySelector('input').focus()
    },
    /** 取消编辑状态 */
    cancelEditable(cell) {
      cell.querySelector('.item__txt').style.display = 'flex'
      cell.querySelector('.item__input').style.display = 'none'
    },
    // moveToErr() {
    //   this.$nextTick(() => {
    //     let isError = document.getElementsByClassName('is-error')
    //     if (isError.length) {
    //       isError[0].scrollIntoView({
    //         block: 'center',
    //         behavior: 'smooth',
    //       })
    //       // 这个当滑动到报错项之后自动获取输入框的焦点,方便用户直接进行输入,延迟 800ms 是因为需要都能到定位成功后在进行获取焦点体验更好一些
    //       setTimeout(() => {
    //         if (isError[0]?.querySelector('input')) {
    //           isError[0].querySelector('input').focus()
    //         }
    //       }, 800)
    //     }
    //   })
    // },
  },
  watch: {
    // 监听表单变动更新状态
    'form.tableData': {
      handler(val) {
        // this.changeCnt++
        // if (
        //   // 编辑模式下第一次赋值不触发变动
        //   (!this.isEditMode && this.changeCnt > 0) ||
        //   (this.isEditMode && this.changeCnt > 1)
        // ) {
        //   this.$emit('updateCategoryList', _.cloneDeep(val))
        // }
        this.$emit('updateCategoryList', _.cloneDeep(val))
      },
      deep: true,
    },
  },
}
</script>

<style lang="less" scoped>
/deep/ .el-input__inner {
  padding-left: 8px;
}

.edit-table {
  height: 100%;
  overflow-y: auto;

  &-btn-oper {
    color: #358df6;
    font-weight: bold;
    font-size: 14px;
  }

  .table-wrap {
    width: 100%;
    height: calc(100% - 36px);
    overflow-y: auto;
  }

  .el-form-item {
    padding: 0;
    &.is-error {
      padding: 0 0 16px 0;
      .item__input {
        display: block !important;
      }

      & + .item__txt {
        display: none !important ;
      }
    }
  }
}
</style>

<style lang="less">
.el-table thead,
.el-table__row {
  color: #333333;
  font-size: 14px;
}

.item__input {
  display: none;
  /* 调整elementUI中样式 如果不需要调整请忽略 */
  .el-input__inner {
    height: 32px !important;
  }
  /* 调整elementUI中样式 如果不需要调整请忽略 */
  .el-input__suffix {
    i {
      font-size: 12px !important;
      line-height: 26px !important;
    }
  }
}
.item__txt {
  box-sizing: border-box;
  border: 1px solid transparent;
  width: 100%;
  height: 32px;
  line-height: 24px;
  padding: 0 8px;
}
.item__txt--hover {
  border: 1px solid #dddddd;
  border-radius: 4px;
  cursor: text;
}
.edittable-select {
  min-width: 311px !important;
}

.edit-table .el-table__body-wrapper {
  height: calc(100% - 40px);
  overflow-y: scroll;
}
</style>


可编辑表格未通过校验时,父组件不可提交表单

提交按钮设置disabled,校验通过才为true

未通过时,input框为可编辑状态;用样式改

    &.is-error {
      padding: 0 0 16px 0;
      .item__input {
        display: block !important;
      }

      & + .item__txt {
        display: none !important ;
      }
    }

问题:父组件从后台拿值后传给子组件,当子组件的数据依赖该值时,无法正常更新数据

注:直接用prop的值,数据更新正常

例:子组件中,某个数据依赖父组件发送请求传回的值

 <el-form :model="form" ref="ruleForm">
   <el-table
     id="dialog-table"
     :data="form.tableData"
     size="small"
     class="table-wrap"
   >
   ...
   </el-table>
 </el-form>
      
export default {
  props: {
    list: {
      type: Array,
      default: () => [],
    }
  },
  data() {
    return {
      form: {
        tableData: this.list,
      }
    }
  }

解决:父组件中,可利用 v-if 条件判断,在数据拿回来后,再渲染子组件
(可加上loading防止出现组件区域空白问题)

<div
  v-loading="!ruleForm.codeTableCategoryList.length"
>
  <div v-if="ruleForm.codeTableCategoryList.length">
    <comp :refer="ref" :basic="basic" :list="ruleForm.codeTableCategoryList" />
  </div>
</div>
<!-- 添加用户弹框--> <el-dialog :title="title" v-model="openAddUser" width="90%" append-to-body fullscreen> <el-form :model="queryEmp" ref="queryDetRef" :inline="true" label-width="150px"> <el-form-item label="用户" prop="EMP_NAME_"> <el-input v-model="queryEmp.EMP_NAME_" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQueryEmp" /> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQueryEmp">搜索</el-button> <el-button icon="Refresh" @click="resetQueryDet">重置</el-button> </el-form-item> </el-form> <!-- 使用 el-row 和 el-col 实现左右布局 --> <el-row :gutter="20"> <!-- 左侧表格 --> <el-col :span="12"> <el-card> <el-table v-loading="loading" :data="empListLeft"> <el-table-column type="selection" width="50" align="center"/> <el-table-column label="用户编号" align="center" key="EMP_ID_" prop="EMP_CODE_"/> <el-table-column label="用户名称" align="center" key="userName" prop="EMP_NAME_"/> <el-table-column label="机构名称" align="center" key="ORG_NAME_" prop="ORG_NAME_"/> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template #default="scope"> <el-button link type="primary" @click="handleAddEmp(scope.row)">添加</el-button> </template> </el-table-column> </el-table> </el-card> </el-col> <!-- 右侧表格 --> <el-col :span="12"> <el-card> <el-table v-loading="loading" :data="empListRight"> <el-table-column type="selection" width="50" align="center"/> <el-table-column label="用户编号" align="center" key="EMP_ID_" prop="EMP_CODE_"/> <el-table-column label="用户名称" align="center" key="
03-11
<template> <div style=" display: flex; justify-content: center; align-items: center; height: 100%; width: 100%; background-color: #fff; "> <!-- 封装后的弹窗组件 --> <side-slope-dialog :visible="dialogVisible" :mode="currentMode" :initial-form="currentForm" @update:visible="dialogVisible = $event" @success="handleDialogSuccess" /> <!-- 页面主体内容 --> <el-container style="height: 100%;"> <el-header> <el-row :gutter="10" type="flex" justify="left"> <el-col :span="3"><el-input v-model="projectName" placeholder="项目名称" size="mini"></el-input></el-col> <el-col :span="3"><el-select v-model="selectStatus" placeholder="项目状态" size="mini" clearable> <el-option v-for="item in StatusOptions" :key="item.value" :label="item.label" :value="item.value" filterable></el-option> </el-select></el-col> <el-col :span="6"><el-date-picker v-model="searchProjectDate" range-separator="→" start-placeholder="请选择开始日期" end-placeholder="请选择结束日期" type="daterange" size="mini" style="width: 100%;"> </el-date-picker> </el-col> <el-col :span="1"><el-button type="primary" size="mini" @click="searchMainTable">查询</el-button></el-col> <el-col :span="1"><el-button type="success" size="mini" style="width:100%" @click="openCreateDialog">新建</el-button></el-col> </el-row> </el-header> <el-main> <el-table :data="tableData" border style="width: 100%;" :main-height="400" :header-row-style="() => { return 'line-height:15px'; }" :cell-style="{ textAlign: 'center' }" :header-cell-style="{ textAlign: 'center' }"> <el-table-column label="序号" type="index" width="120"></el-table-column> <el-table-column label="项目名称" prop="projectName" width="250"></el-table-column> <el-table-column label="项目编号" prop="projectCode" width="150"></el-table-column> <el-table-column label="项目周期" width="250"> <template slot-scope="scope"> {{ formatDateRange(scope.row.projectStartDate, scope.row.projectEndDate) }} </template> </el-table-column> <el-table-column label="项目状态" width="200"> <template slot-scope="scope"> {{ getStatusText(scope.row.status) }} </template> </el-table-column> <el-table-column label="边坡总数" prop="sideSlopeTotalCount" width="150"></el-table-column> <el-table-column label="已完成边坡数" prop="sideSlopeCompleteCount" width="194"></el-table-column> <el-table-column label="完成率" width="150"> <template slot-scope="scope"> {{ calculateCompletionRate(scope.row.sideSlopeCompleteCount, scope.row.sideSlopeTotalCount) }} </template> </el-table-column> <el-table-column label="操作" width="200"> <template slot-scope="scope"> <el-button @click="openViewDialog(scope.row)" type="text" size="small">查看</el-button> <el-button @click="openEditDialog(scope.row)" type="text" size="small">编辑</el-button> <el-button @click="deleteItem(scope.row)" type="text" size="small">删除</el-button> </template> </el-table-column> </el-table> </el-main> <el-footer> <!-- 分页 --> <div class="pagination" style="margin-top:20px;text-align:center;"> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total,prev, sizes, pager, next,jumper" :current-page.sync="pageParams.pageNo" :page-size="pageParams.pageSize" :page-sizes="[10, 40, 60, 100]" :total="pageParams.total"> </el-pagination> </div> </el-footer> </el-container> </div> </template> <script> import sideSlopeDialog from './sideSlopeDialog.vue'; import { mapCfg } from "@/utils"; // 字典映射工具 import { getPeriodicInspectionPageList, deletePeriodicInspection } from '../../api/testProject'; export default { name: "ProjectManagement", components: { sideSlopeDialog }, data() { return { projectName: '', selectStatus: '', StatusOptions: [], searchProjectDate: [], tableData: [], pageParams: { pageNo: 1, pageSize: 10, total: 0 }, // 弹窗相关状态 dialogVisible: false, dialogMode: "create", currentForm: { projectCode: '', projectName: '', projectStartDate: '', projectEndDate: '', projectUser: '', remark: '', sideSlopeDetailList: [] } } }, computed: { }, async created() { await this.getStatus(); this.loadTableData(); }, methods: { // 将状态值转换为中文文本 getStatusText(statusValue) { const option = this.StatusOptions.find(opt => opt.value === statusValue); return option.label; }, // 打开新建弹窗 openCreateDialog() { this.dialogMode = 'create'; this.currentForm = { projectCode: '', projectName: '', projectStartDate: '', projectEndDate: '', projectUser: '', remark: '', sideSlopeDetailList: [] }; this.dialogVisible = true; }, // 打开编辑弹窗 openEditDialog(row) { this.currentMode = 'edit'; this.currentForm = { ...row, projectStartDate: row.projectStartDate, projectEndDate: row.projectEndDate }; this.dialogVisible = true; }, // 打开查看弹窗 openViewDialog(row) { this.dialogMode = 'view'; this.currentForm = { ...row, projectStartDate: row.projectStartDate, projectEndDate: row.projectEndDate }; this.dialogVisible = true; }, // 弹窗提交成功后的处理 handleDialogSuccess() { this.loadTableData(); }, // 加载主表格数据 async loadTableData() { try { const params = { pageNo: this.pageParams.pageNo, pageSize: this.pageParams.pageSize, projectName: this.projectName, status: this.selectStatus, startDate: this.searchProjectDate[0], endDate: this.searchProjectDate[1] }; const res = await getPeriodicInspectionPageList(params); this.tableData = res.entities || []; this.pageParams.total = res.entityCount || 0; } catch (error) { console.error('加载数据失败', error); this.$message.error('加载数据失败'); } }, // 搜索主表格 searchMainTable() { this.pageParams.pageNo = 1; this.loadTableData(); }, // 删除项目 async deleteItem(row) { try { await this.$confirm('确定要删除该项目吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await deletePeriodicInspection({ id: row.id }); this.$message.success('删除成功'); this.loadTableData(); } catch (error) { if (error !== 'cancel') { console.error('删除失败', error); this.$message.error('删除失败'); } } }, // 项目状态字典 async getStatus() { try { const dictList = await mapCfg("Inspection.Periodic.PeriodicInspectionStatus")(); console.log(dictList) this.StatusOptions = dictList.map(item => ({ value: item.key, label: item.value })); } catch (error) { console.error('获取状态失败', error); } }, // 格式化日期范围 formatDateRange(start, end) { if (!start || !end) return ''; return `${start} 至 ${end}`; }, // 计算完成率 calculateCompletionRate(completed, total) { if (!total) return '0%'; return `${((completed / total) * 100).toFixed(1)}%`; }, // 分页处理 handleSizeChange(val) { this.pageParams.pageSize = val; this.loadTableData(); }, handleCurrentChange(val) { this.pageParams.pageNo = val; this.loadTableData(); } } } </script> <style lang="scss" scoped> .el-header { color: #333; text-align: center; line-height: 60px; } .el-main { color: #333; text-align: center; line-height: 100%; padding-left: 5px; padding-top: 0px; } .pagination { margin-top: 20px; text-align: center; } </style> 实现查看和修改的时候能看到已经选择的边坡取得已选择的定期检查边坡列表接口 【GET】/sshm/rest/periodicInspection/getSelectedPeriodicInspectionSideSlopeList 参数: periodicId------定期检查Id
08-16
<template> <div class="position_content"> <div class="position_search_form"> <el-form :inline="true" :model="searchForm" ref="searchForm"> <el-form-item label="职位名称" prop="name"> <el-input v-model="searchForm.name" placeholder="请输入职位名称"></el-input> </el-form-item> <el-form-item label="驾龄" prop="driveAge"> <el-input v-model="searchForm.driveAge" placeholder="请输入驾龄"></el-input> </el-form-item> <el-form-item label="薪资发放方式" prop="payGrantType"> <el-select v-model="searchForm.payGrantType" placeholder="请选择薪资发放方式"> <el-option v-for="(item, index) in payGrantTypeArr" :key="index" :label="item.dictLabel" :value="item.dictValue" /> </el-select> </el-form-item> <el-form-item label="准驾车型" prop="driveMode"> <el-select v-model="searchForm.driveMode" placeholder="请选择准驾车型"> <el-option v-for="(item, index) in driverModeArr" :key="index" :label="item.dictLabel" :value="item.dictLabel" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleSearchList()">查询</el-button> <el-button @click="handleResetList()">重置</el-button> </el-form-item> </el-form> </div> <div class="position_tabbar"> <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddPosition()">新增</el-button> </div> <div class="position_table"> <el-table :data="tableData" border height="72vh" style="width: 100%;" :header-cell-style="headerCellStyle" v-loading="tableLoading" > <el-table-column prop="name" label="职位名称" min-width="120" /> <el-table-column prop="postType" label="职位类别" align="center" min-width="120"> <template #default="scope"> {{ dictTypeChange(postTypeArr,scope.row.postType) }} </template> </el-table-column> <el-table-column prop="numApply" label="已投简历" align="center" min-width="120" /> <el-table-column prop="name" label="地区" min-width="120" show-overflow-tooltip> <template #default="scope"> <span>{{scope.row.getOnAddressDistr}}</span> </template> </el-table-column> <el-table-column prop="salaryStand" label="薪资标准" align="center" min-width="120" /> <el-table-column prop="payGrantType" label="薪资发放方式" align="center" min-width="120"> <template #default="scope"> {{ dictTypeChange(payGrantTypeArr,scope.row.payGrantType) }} </template> </el-table-column> <el-table-column prop="goodsType" label="货物类型" align="center" min-width="120"> <template #default="scope"> {{ dictTypeChange(goodsTypeArr,scope.row.goodsType) }} </template> </el-table-column> <el-table-column prop="driveMode" label="准驾车型" align="center" min-width="120"></el-table-column> <el-table-column prop="driveAge" label="驾龄要求" align="center" min-width="120" /> <el-table-column label="操作" width="220" align="center" fixed="right"> <template #default="scope"> <el-button type="primary" size="mini" @click="handleEditPosition(scope.row)">编辑</el-button> <el-button type="primary" size="mini" @click="handleDetailPosition(scope.row)">详情</el-button> <el-button type="danger" size="mini" @click="handleDeletePosition(scope.row)">删除</el-button> </template> </el-table-column> </el-table> </div> <div class="position_pagination"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 15, 20, 25]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total" > </el-pagination> </div> <!-- form新增/修改弹窗 --> <el-dialog :title="title" :visible.sync="dialogFormVisible" width="60%" :before-close="handleFormBeforeClose" > <div class="position_form_content"> <el-form :model="positionForm" :rules="positionRules" ref="positionFormRef" label-width="120px"> <div class="position_form_item"> <el-form-item label="标题" prop="name" style="width: 100%"> <el-input style="width: 100%" v-model="positionForm.name" placeholder="请输入发布标题"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="货物类型" prop="goodsType" style="width: 50%"> <el-select v-model="positionForm.goodsType" placeholder="请选择货物类型" style="width: 100%" > <el-option v-for="(item, index) in goodsTypeArr" :key="index" :label="item.dictLabel" :value="item.dictValue" /> </el-select> </el-form-item> <el-form-item label="驾照等级" prop="driveMode" style="width: 50%"> <el-select v-model="positionForm.driveMode" placeholder="请选择驾照等级" style="width: 100%"> <el-option v-for="(item, index) in driverModeArr" :key="index" :label="item.dictLabel" :value="item.dictLabel" /> </el-select> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="任务类型" prop="position" style="width: 50%"> <el-select v-model="positionForm.position" placeholder="请选择任务类型" style="width: 100%"> <el-option v-for="(item, index) in positionArr" :key="index" :label="item.dictValue" :value="item.dictLabel" /> </el-select> </el-form-item> <el-form-item label="岗位类型" prop="transportLine" style="width: 50%"> <el-select v-model="positionForm.postType" placeholder="请选择任务类型" style="width: 100%"> <el-option v-for="(item, index) in postTypeArr" :key="index" :label="item.dictLabel" :value="item.dictValue" /> </el-select> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="用工人数" prop="employGap" style="width: 50%"> <el-input v-model="positionForm.employGap" type="number" placeholder="请输入用工人数"></el-input> </el-form-item> <el-form-item label="驾龄要求" prop="driveAge" style="width: 50%"> <el-input v-model="positionForm.driveAge" placeholder="请输入驾龄要求"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="薪资范围" prop="salaryStand" style="width: 50%"> <el-input v-model="positionForm.salaryStand" type="number" placeholder="请输入薪资范围"></el-input> </el-form-item> <el-form-item label="福利待遇" prop="socialBenefits" style="width: 50%"> <el-input v-model="positionForm.socialBenefits" placeholder="请输入福利待遇"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="联系方式" prop="salaryStand" style="width: 50%"> <el-input v-model="positionForm.salaryStand" type="number" placeholder="请输入联系方式"></el-input> </el-form-item> <el-form-item label="运输次数" prop="haulCount" style="width: 50%"> <el-input v-model="positionForm.haulCount" placeholder="请输入运输次数"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="开始时间" prop="startDate" style="width: 50%"> <el-date-picker v-model="positionForm.startDate" type="datetime" style="width: 100%" placeholder="请选择开始时间" @change="handleStartDate" /> </el-form-item> <el-form-item label="结束时间" prop="endDate" style="width: 50%"> <el-date-picker v-model="positionForm.endDate" type="datetime" style="width: 100%" placeholder="请选择结束时间" @change="handleEndDate" /> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="上车地址" prop="getOnAddress" style="width: 50%"> <el-cascader ref="formCascader" v-model="positionForm.getOnAddress" :options="mapDataFormat" style="width: 100%" @change="handleCascaderChange" /> </el-form-item> <el-form-item label="运输路线" prop="transportLine" style="width: 50%"> <el-input v-model="positionForm.transportLine" placeholder="请输入运输路线,例如:A-B"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="补充描述" prop="remark" style="width: 100%"> <el-input type="textarea" show-word-limit :rows="10" v-model="positionForm.remark" placeholder="请输入补充描述" /> </el-form-item> </div> </el-form> </div> <span slot="footer" class="dialog-footer"> <el-button @click="handleFormCancel">取 消</el-button> <el-button type="primary" @click="handleFormSubmit">确 定</el-button> </span> </el-dialog> <!-- form详情弹窗 --> <el-dialog title="职位详情" :visible.sync="dialogFormDetailVisible" width="40%" :before-close="handleFormDetailBeforeClose" > <div class="position_form_content"> <el-form :model="positionFormDetail" label-width="120px"> <div class="position_form_item"> <el-form-item label="职位名称" prop="name" style="width: 50%"> <el-input v-model="positionFormDetail.name" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="企业名称" prop="deptName" style="width: 50%"> <el-input v-model="positionFormDetail.deptName" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="货物类型" prop="goodsType" style="width: 50%"> <el-input v-model="positionFormDetail.goodsType" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="准驾车型" prop="driveMode" style="width: 50%"> <el-input v-model="positionFormDetail.driveMode" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="职位类别" prop="postType" style="width: 50%"> <el-input v-model="positionFormDetail.postType" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="用工方式" prop="transportLine" style="width: 50%"> <el-input v-model="positionFormDetail.transportLine" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="用工缺口" prop="employGap" style="width: 50%"> <el-input v-model="positionFormDetail.employGap" type="number" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="薪资标准" prop="salaryStand" style="width: 50%"> <el-input v-model="positionFormDetail.salaryStand" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="薪资发放方式" prop="payGrantType" style="width: 50%"> <el-input v-model="positionFormDetail.payGrantType" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="补贴" prop="socialBenefits" style="width: 50%"> <el-input v-model="positionFormDetail.socialBenefits" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="岗位开始时间" prop="startDate" style="width: 50%"> <el-input v-model="positionFormDetail.startDate" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="岗位结束时间" prop="endDate" style="width: 50%"> <el-input v-model="positionFormDetail.endDate" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="驾龄要求" prop="driveAge" style="width: 50%"> <el-input v-model="positionFormDetail.driveAge" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> <el-form-item label="年龄要求" prop="age" style="width: 50%"> <el-input v-model="positionFormDetail.age" placeholder="暂无数据" :readonly="true"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="地区" prop="getOnAddressDistr" style="width: 100%"> <el-input v-model="positionFormDetail.getOnAddressDistr" placeholder="暂无数据" :readonly="true" /> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="岗位说明" prop="remark" style="width: 100%"> <el-input type="textarea" show-word-limit :rows="10" v-model="positionFormDetail.remark" placeholder="暂无数据" :readonly="true" /> </el-form-item> </div> </el-form> </div> <span slot="footer" class="dialog-footer"> <el-button @click="handleFormDetailCancel">取 消</el-button> </span> </el-dialog> </div> </template> <script> import { addPosition, delPosition, dictTypeList, positionDetail, positionList, updatePosition } from "@/api/positionManage"; import mapData from "@/utils/data.json" /** * desc:职位管理 */ export default { name: "index", data(){ return { searchForm:{ name:'', driveAge:'', payGrantType:'', driveMode:'' }, driverModeArr:[], payGrantTypeArr:[], postTypeArr:[], positionArr:[], goodsTypeArr:[], currentPage:1, pageSize:20, total:0, tableData:[], tableLoading:false, title:'', apiModel:'', dialogFormVisible:false, positionForm:{ id:'', name:'', deptId:'', deptName:'', postType:'', socialBenefits:'', transportLine:'', employGap:'', salaryStand:'', payGrantType:'', startDate:'', endDate:'', remark:'', driveAge:'', age:'', goodsType:'', driveMode:'', getOnAddress:null, getOnAddressProvince:'', getOnAddressCity:'', getOnAddressDistr:'', }, positionRules:{ name:[{ required: true, message: '请输入职位名称', trigger: 'blur' }], driveMode:[{ required: true, message: '请选择准驾车型', trigger: 'change' }], postType:[{ required: true, message: '请选择职位类别', trigger: 'change' }], driveAge:[{ required: true, message: '请输入驾龄要求', trigger: 'blur' }], goodsType:[{ required: true, message: '请选择货物类型', trigger: 'change' }], payGrantType:[{ required: true, message: '请选择薪资发放方式', trigger: 'change' }], age:[{ required: true, message: '请输入年龄要求', trigger: 'blur' }], startDate:[{ required: true, message: '请选择开始时间', trigger: 'change' }], endDate:[{ required: true, message: '请选择结束时间', trigger: 'change' }], remark:[{ required: true, message: '请输入岗位说明', trigger: 'blur' }], salaryStand:[{ required: true, message: '请输入薪资标准', trigger: 'blur' }], socialBenefits:[{ required: true, message: '请输入补贴', trigger: 'blur' }], employGap:[{ required: true, message: '请输入用工缺口', trigger: 'blur' }] }, dialogFormDetailVisible:false, positionFormDetail:{ name:'', deptId:'', deptName:'', postType:'', socialBenefits:'', transportLine:'', employGap:'', salaryStand:'', payGrantType:'', startDate:'', endDate:'', remark:'', driveAge:'', age:'', goodsType:'', driveMode:'', getOnAddressDistr:'' }, } }, computed:{ headerCellStyle(){ return { textAlign:'center' } }, mapDataFormat(){ return this.transformData(this.mapDataFn(mapData)); } }, mounted() { this.getDriverMode(); this.getPostType(); this.getGoodsType(); this.getPostSign() /* this.loadList(); */ }, methods:{ // 转换函数 transformData(data) { return data.map(item => ({ value: item.pid, label: item.fullname, children: item.children ? this.transformData(item.children) : null })); }, mapDataFn(data){ let _retData = []; data.map(item => { Object.keys(item).forEach(key => { if(key === 'pid'){ _retData.push({ pid: item.pid, fullname: item.fullname, children: item.children ? this.mapDataFn(item.children) : null }); }else if(key === 'cid'){ _retData.push({ pid: item.cid, fullname: item.fullname, }); } }) }) return _retData; }, dateFormat(date){ if(String(date).indexOf('GMT')>-1){ let _date = new Date(date); let _year = _date.getFullYear(); let _month = String(_date.getMonth() + 1).padStart(2, '0'); let _day = String(date.getDate()).padStart(2, '0'); return `${_year}-${_month}-${_day}`; }else{ return date; } }, //字典转换 dictTypeChange(arr,val){ if(!val){ return "——"; } return arr.find(item => item.dictValue === val)?.dictLabel; }, // 获取准驾车型 getDriverMode(){ let _this =this; dictTypeList({dictType: "driving_rank"}).then(res =>{ if(res.code === 200){ res.rows.forEach(item => { _this.driverModeArr.push({ dictLabel: item.dictLabel, dictValue: item.dictValue }) }) } }) }, //获取签约类别 getPostSign(){ let _this =this; dictTypeList({dictType: "apply_sign_type"}).then(res =>{ if(res.code === 200){ res.rows.forEach(item => { _this.positionArr.push({ dictLabel: item.dictLabel, dictValue: item.dictValue }) }) } }) }, //获取岗位类别 getPostType(){ let _this =this; dictTypeList({dictType: "post_type"}).then(res =>{ if(res.code === 200){ res.rows.forEach(item => { _this.postTypeArr.push({ dictLabel: item.dictLabel, dictValue: item.dictValue }) }) } }) }, //获取货物类型 getGoodsType(){ let _this =this; dictTypeList({dictType: "goods_type"}).then(res =>{ if(res.code === 200){ res.rows.forEach(item => { _this.goodsTypeArr.push({ dictLabel: item.dictLabel, dictValue: item.dictValue }) }) } }) }, loadList(){ let _this = this; _this.tableLoading = true; let _params = { pageNum:_this.currentPage, pageSize:_this.pageSize } //判断是否为超级管理员权限 let _roles = _this.$store.getters.roles; if(!_roles.includes('admin')){ _params.deptId = this.$store.state.user.deptId; } _params = Object.assign(_params,_this.searchForm); positionList(_params).then(res => { _this.tableLoading = false; if(res.code === 200){ _this.tableData = res.rows; _this.total = res.total; }else{ _this.$message({ type: 'error', message: res.msg }); } }) }, loadFormDetail(id,type){ let _this = this; positionDetail(id).then(res => { if(res.code === 200){ if(type === '1'){ _this.positionForm = res.data; _this.positionForm.getOnAddress = [_this.positionForm.getOnAddressProvince,_this.positionForm.getOnAddressCity]; }else{ _this.positionFormDetail = res.data; _this.positionFormDetail.deptName = _this.$store.state.user.deptName; //字典值转换,赋值到详情中 _this.positionFormDetail.goodsType = _this.dictTypeChange(_this.goodsTypeArr,_this.positionFormDetail.goodsType); _this.positionFormDetail.driveMode = _this.dictTypeChange(_this.driverModeArr,_this.positionFormDetail.driveMode); _this.positionFormDetail.payGrantType = _this.dictTypeChange(_this.payGrantTypeArr,_this.positionFormDetail.payGrantType); _this.positionFormDetail.postType = _this.dictTypeChange(_this.postTypeArr,_this.positionFormDetail.postType); } }else{ _this.$message({ type: 'error', message: res.msg }); } }) }, resetForm(){ this.$refs.positionFormRef.resetFields(); Object.keys(this.positionForm).forEach(key => { this.positionForm[key] = ''; }) Object.keys(this.positionFormDetail).forEach(key => { this.positionForm[key] = ''; }) }, handleSearchList(){ this.loadList(); }, handleResetList(){ this.$refs.searchForm.resetFields(); this.loadList(); }, handleSizeChange(val){ this.pageSize = val; this.loadList(); }, handleCurrentChange(val){ this.currentPage = val; this.loadList(); }, handleFormSubmit(){ let _this = this; _this.$refs.positionFormRef.validate((valid) => { if(valid){ if(_this.dateCompareFn(_this.dateFormat(_this.positionForm.startDate))>_this.dateCompareFn(_this.dateFormat(_this.positionForm.endDate))){ _this.$message({ type: 'error', message: '开始日期不能大于结束日期!' }); return false; } if(_this.apiModel === 'add'){ _this.positionForm.startDate = _this.dateFormat(_this.positionForm.startDate)+' 00:00:00'; _this.positionForm.endDate = _this.dateFormat(_this.positionForm.endDate)+' 00:00:00'; addPosition(_this.positionForm).then(res => { if(res.code === 200){ _this.$message({ type: 'success', message: '新增成功!' }); _this.resetForm(); _this.dialogFormVisible = false; _this.loadList(); }else{ _this.$message({ type: 'error', message: res.msg }); } }) }else{ _this.positionForm.startDate = _this.dateFormat(_this.positionForm.startDate)+' 00:00:00'; _this.positionForm.endDate = _this.dateFormat(_this.positionForm.endDate)+' 00:00:00'; updatePosition(_this.positionForm).then(res => { if(res.code === 200){ _this.$message({ type: 'success', message: '修改成功!' }); _this.resetForm(); _this.dialogFormVisible = false; _this.loadList(); }else{ _this.$message({ type: 'error', message: res.msg }); } }) } } }) }, handleFormBeforeClose(){ this.dialogFormVisible = false; this.resetForm(); }, handleFormCancel(val){ this.dialogFormVisible = false; this.resetForm(); }, handleAddPosition(){ this.dialogFormVisible = true; this.title = '新增信息'; this.apiModel = 'add'; this.positionForm.deptId = this.$store.state.user.deptId; this.positionForm.deptName = this.$store.state.user.deptName; }, handleEditPosition(row){ this.dialogFormVisible = true; this.title = '编辑信息'; this.apiModel = 'update'; this.positionForm.id = row.id; this.positionForm.deptId = this.$store.state.user.deptId; this.positionForm.deptName = this.$store.state.user.deptName; this.loadFormDetail(row.id,'1'); }, handleDetailPosition(row){ this.dialogFormDetailVisible = true; this.loadFormDetail(row.id,'2'); }, handleFormDetailBeforeClose(){ this.dialogFormDetailVisible = false; this.resetForm(); }, handleFormDetailCancel(val){ this.dialogFormDetailVisible = false; this.resetForm(); }, handleDeletePosition(row){ let _this = this; this.$confirm('是否删除此职位?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { delPosition(row.id).then(res => { if(res.code === 200){ _this.$message({ type: 'success', message: '删除成功!' }); _this.loadList() }else{ _this.$message({ type: 'error', message: res.msg }); } }) }) }, //地区事件 handleCascaderChange(val){ this.positionForm.getOnAddressProvince = val[0]; this.positionForm.getOnAddressCity = val[1]; this.positionForm.getOnAddressDistr = this.$refs.formCascader.getCheckedNodes()[0].pathLabels.join(''); }, //时间设置 handleStartDate(val){ if(this.dateCompareFn(this.dateFormat(val))>this.dateCompareFn(this.dateFormat(this.positionForm.endDate))){ this.$message({ type: 'error', message: '开始日期不能大于结束日期!' }); } }, handleEndDate(val){ if(this.dateCompareFn(this.dateFormat(val))<this.dateCompareFn(this.dateFormat(this.positionForm.startDate))){ this.$message({ type: 'error', message: '结束日期不能小于开始日期!' }); } }, dateCompareFn(date){ const isoDateString = date.replace(' ', 'T'); const _date = new Date(isoDateString); return _date.getTime(); } } } </script> <style scoped lang="scss"> .position_content{ width: 100%; box-sizing: border-box; padding: 10px 15px; .position_tabbar{ margin-bottom: 10px; } .position_pagination{ margin-top: 10px; width: 100%; text-align: right; } .position_form_item{ display: flex; flex-direction: row; } .position_form_content{ width: 100%; height: 65vh; box-sizing: border-box; padding: 10px; overflow-y: auto; } } </style> 根据 <el-form :model="positionForm" :rules="positionRules" ref="positionFormRef" label-width="120px"> <div class="position_form_item"> <el-form-item label="标题" prop="name" style="width: 100%"> <el-input style="width: 100%" v-model="positionForm.name" placeholder="请输入发布标题"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="货物类型" prop="goodsType" style="width: 50%"> <el-select v-model="positionForm.goodsType" placeholder="请选择货物类型" style="width: 100%" > <el-option v-for="(item, index) in goodsTypeArr" :key="index" :label="item.dictLabel" :value="item.dictValue" /> </el-select> </el-form-item> <el-form-item label="驾照等级" prop="driveMode" style="width: 50%"> <el-select v-model="positionForm.driveMode" placeholder="请选择驾照等级" style="width: 100%"> <el-option v-for="(item, index) in driverModeArr" :key="index" :label="item.dictLabel" :value="item.dictLabel" /> </el-select> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="任务类型" prop="position" style="width: 50%"> <el-select v-model="positionForm.position" placeholder="请选择任务类型" style="width: 100%"> <el-option v-for="(item, index) in positionArr" :key="index" :label="item.dictValue" :value="item.dictLabel" /> </el-select> </el-form-item> <el-form-item label="岗位类型" prop="transportLine" style="width: 50%"> <el-select v-model="positionForm.postType" placeholder="请选择任务类型" style="width: 100%"> <el-option v-for="(item, index) in postTypeArr" :key="index" :label="item.dictLabel" :value="item.dictValue" /> </el-select> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="用工人数" prop="employGap" style="width: 50%"> <el-input v-model="positionForm.employGap" type="number" placeholder="请输入用工人数"></el-input> </el-form-item> <el-form-item label="驾龄要求" prop="driveAge" style="width: 50%"> <el-input v-model="positionForm.driveAge" placeholder="请输入驾龄要求"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="薪资范围" prop="salaryStand" style="width: 50%"> <el-input v-model="positionForm.salaryStand" type="number" placeholder="请输入薪资范围"></el-input> </el-form-item> <el-form-item label="福利待遇" prop="socialBenefits" style="width: 50%"> <el-input v-model="positionForm.socialBenefits" placeholder="请输入福利待遇"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="联系方式" prop="salaryStand" style="width: 50%"> <el-input v-model="positionForm.salaryStand" type="number" placeholder="请输入联系方式"></el-input> </el-form-item> <el-form-item label="运输次数" prop="haulCount" style="width: 50%"> <el-input v-model="positionForm.haulCount" placeholder="请输入运输次数"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="开始时间" prop="startDate" style="width: 50%"> <el-date-picker v-model="positionForm.startDate" type="datetime" style="width: 100%" placeholder="请选择开始时间" @change="handleStartDate" /> </el-form-item> <el-form-item label="结束时间" prop="endDate" style="width: 50%"> <el-date-picker v-model="positionForm.endDate" type="datetime" style="width: 100%" placeholder="请选择结束时间" @change="handleEndDate" /> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="上车地址" prop="getOnAddress" style="width: 50%"> <el-cascader ref="formCascader" v-model="positionForm.getOnAddress" :options="mapDataFormat" style="width: 100%" @change="handleCascaderChange" /> </el-form-item> <el-form-item label="运输路线" prop="transportLine" style="width: 50%"> <el-input v-model="positionForm.transportLine" placeholder="请输入运输路线,例如:A-B"></el-input> </el-form-item> </div> <div class="position_form_item"> <el-form-item label="补充描述" prop="remark" style="width: 100%"> <el-input type="textarea" show-word-limit :rows="10" v-model="positionForm.remark" placeholder="请输入补充描述" /> </el-form-item> </div> </el-form>里面的字段名和参数名,修改列表的和详情数据
08-21
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值