vue+element 表单嵌套表格 增删改查

表单校验参考了https://blog.csdn.net/iamlujingtao/article/details/105186117

效果如图所示,主要做了一些美化和解决一些看着难受的点

代码如下,有时间再介绍

<template>
  <div class="app-container">
    <div class="filter-container">
      <el-input
        v-model="listQuery.car"
        placeholder="车辆id"
        style="width: 200px"
        class="filter-item"
        clearable
        @keyup.enter.native="handleFilter"
      />
      <el-autocomplete
        v-model="listQuery.project"
        :fetch-suggestions="querySearchAsync"
        placeholder="项目名称"
        class="filter-item"
        clearable
        @clear="setBlur()"
        @keyup.enter.native="handleFilter"
      />
      <el-date-picker
        v-model="listQuery.startTime"
        type="datetime"
        placeholder="选择开始日期时间"
        class="filter-item"
        :picker-options="pickerOptions"
        value-format="yyyy-MM-dd HH:mm:ss"
      />
      <el-date-picker
        v-model="listQuery.endTime"
        type="datetime"
        placeholder="选择结束日期时间"
        class="filter-item"
        :picker-options="pickerOptions"
        value-format="yyyy-MM-dd HH:mm:ss"
      />
      <el-button-group>
        <el-button
          class="filter-item"
          style="margin-left: 20px"
          icon="el-icon-search"
          plain
          round
          @click="handleFilter"
        > 搜索
        </el-button>
        <el-button
          class="filter-item"
          style="margin-left: 10px"
          icon="el-icon-circle-plus-outline"
          plain
          round
          @click="add"
        > 新增
        </el-button>
      </el-button-group>
    </div>

    <el-form ref="form" :model="form" :rules="rules">
      <el-table v-loading="loading" :data="form.tableData" style="width: 100%" border fit>
        <el-table-column label="车辆" min-width="15%" prop="car" align="center">
          <template v-slot="{row,$index}">
            <template v-if="row.edit">
              <el-form-item :prop="'tableData.'+$index+'.car'" :rules="rules.car">
                <el-input v-model="row.car" clearable size="small" />
              </el-form-item>
            </template>
            <span v-else>{{ row.car }}</span>
          </template>
        </el-table-column>

        <el-table-column label="项目" min-width="15%" prop="project" align="center">
          <template v-slot="{row,$index}">
            <template v-if="row.edit">
              <el-form-item :prop="'tableData.'+$index+'.project'" :rules="rules.project">
                <el-input v-model="row.project" clearable size="small" />
              </el-form-item>
            </template>
            <span v-else>{{ row.project }}</span>
          </template>
        </el-table-column>

        <el-table-column label="开始日期" min-width="20%" align="center">
          <template v-slot="{row,$index}">
            <template v-if="row.edit">
              <el-form-item :prop="'tableData.'+$index+'.startTime'" :rules="rules.startTime">
                <el-date-picker
                  v-model="row.startTime"
                  type="datetime"
                  placeholder="请选择日期"
                  align="center"
                  size="small"
                  :picker-options="pickerOptions"
                  value-format="yyyy-MM-dd HH:mm:ss"
                />
              </el-form-item>
            </template>
            <template v-else>
              <i class="el-icon-time" />
              <span>{{ row.startTime }}</span>
            </template>
          </template>
        </el-table-column>

        <el-table-column label="结束日期" min-width="20%" align="center">
          <template v-slot="{row,$index}">
            <template v-if="row.edit">
              <el-form-item :prop="'tableData.'+$index+'.endTime'" :rules="rules.endTime">
                <el-date-picker
                  v-model="row.endTime"
                  type="datetime"
                  placeholder="请选择日期"
                  align="center"
                  size="small"
                  :picker-options="pickerOptions"
                  value-format="yyyy-MM-dd HH:mm:ss"
                />
              </el-form-item>
            </template>
            <template v-else>
              <i class="el-icon-time" />
              <span>{{ row.endTime }}</span>
            </template>
          </template>
        </el-table-column>

        <el-table-column label="操作" min-width="20%" align="center">
          <template slot-scope="{row,$index}">

            <template v-if="!row.edit">
              <!--加div是为了和下面else中的button不一样,可以避免点击按钮后不失焦-->
              <div>
                <el-button
                  id="edit-button"
                  size="small"
                  plain
                  round
                  icon="el-icon-edit"
                  type="primary"
                  @click="handleEdit(row)"
                >
                  修改
                </el-button>
                <el-button
                  :id="'deleteButton'+$index"
                  size="small"
                  style="margin-left: 15px"
                  plain
                  round
                  icon="el-icon-delete"
                  type="danger"
                  @click="confirmDelete($index,row)"
                >
                  删除
                </el-button>
              </div>
            </template>

            <template v-else>
              <el-button
                id="confirm-save-button"
                size="small"
                plain
                round
                icon="el-icon-circle-check"
                type="success"
                @click="confirmSave($index,row)"
              >{{
                '保存'
              }}
              </el-button>
              <el-button
                id="cancel-save-button"
                style="margin-left: 15px"
                size="small"
                plain
                round
                icon="el-icon-circle-close"
                type="info"
                @click="cancelSave($index,row)"
              >{{
                '取消保存'
              }}
              </el-button>
            </template>

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

      </el-table>
    </el-form>
    <pagination
      v-show="total > 0"
      :total="total"
      :page.sync="listQuery.page"
      :limit.sync="listQuery.limit"
      :page-sizes.sync="pageSizes"
      @pagination="getTableData"
    />
  </div>
