笔记:elment plus table/form 动态添加行 可编辑提交表单

如图👇

复制代码
<template>
    <NormalDialog ref="dialogRef" :visiable="centerDialogVisible" title="施工计划在线维护" @update:visible="centerDialogVisible = false" :isShowScreenBtn="true" dialogWidth='1100px' :contentStyle="{ 'min-height': '150px' }">
      <template #content>
        <div class="main">
          <el-button size="small" type="primary" style="padding: 7px 11px;" @click="addMasterTask" v-if="editable">添加主任务</el-button>
          <el-table
            :key="tKey"
            ref="xSortTable"
            :data="tableData"
            style="width: 100%;"
            row-key="id"
            v-loading="loading"
            default-expand-all
            :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
            header-row-class-name="xmlist-head-tr-class"
            row-class-name="xmlist-head-tr-class"
            :border="true"
            class="xmlist-table">
            <el-table-column width="50" type="" header-align="center" align="center" v-if="editable">
              <template #header>
                <el-tooltip effect="dark" content="按住后可以上下拖动排序!" placement="top">
                  <el-icon><WarningFilled /></el-icon>
                </el-tooltip>
              </template>
              <template #default>
                <div class="drag-btn">
                  <el-icon><Rank /></el-icon>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="pmPrjInfo.agreementCode" label="任务名称" min-width="200" header-align="center" align="left">
              <template #default="scope">
                <el-input v-if="editable" v-model="scope.row.taskName" size="small" :maxlength="40"></el-input>
                <div style="flex: 1" v-else>{{ scope.row.taskName }}</div>
              </template>
            </el-table-column>
            <el-table-column label="预计开始时间" min-width="190" header-align="center" align="left">
              <template #default="scope">
                <el-date-picker type="date" v-if="editable" v-model="scope.row.startDate" size="small" value-format="YYYY-MM-DD"></el-date-picker>
                <div style="flex: 1" v-else>{{ scope.row.startDate }}</div>
              </template>
            </el-table-column>
            <el-table-column label="预计结束时间" min-width="190" header-align="center" align="left">
              <template #default="scope">
                <el-date-picker v-if="editable" type="date" v-model="scope.row.endDate" size="small" value-format="YYYY-MM-DD"></el-date-picker>
                <div style="flex: 1" v-else>{{ scope.row.endDate }}</div>
              </template>
            </el-table-column>
            <el-table-column label="进度(%)" min-width="190" header-align="center" align="center">
              <template #default="scope">
                <el-input-number v-if="editable" :precision="0" :controls="false" size="small" :max="100" :min="0" v-model="scope.row.progress" style="width: 100%"></el-input-number>
                <div style="flex: 1" v-else>{{ scope.row.progress }}11</div>
              </template>
            </el-table-column>
            <el-table-column prop="status" label="完成状态" min-width="190" header-align="center" align="center">
              <template #default="scope">
                <div style="flex: 1">{{ scope.row.status }}</div>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="150" header-align="center" align="center" fixed="right" v-if="editable">
              <template #default="scope">
                <div style="flex:1">
                  <el-button type="primary" link size="small" v-if="scope.row.parent == 0||(scope.row.children&&scope.row.children.length>0)" @click="addTask(scope.row)">添加子任务</el-button>
                  <el-button type="danger" link size="small" @click="delRow(scope.row)">删除</el-button>
                </div>
              </template>
            </el-table-column>
          </el-table>
          <div class="searchLine">
            <div class="searchBtn" @click="submit" v-if="editable">提交</div>
          </div>
        </div>
      </template>
    </NormalDialog>
</template>

<script setup lang="ts">
import { ref, watch, nextTick, onMounted,toRaw } from 'vue'
import Sortable from "sortablejs"
import { cloneDeep } from 'lodash-es';
import { v1 as uuidv1 } from "uuid"
import { ElMessage } from 'element-plus'
import { NormalDialog } from '@code-front/dynamic-form';
import { Rank, WarningFilled } from '@element-plus/icons-vue'
import { saveSgjh,getSgjh } from "@/service/api/onsale"
interface Props {
  tasks?: {
    data: any[]
  }
  editable?: boolean,
  prjCode: string
}

