笔记: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>
相关推荐
Mintopia4 分钟前
AIGC 训练数据的隐私保护技术:联邦学习在 Web 场景的落地
前端·javascript·aigc
鹏多多7 分钟前
React项目集成苹果登录react-apple-signin-auth插件手把手指南
前端·javascript·react.js
初九之潜龙勿用14 分钟前
技术与情感交织的一生 (十四)
笔记·印象笔记
love530love18 分钟前
【笔记】 Podman Desktop 中部署 Stable Diffusion WebUI (GPU 支持)
人工智能·windows·笔记·python·容器·stable diffusion·podman
笔尖的记忆19 分钟前
浏览器的观察者
前端·javascript
高热度网19 分钟前
初始化electron项目运行后报错 electron uninstall 解决方法
前端·javascript
我的写法有点潮21 分钟前
竟然被element-plus背刺了
前端·javascript·vue.js
岁月宁静24 分钟前
AI 聊天消息长列表性能优化:后端分页 + 前端虚拟滚动
前端·vue.js·人工智能
Olrookie26 分钟前
若依前后端分离版学习笔记(十九)——导入,导出实现流程及图片,文件组件
前端·vue.js·笔记
初圣魔门首席弟子28 分钟前
C++ STL string(字符串)学习笔记
c++·笔记·学习