</template>

<script>
import Pagination from '@/components/Pagination'
// import api8899 from '@/api/api8899'

export default {
  components: { Pagination },
  data() {
    return {
      // 输入建议
      restaurants: [],
      // 加载状态
      loading: true,
      list: null,
      total: 0,
      listQuery: {
        page: 1,
        limit: 12,
        car: undefined,
        project: undefined,
        startTime: undefined,
        endTime: undefined
      },
      pageSizes: [12, 24, 36],
      form: {
        tableData: []
      },
      rules: {
        car: [{
          type: 'string',
          required: true,
          trigger: 'change',
          message: '车辆不能为空'
        }],
        project: [{
          type: 'string',
          required: true,
          trigger: 'change',
          message: '项目不能为空'
        }],
        startTime: [{
          type: 'string',
          required: true,
          trigger: 'change',
          message: '开始时间不能为空'
        }]
      },
      // 时间选项
      pickerOptions: {
        shortcuts: [
          // {
          //   text: '现在',
          //   onClick (picker) {
          //     picker.$emit('pick', new Date())
          //   }
          // },
          {
            text: '昨天',
            onClick(picker) {
              const date = new Date()
              date.setTime(date.getTime() - 3600 * 1000 * 24)
              picker.$emit('pick', date)
            }
          }, {
            text: '三天前',
            onClick(picker) {
              const date = new Date()
              date.setTime(date.getTime() - 3600 * 1000 * 24 * 3)
              picker.$emit('pick', date)
            }
          }, {
            text: '七天前',
            onClick(picker) {
              const date = new Date()
              date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
              picker.$emit('pick', date)
            }
          }]
      }

    }
  },
  mounted() {
    this.getTableData()
    this.inputSuggest()
  },
  methods: {
    async getTableData() {
      // const res = await api8899.search_car(this.listQuery)
      // this.total = res.data.code === 200 ? res.data.data.count : 0
      // this.form.tableData = res.data.data.data
      this.form.tableData = [
        {
          car: 'Aaa012',
          projectId: 'aaa',
          project: '某个项目',
          startTime: '2021-09-21 13:51:43',
          endTime: '2021-09-26 13:51:43'
        },
        {
          car: 'Aaa234',
          projectId: 'aaa',
          project: '某个项目',
          startTime: '2021-08-26 13:51:43',
          endTime: ''
        },
        {
          car: 'Aaa345',
          projectId: 'bbb',
          project: '其他项目',
          startTime: '2021-07-26 13:51:43',
          endTime: '2021-07-26 13:51:43'
        },
        {
          car: 'Aaa222',
          projectId: 'bbb',
          project: '其他项目',
          startTime: '2021-09-09 13:51:43',
          endTime: ''
        }
      ]
      this.form.tableData.map((v) => {
        this.$set(v, 'edit', false)
        // v.edit = false // 不能被v-if监听到
        return v
      })
      this.loading = false
    },
    handleFilter() {
      this.loading = true
      this.getTableData()
      this.loading = false
    },
    handleEdit(row) {
      if (row.temp === undefined) {
        row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
      } else if (row.temp.car === '') {
        delete row.temp
        row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
      }
      row.edit = !row.edit
    },
    validateField(form, index) {
      let result = true
      for (const item of this.$refs[form].fields) {
        if (parseInt(item.prop.split('.')[1]) === index) {
          this.$refs.form.validateField(item.prop, (error) => {
            if (error) {
              result = false
            }
          })
        }
        if (!result) break
      }
      return result
    },
    async confirmSave(index, row) {
      if (!this.validateField('form', index)) {
        this.$message({
          type: 'info',
          message: '保存修改失败,请检查是否有未填字段',
          center: true
        })
        return
      }
      // 判断值是否有改变,如果没有则不做下面的操作
      const tmp = Object.assign({}, row)
      const rowTmp = Object.assign({}, tmp.temp)
      delete rowTmp.edit
      delete tmp.temp
      delete tmp.edit
      if (JSON.stringify(rowTmp) === JSON.stringify(tmp)) {
        row.edit = !row.edit
        return
      }
      // 配置发送的请求参数
      // const params = {
      //   'car': row.car,
      //   'project': row.project,
      //   'startTime': row.startTime,
      //   'endTime': row.endTime,
      //   'oCar': row.temp.car,
      //   'oProject': row.temp.project,
      //   'oStartTime': row.temp.startTime,
      //   'oEndTime': row.temp.endTime
      // }
      this.loading = true
      // const response = await api8899.upsert_car(params)
      const response = { 'data': { 'code': 200 }}
      // const response = { 'data': { 'code': 23423,'message':'自定义提示' }}
      this.loading = false
      let type, message
      if (response.data.code === 500) {
        type = 'error'
        message = '保存出错,请联系开发人员修复'
      } else if (response.data.code === 200) {
        type = 'success'
        message = '保存成功'
      } else {
        type = 'info'
        message = response.data.message
      }
      this.$message({
        type: type,
        message: message,
        center: true
      })
      // info为保存数据重复等问题,直接返回
      if (type === 'info') {
        return
      }
      row.edit = !row.edit
      delete row.temp
      row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
    },
    cancelSave(index, row) {
      if (row.temp.car === '') {
        this.$delete(this.form.tableData, index)
        return
      }
      this.$set(this.form.tableData, index, row.temp) // 取消后还原数据到修改前状态
    },
    // 删除数据
    async confirmDelete(index, row) {
      // 让按钮失焦防止在提示时按多次enter键导致多次提交
      document.getElementById('deleteButton' + index).blur()
      // 设置弹窗样式
      const h = this.$createElement
      const confirmText1 = ['车辆 : ', '项目 : ', '开始时间 : ', '结束时间 : ']
      const confirmText2 = [row.car, row.project, row.startTime, row.endTime]
      const newData = []
      for (const i in confirmText1) {
        newData.push(h('span', null, confirmText1[i]))
        newData.push(h('i', { style: 'color: teal' }, confirmText2[i]))
        newData.push(h('div', null, null))
      }
      const confirm = await this.$msgbox({
        title: '确定删除该条数据吗',
        message: h('p', null, newData),
        showCancelButton: true,
        confirmButtonText: '确定',
        cancelButtonText: '取消'
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除',
          center: true
        })
      })
      if (confirm === 'confirm') {
        this.loading = true
        // const response = await api8899.delete_car({ 'car': row.car, 'project': row.project, 'startTime': row.startTime })
        const response = { 'data': { 'code': 200 }}
        const type = response.data.code === 200 ? 'success' : 'error'
        const message = response.data.code === 200 ? '成功删除数据' : '删除数据失败,请联系开发人员修复'
        this.$delete(this.form.tableData, index)
        this.loading = false
        this.$message({
          type: type,
          message: message,
          center: true
        })
      }
    },
    // 新增时重新初始化一个插入第一行
    add() {
      const temp = {
        car: '',
        project: '',
        projectId: '',
        startTime: '',
        endTime: '',
        edit: true,
        temp: {
          car: '',
          project: '',
          startTime: '',
          endTime: ''
        }
      }
      this.form.tableData.unshift(temp)
    },
    // 获取项目输入框建议
    async inputSuggest() {
      // const tmp = await api8899.search_project()
      const tmp = { data: { data: [{ value: '某个项目b' }, { value: '某个项目a' }] }}
      this.restaurants = tmp.data.data
    },
    // 处理输入框建议,restaurants必须是包含value属性的对象 [ {value:'xxx'} , ...]
    querySearchAsync(queryString, cb) {
      const restaurants = this.restaurants
      const results = queryString ? restaurants.filter(this.createStateFilter(queryString)) : restaurants
      cb(results)
    },
    // 输入框建议过滤规则
    createStateFilter(queryString) {
      return (state) => {
        return (state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
      }
    },
    // 清空输入框时取消聚焦 使用户重新点击来展示建议下拉框
    setBlur() {
      document.activeElement.blur()
    }
  }
}
</script>