const props = withDefaults(defineProps<Props>(), {
  tasks: () => ({ data: [] }),
  editable: false,
  prjCode:""
})

const emit = defineEmits(['submitGant'])
const loading = ref(false)
const tKey = ref(0)
const tableData = ref([])
const activeRows = ref([])
const xSortTable = ref()
const centerDialogVisible = ref(false)
const dialogRef = ref()

// watch([props.prjCode], (oldValue, newValue) => {
//   console.log(newValue,newValue,newValue);
//   newValue&&getSgjhFn(newValue)
// }, { deep: true })

const addMasterTask = () => {
  const obj = { id: uuidv1().replace(/-/g, ''), parent: "0",prjCode: props.prjCode }
  tableData.value.push(obj)
  nextTick(() => {
    tKey.value++
    rowDrop()
  })
}

const cancelFn = ($evnt) => {
  console.log($evnt);
}

const addTask = (row: any) => {
  const obj = { id: uuidv1().replace(/-/g, ''), parent: row.id,prjCode: props.prjCode }
  tableData.value.forEach(v => {
    if (v.id === row.id) {
      v.children.push(obj)
    }
  })
  nextTick(() => {
    tKey.value++
    rowDrop()
  })
}

const findIdIndexToDelFn = (list,targetId) => { 
  return list.filter(item => item.id !== targetId).map(v => ({
    ...v,
    children: findIdIndexToDelFn(v.children || [], targetId)
  }))
}

const swapIntegers = (list, targetindex) => { 
  let result = null;
  list.forEach((item,index) => {
    if(index == targetindex){
      result = item
    }
    if (condition) {
      
    }
  })
  return result
  return list.filter((item,index) => index == targetindex).map(v => ({
    ...v,
    children: findIdFn(v.children || [], targetindex)
  }))
}


const delRow = (row: any) => {
  tableData.value = findIdIndexToDelFn(tableData.value,row.id)
  nextTick(() => {
    tKey.value++
    rowDrop()
  })
}

const flatToTree = (flatData: any[], rootId = "0") => {
  const buildTree = (arr: any[], parent: string) => {
    const tree = []
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].parent == parent) {
        const node = { ...arr[i] }
        node.children = []
        const children = buildTree(arr, arr[i].id)
        if (children.length > 0) {
          node.children = children
        }
        tree.push(node)
      }
    }
    return tree
  }
  return buildTree(flatData, rootId)
}

const flatData = (source: any[]) => {
  let result = toRaw(source)
  let res: any[] = []
  result.forEach(el => {
    res.push(el)
    el.children && res.push(...flatData(el.children))
  })
  return res
}

const rowDrop = () => {
  nextTick(() => {
    const tbody = xSortTable.value.$el.querySelector('.prod-table__body-wrapper tbody')
    Sortable.create(tbody, {
      handle: '.drag-btn',
      animation: 200,
      onMove: ({ dragged, related }: any) => {
        const currentresult = flatData(tableData.value)
        const oldRow = currentresult[dragged.rowIndex]
        const newRow = currentresult[related.rowIndex]
        if (oldRow.parent === newRow.id || oldRow.id === newRow.parent) {
          return false
        }
      },
      onEnd: ({ newIndex, oldIndex }: any) => {
        activeRows.value = flatData(tableData.value)
        const currRow = activeRows.value.splice(oldIndex, 1)[0]
        activeRows.value.splice(newIndex, 0, currRow)
        tableData.value = flatToTree(activeRows.value)
        nextTick(() => {
          tKey.value++
          rowDrop()
        })
      }
    })
  })
}

const submit = () => {
  if (tableData.value.length === 0) {
    return ElMessage.warning("请添加任务")
  }
  let submitData = cloneDeep(tableData.value)
  // 设置进度
  const deepSetId = (arr: any[], pId: number | string) => {
    for (let i = 0; i < arr.length; i++) {
      if (pId === 0) {
        arr[i].id = i + 1
      } else {
        arr[i].id = `${pId}.${i + 1}`
      }
      arr[i]['parent'] = pId
      let status = null
      if (arr[i].taskProgress > 0 && arr[i].taskProgress < 100) {
        status = '进行中'
      } else if (arr[i].taskProgress == 100) {
        status = '已完成'
      } else {
        status = '未进行'
      }
      arr[i]['status'] = status

      if (arr[i].children) {
        deepSetId(arr[i].children, arr[i].id)
      }
    }
  }
  deepSetId(submitData, 0)

  let bkData = flatData(submitData)
  if (bkData.some(item => !item.taskName)) {
    ElMessage.warning("请填写任务名称")
    return
  }
  if (bkData.some(item => !item.startDate)) {
    ElMessage.warning("请填预计开始时间")
    return
  }
  if (bkData.some(item => !item.endDate)) {
    ElMessage.warning("请填写预计结束时间")
    return
  }
  saveSgjhFn(submitData)
}

// 保存计划实施
const saveSgjhFn = async (submitData) => {
  const params = {
    planList:getDataFilter(submitData)
  }
  const res = await saveSgjh(params)
  console.log(res.response.data);
  const { code,msg } =  res.response.data
  if (code == 0) {
    ElMessage.success("新建成功")
    tableData.value = []
    dialogRef.value?.closeDialog()
  } else {
    ElMessage.success(msg)
  }
}

// 获取详情
// 获取详情filter
const setDataFilter = (data) => {
  return data.map(v => {
    if (v.children && v.children.length > 0) {
      return {
        ...v,
        parent:v.parentId,
        children: setDataFilter(v.children)
      }
    } else {
      return {
        ...v,
        parent:v.parentId,
        children: []
      }
    }
  })
}
const getSgjhFn = (prjCode) => {
  loading.value = true;
  getSgjh({
    prjCode
  }).then((res) => {
    loading.value = false;
    const { code,body} = res.response.data
    if (code == 0) {
      tableData.value = setDataFilter(body.itemList)
      nextTick(() => {
        tKey.value++
        rowDrop()
      })
    }
  })
}

//
const getDataFilter = (data) => {
  return data.map(v => {
    if (v.children && v.children.length > 0) {
      return {
        prjCode:v.prjCode,
        taskName:v.taskName,
        progress:v.progress,
        status:v.status,
        startDate:v.startDate,
        endDate:v.endDate,
        children: getDataFilter(v.children)
      }
    } else {
      return {
        prjCode:v.prjCode,
        taskName:v.taskName,
        progress:v.progress,
        status:v.status,
        startDate:v.startDate,
        endDate:v.endDate,
        children: []
      }
    }
  })
}

onMounted(() => {
  rowDrop()
})

const open = ()=>{
  centerDialogVisible.value = true
  getSgjhFn(props.prjCode)
}

defineExpose({
  open
})
</script>
<style lang="scss" scoped>
.searchLine {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}

.searchBtn {
  width: 80px;
  height: 32px;
  font-size: 15px;
  font-weight: 500;
  color: #FFFFFF;
  background: #4A94FF;
  border-radius: 4px;
  line-height: 32px;
  text-align: center;
  cursor: pointer;
  margin-left: 10px;
  margin-top: 10px;
}
.main{
  box-sizing: border-box;
  padding: 10px 10px;
}

::v-deep(.cell) {
  display: flex;
  align-items: center;
}

.drag-btn {
  cursor: move;
  font-size: 12px;
  flex: 1;
}

.sortable-ghost,
.sortable-chosen {
  background-color: #dfecfb;
}
</style>
相关推荐
weifont22 分钟前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情27 分钟前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
孤寂大仙v33 分钟前
【Linux笔记】——进程信号的产生
linux·服务器·笔记
几何心凉1 小时前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js
愚戏师1 小时前
Linux复习笔记(三) 网络服务配置(web)
linux·运维·笔记
小堃学编程1 小时前
前端学习(1)—— 使用HTML编写一个简单的个人简历展示页面
前端·javascript·html
scdifsn2 小时前
动手学深度学习12.4.硬件-笔记&练习(PyTorch)
pytorch·笔记·深度学习·缓存·内存·硬盘·深度学习硬件
北温凉2 小时前
【学习笔记】机器学习(Machine Learning) | 第六章(2)| 过拟合问题
笔记·机器学习
懒羊羊我小弟2 小时前
使用 ECharts GL 实现交互式 3D 饼图:技术解析与实践
前端·vue.js·3d·前端框架·echarts
运维@小兵2 小时前
vue访问后端接口,实现用户注册
前端·javascript·vue.js