<style scoped>
.filter-container {
  text-align: left;
  padding-bottom: 10px;
}

.filter-item {
  display: inline-block;
  vertical-align: middle;
  margin-bottom: 10px;
}

.app-container {
  padding: 20px;
}

/deep/ .el-form-item {
  margin-bottom: 0
}

/deep/ .el-input__inner {
  text-align: center;
}

/deep/ .el-button--text {
  display: none;
}
</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue Element 后台管理源码是一个基于Vue.jsElement UI的快速开发平台,用于构建现代化的响应式管理系统。它提供了丰富的组件和解决方案,帮助开发者快速搭建高质量的后台管理界面。 该源码具有以下主要特点: 1. 响应式布局:根据屏幕尺寸自动调整布局,使得后台管理系统在不同设备上都能够适应。 2. 多功能组件:提供了丰富的Element UI组件,包括表格表单、图表、对话框等,能够满足各种常见的后台管理功能需求。 3. 可扩展性:源码采用模块化开发,提供了清晰的目录结构和组件划分,开发者可以根据自己的需求进行定制和扩展。 4. 路由管理:源码使用Vue Router进行路由管理,支持动态路由和嵌套路由,可以方便地实现导航菜单和页面的跳转。 5. 权限控制:提供了基于角色的权限控制功能,开发者可以根据用户角色进行页面和功能的权限管理。 6. 数据交互:通过封装axios库,实现了与后端接口的数据交互,并提供了统一的请求拦截和响应处理机制。 7. 国际化支持:支持多语言切换,可以根据用户的语言设置显示不同的界面内容。 总之,Vue Element 后台管理源码是一个功能强大、易于使用和扩展的开发工具,适用于构建中大型的后台管理系统。开发者可以基于这个源码快速搭建出具有良好用户体验的后台管理界面。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值