ruoyi自定义工作流(前端二,添加流程管理页面)

4 篇文章 0 订阅
3 篇文章 0 订阅

添加流程管理页面包括三部分

引入api代码
引入主页面代码
添加菜单

引入api代码(fp放在api目录下)

引入api

  1. exam.js
import request from '@/utils/request'

// 查询流程节点与审批关联列表
export function listExam(query) {
  return request({
    url: '/fp/exam/list',
    method: 'get',
    params: query
  })
}

// 查询流程节点与审批关联详细
export function getExam(nodeId) {
  return request({
    url: '/fp/exam/' + nodeId,
    method: 'get'
  })
}

// 新增流程节点与审批关联
export function addExam(data) {
  return request({
    url: '/fp/exam',
    method: 'post',
    data: data
  })
}

// 修改流程节点与审批关联
export function updateExam(data) {
  return request({
    url: '/fp/exam',
    method: 'put',
    data: data
  })
}

// 删除流程节点与审批关联
export function delExam(nodeId) {
  return request({
    url: '/fp/exam/' + nodeId,
    method: 'delete'
  })
}

// 导出流程节点与审批关联
export function exportExam(query) {
  return request({
    url: '/fp/exam/export',
    method: 'get',
    params: query
  })
}
  1. group.js
import request from '@/utils/request'

// 查询流程分组列表
export function listGroup(query) {
  return request({
    url: '/transmonitor/group/list',
    method: 'get',
    params: query
  })
}

// 查询流程分组列表不分页
export function allGroup(query) {
  return request({
    url: '/fp/group/all',
    method: 'get',
    params: query
  })
}

// 查询流程分组详细
export function getGroup(id) {
  return request({
    url: '/fp/group/' + id,
    method: 'get'
  })
}

// 新增流程分组
export function addGroup(data) {
  return request({
    url: '/fp/group',
    method: 'post',
    data: data
  })
}

// 分组排序
export function sortGroup(data) {
  return request({
    url: '/fp/group/sort',
    method: 'post',
    data: data
  })
}

// 修改流程分组
export function updateGroup(data) {
  return request({
    url: '/fp/group',
    method: 'put',
    data: data
  })
}

// 删除流程分组
export function delGroup(id) {
  return request({
    url: '/fp/group/' + id,
    method: 'delete'
  })
}

// 导出流程分组
export function exportGroup(query) {
  return request({
    url: '/fp/group/export',
    method: 'get',
    params: query
  })
}

  1. mount.js
import request from '@/utils/request'

// 查询流程挂载列表
export function listMount(query) {
  return request({
    url: '/fp/mount/list',
    method: 'get',
    params: query
  })
}

// 查询流程挂载详细
export function getMount(busCode) {
  return request({
    url: '/fp/mount/' + busCode,
    method: 'get'
  })
}

// 新增流程挂载
export function addMount(data) {
  return request({
    url: '/fp/mount',
    method: 'post',
    data: data
  })
}

// 修改流程挂载
export function updateMount(data) {
  return request({
    url: '/fp/mount',
    method: 'put',
    data: data
  })
}

// 删除流程挂载
export function delMount(busCode) {
  return request({
    url: '/fp/mount/' + busCode,
    method: 'delete'
  })
}

// 导出流程挂载
export function exportMount(query) {
  return request({
    url: '/fp/mount/export',
    method: 'get',
    params: query
  })
}

  1. node.js
import request from '@/utils/request'

// 查询流程节点列表
export function listNode(query) {
  return request({
    url: '/fp/node/list',
    method: 'get',
    params: query
  })
}

// 查询流程回调处理列表 不分页
export function allCallback(query) {
  return request({
    url: '/fp/callback/all',
    method: 'get',
    params: query
  })
}

// 查询流程进度
export function listPs(query) {
  return request({
    url: '/fp/busNode/query',
    method: 'get',
    params: query
  })
}

// 查询流程节点详细
export function getNode(nodeId) {
  return request({
    url: '/fp/node/' + nodeId,
    method: 'get'
  })
}

// 查询流程节点及操作权限
export function listNodeDatas(proId) {
  return request({
    url: '/fp/node/datas/' + proId,
    method: 'get'
  })
}

// 新增流程节点
export function addNode(data) {
  return request({
    url: '/fp/node',
    method: 'post',
    data: data
  })
}

// 新增流程节点
export function insertNode(data) {
  return request({
    url: '/fp/node/insert',
    method: 'post',
    data: data
  })
}

// 修改流程节点
export function updateNode(data) {
  return request({
    url: '/fp/node',
    method: 'put',
    data: data
  })
}

// 删除流程节点
export function delNode(nodeId) {
  return request({
    url: '/fp/node/' + nodeId,
    method: 'delete'
  })
}

// 导出流程节点
export function exportNode(query) {
  return request({
    url: '/fp/node/export',
    method: 'get',
    params: query
  })
}

  1. proce.js
import request from '@/utils/request'

// 查询流程定义列表
export function listProce(query) {
  return request({
    url: '/fp/proce/list',
    method: 'get',
    params: query
  })
}

// 查询流程定义列表不分页
export function allProce(query) {
  return request({
    url: '/fp/proce/all',
    method: 'get',
    params: query
  })
}

// 查询流程定义详细
export function getProce(proId) {
  return request({
    url: '/fp/proce/' + proId,
    method: 'get'
  })
}

// 新增流程定义
export function addProce(data) {
  return request({
    url: '/fp/proce',
    method: 'post',
    data: data
  })
}

// 停用或启用流程
export function setStatus(data) {
  return request({
    url: '/fp/proce/setStatus',
    method: 'post',
    data: data
  })
}

// 修改流程定义
export function updateProce(data) {
  return request({
    url: '/fp/proce',
    method: 'put',
    data: data
  })
}

// 删除流程定义
export function delProce(proId) {
  return request({
    url: '/fp/proce/' + proId,
    method: 'delete'
  })
}

// 导出流程定义
export function exportProce(query) {
  return request({
    url: '/fp/proce/export',
    method: 'get',
    params: query
  })
}

// 预览流程节点
export function queryPs(query) {
  return request({
    url: '/fp/proce/query',
    method: 'get',
    params: query
  })
}

  1. start.js
import request from '@/utils/request'

// 查询我的申请
export function listApply(query) {
  return request({
    url: '/fp/start/list',
    method: 'get',
    params: query
  })
}
// 查询抄送我的
export function listCcme(query) {
  return request({
    url: '/fp/start/ccme',
    method: 'get',
    params: query
  })
}
// 查询由我参与的
export function listBmpt(query) {
  return request({
    url: '/fp/start/bmpt',
    method: 'get',
    params: query
  })
}

// 查询流程节点列表
export function listStart(query) {
  return request({
    url: '/fp/start/query',
    method: 'get',
    params: query
  })
}

// 流程节点操作
export function executorFp(data) {
  return request({
    url: '/fp/start/executorFp',
    method: 'post',
    data: data
  })
}


// 流程启动
export function startFp(data) {
  return request({
    url: '/fp/start/startFp',
    method: 'post',
    data: data
  })
}


// 流程重新发起
export function initiateFp(data) {
  return request({
    url: '/fp/start/initiateFp',
    method: 'post',
    data: data
  })
}

// 流程驳回
export function rejectedFp(data) {
  return request({
    url: '/fp/start/rejectedFp',
    method: 'post',
    data: data
  })
}


引入主页面代码(fp放在views目录下)

主要页面

  1. apply–>index.vue
<template>
  <div class="app-container">
    <div v-if="flag == '1'">
      <p style="font-size: 18px; font-weight: bold;">我的申请</p>
      <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
        <el-form-item label="所属流程" prop="proId">
          <el-input
            v-model="queryParams.proId"
            placeholder="请输入所属流程"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="发起时间" prop="subTime">
          <el-input
            v-model="queryParams.subTime"
            placeholder="请输入发起时间"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>

      <el-table v-loading="loading" :data="startList" border>
        <el-table-column label="流程" align="center" prop="proName">
          <template slot-scope="scope">
            <el-link :underline="false" type="primary" @click="openFpDesc(scope.row)">{{scope.row.proName}}</el-link>
          </template>
        </el-table-column>
        <el-table-column label="状态" align="center" prop="proGoStatus">
          <template slot-scope="scope">
            <el-tag color="#f8eaec" v-if="scope.row.proGoStatus == '5'">已转交</el-tag>
            <el-tag type="info" v-if="scope.row.proGoStatus == '4'">已撤回</el-tag>
            <el-tag type="danger" v-if="scope.row.proGoStatus == '3'">已驳回</el-tag>
            <el-tag type="success" v-if="scope.row.proGoStatus == '2'">已完成</el-tag>
            <el-tag type="warning" v-if="scope.row.proGoStatus == '1'">审批中</el-tag>
            <el-tag v-if="scope.row.proGoStatus == '0'">未审批</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="待谁处理" align="center">
          <template slot-scope="scope">
            <span style="color: #CCAD6F;"
                  v-if="scope.row.todoUserIdDictText">{{scope.row.todoUserIdDictText}}等用户处理中...</span>
            <span style="color: #CCAD6F;"
                  v-if="scope.row.todoRoleIdDictText">{{scope.row.todoRoleIdDictText}}等角色处理中...</span>
            <span v-if="!scope.row.todoRoleIdDictText && !scope.row.todoUserIdDictText">流程已结束</span>
          </template>
        </el-table-column>
        <el-table-column label="所属业务类型" align="center" prop="busType">
          <template slot-scope="scope">
            <el-tag>一般业务</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="110">
          <template slot-scope="scope">
            <el-button
              v-if="scope.row.proGoStatus != '2'"
              size="mini"
              type="text"
              icon="el-icon-refresh-left"
              @click="initiateFpStatus(scope.row)"
            >撤回
            </el-button>
            <el-divider v-if="scope.row.proGoStatus != '2'" direction="vertical"></el-divider>
            <!--     <el-button
                   v-if="scope.row.proGoStatus == '2' || scope.row.proGoStatus == '3'"
                   size="mini"
                   type="text"
                   icon="el-icon-thumb"
                 >重新申请
                 </el-button>-->
          </template>
        </el-table-column>
      </el-table>

      <pagination
        v-show="total>0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />

    </div>

    <!--审批详情-->
    <div v-if="flag == '2'">
      <el-row>
        <el-col :span="24">
          <el-card shadow="never" style="height: 62px;padding-top: 8px;">
            <el-row>
              <el-col :span="9">
                <el-button style="margin-top: -16px;" icon="el-icon-arrow-left" @click="callMainPage">返回</el-button>
              </el-col>
              <el-col :span="12">
                <p style="display: inline;">{{actionRow.groupName}}</p>
              </el-col>
              <el-col :span="3" style="margin-top: -10px;">
                <div style="float: right;margin-right: -4%;">
                  <el-button style="margin-right: -2%" v-if="!disType"
                             @click="subForm">提交
                  </el-button>
                  <el-button style="margin-right: -2%" type="primary" v-if="actionRow.proGoStatus == '3'"
                             @click="applyFor">操作
                  </el-button>
                </div>
              </el-col>
            </el-row>
          </el-card>
          <el-card shadow="never">
            <el-row type="flex">
              <el-col :span="20" style="margin-right: 40px;">
                <parser :form-conf="formConf"></parser>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">状态</p>
                <el-tag color="#f8eaec" v-if="actionRow.proGoStatus == '5'">已转交</el-tag>
                <el-tag type="info" v-if="actionRow.proGoStatus == '4'">已撤回</el-tag>
                <el-tag type="danger" v-if="actionRow.proGoStatus == '3'">已驳回</el-tag>
                <el-tag type="success" v-if="actionRow.proGoStatus == '2'">已完成</el-tag>
                <el-tag type="warning" v-if="actionRow.proGoStatus == '1'">审批中</el-tag>
                <el-tag v-if="actionRow.proGoStatus == '0'">未审批</el-tag>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">提交时间</p>
                <p style="font-size: 13px;">{{actionRow.subTime}}</p>
              </el-col>
            </el-row>
          </el-card>
          <el-card class="fp-card">
            <div slot="header" class="clearfix">
              <span>流程进度</span>
            </div>
            <div class="text item">
              <fp-time-line ref="ftl" :activities="activities"></fp-time-line>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
  import Parser from 'form-gen-parser'
  import { listApply, initiateFp, rejectedFp } from '@/api/fp/start'
  import { listPs } from '@/api/fp/node'
  import FpTimeLine from '../model/tl/index'

  export default {
    name: 'Apply',
    components: { Parser, FpTimeLine },
    data() {
      return {
        // 遮罩层
        loading: true,
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 是否禁用下拉框
        disType: true,
        // 总条数
        total: 0,
        // 流程启动信息表格数据
        startList: [],
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          proId: null,
          subTime: null
        },
        flag: '1',
        actionRow: {},
        formConf: {},
        activities: [],
        selectRow: [],
        setForm: { zf: 0 },
        taskId: ''
      }
    },
    created() {
      this.getList()
    },
    methods: {
      /** 查询流程启动信息列表 */
      getList() {
        this.loading = true
        listApply(this.queryParams).then(response => {
          this.startList = response.rows
          this.total = response.total
          this.loading = false
        })
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      callMainPage() {
        this.flag = '1'
        this.actionRow = {}
        this.activities = []
      },
      /** 打开审批详情*/
      openFpDesc(row) {
        this.actionRow = row
        //回显数据
        this.formData = JSON.parse(row.formData)
        this.formConf = JSON.parse(row.formConf)
        this.callShowData()
        this.flag = '2'
        this.queryProcessSchedule()
      },
      queryProcessSchedule() {
        listPs({ startId: this.actionRow.id }).then(res => {
          if (res.data) {
            this.activities = res.data
          }
        })
      },
      callShowData() {
        this.formConf.fields.map(item => {
          var __config__ = item.__config__
          __config__.defaultValue = this.formData[item.__vModel__] ? this.formData[item.__vModel__] : __config__.defaultValue
          return item
        })
      },
      initiateFpStatus(scope) {
        this.loading = true
        const { id, proId } = scope
        initiateFp({ id, proId }).then(res => {
          this.msgSuccess('操作成功!')
          this.getList()
          this.loading = false
        })
      },
      applyFor() {
        this.disType = !this.disType
      },
      subForm() {
        this.loading = true
        rejectedFp({
          formData: JSON.stringify(this.formData), ...this.formData.fp,
          id: this.actionRow.id
        }).then(res => {
          this.msgSuccess('操作成功!')
          this.callMainPage()
          this.loading = false
          this.getList()
        })
      },
    }
  }
</script>

  1. bmpt —> index.vue
<template>
  <div class="app-container">
    <div v-if="flag == '1'">
      <p style="font-size: 18px; font-weight: bold;">由我参与</p>
      <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
        <el-form-item label="所属流程" prop="proId">
          <el-input
            v-model="queryParams.proId"
            placeholder="请输入所属流程"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="发起时间" prop="subTime">
          <el-input
            v-model="queryParams.subTime"
            placeholder="请输入发起时间"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>

      <el-table v-loading="loading" :data="startList" border>
        <el-table-column label="流程" align="center" prop="proName">
          <template slot-scope="scope">
            <el-link :underline="false" type="primary" @click="openFpDesc(scope.row)">{{scope.row.proName}}</el-link>
          </template>
        </el-table-column>
        <el-table-column label="状态" align="center" prop="proGoStatus">
          <template slot-scope="scope">
            <el-tag color="#f8eaec" v-if="scope.row.proGoStatus == '5'">已转交</el-tag>
            <el-tag type="info" v-if="scope.row.proGoStatus == '4'">已撤回</el-tag>
            <el-tag type="danger" v-if="scope.row.proGoStatus == '3'">已驳回</el-tag>
            <el-tag type="success" v-if="scope.row.proGoStatus == '2'">已完成</el-tag>
            <el-tag type="warning" v-if="scope.row.proGoStatus == '1'">审批中</el-tag>
            <el-tag v-if="scope.row.proGoStatus == '0'">未审批</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="发起人" align="center" prop="proOriginatorDictText"/>
        <el-table-column label="发起时间" align="center" prop="subTime"/>
        <el-table-column label="待谁处理" align="center">
          <template slot-scope="scope">
            <span style="color: #CCAD6F;"
                  v-if="scope.row.todoUserIdDictText">{{scope.row.todoUserIdDictText}}等用户处理中...</span>
            <span style="color: #CCAD6F;"
                  v-if="scope.row.todoRoleIdDictText">{{scope.row.todoRoleIdDictText}}等角色处理中...</span>
            <span v-if="!scope.row.todoRoleIdDictText && !scope.row.todoUserIdDictText">流程已结束</span>
          </template>
        </el-table-column>
        <el-table-column label="所属业务类型" align="center" prop="busType">
          <template slot-scope="scope">
            <el-tag>一般业务</el-tag>
          </template>
        </el-table-column>
      </el-table>

      <pagination
        v-show="total>0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </div>

    <!--审批详情-->
    <div v-if="flag == '2'">
      <el-row>
        <el-col :span="24">
          <el-card shadow="never" style="height: 62px;padding-top: 8px;">
            <el-row>
              <el-col :span="9">
                <el-button style="margin-top: -16px;" icon="el-icon-arrow-left" @click="callMainPage">返回</el-button>
              </el-col>
              <el-col :span="12">
                <p style="display: inline;">{{actionRow.groupName}}</p>
              </el-col>
              <el-col :span="3">
              </el-col>
            </el-row>
          </el-card>
          <el-card shadow="never">
            <el-row type="flex">
              <el-col :span="20" style="margin-right: 40px;">
                <parser :form-conf="formConf"></parser>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">状态</p>
                <el-tag color="#f8eaec" v-if="actionRow.proGoStatus == '5'">已转交</el-tag>
                <el-tag type="info" v-if="actionRow.proGoStatus == '4'">已撤回</el-tag>
                <el-tag type="danger" v-if="actionRow.proGoStatus == '3'">已驳回</el-tag>
                <el-tag type="success" v-if="actionRow.proGoStatus == '2'">已完成</el-tag>
                <el-tag type="warning" v-if="actionRow.proGoStatus == '1'">审批中</el-tag>
                <el-tag v-if="actionRow.proGoStatus == '0'">未审批</el-tag>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">提交时间</p>
                <p style="font-size: 13px;">{{actionRow.subTime}}</p>
              </el-col>
            </el-row>
          </el-card>
          <el-card class="fp-card">
            <div slot="header" class="clearfix">
              <span>流程进度</span>
            </div>
            <div class="text item">
              <fp-time-line ref="ftl" :activities="activities"></fp-time-line>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>

  </div>
</template>

<script>
  import Parser from 'form-gen-parser'
  import { listBmpt } from '@/api/fp/start'
  import { listPs } from '@/api/fp/node'
  import FpTimeLine from '../model/tl/index'

  export default {
    name: 'Bmpt',
    components: { Parser, FpTimeLine },
    data() {
      return {
        // 遮罩层
        loading: true,
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 总条数
        total: 0,
        // 流程启动信息表格数据
        startList: [],
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          proId: null,
          subTime: null
        },
        flag: '1',
        actionRow: {},
        formConf: {},
        setForm: { zf: 0 },
        activities: [],
        selectRow: [],
        taskId: ''
      }
    },
    created() {
      this.getList()
    },
    methods: {
      /** 查询流程启动信息列表 */
      getList() {
        this.loading = true
        listBmpt(this.queryParams).then(response => {
          this.startList = response.rows
          this.total = response.total
          this.loading = false
        })
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      callMainPage() {
        this.flag = '1'
        this.actionRow = {}
        this.activities = []
      },
      /** 打开审批详情*/
      openFpDesc(row) {
        this.actionRow = row
        this.formData = JSON.parse(row.formData)
        //回显数据
        this.formConf = JSON.parse(row.formConf)
        this.callShowData()
        this.flag = '2'
        this.queryProcessSchedule()
      },
      queryProcessSchedule() {
        listPs({ startId: this.actionRow.id }).then(res => {
          if (res.data) {
            this.activities = res.data
          }
        })
      },
      callShowData() {
        this.formConf.fields.map(item => {
          var __config__ = item.__config__
          __config__.defaultValue = this.formData[item.__vModel__] ? this.formData[item.__vModel__] : __config__.defaultValue
          return item
        })
      },
    }
  }
</script>

  1. ccme —> index.vue
<template>
  <div class="app-container">
    <div v-if="flag == '1'">
      <p style="font-size: 18px; font-weight: bold;">抄送我的</p>
      <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
        <el-form-item label="所属流程" prop="proId">
          <el-input
            v-model="queryParams.proId"
            placeholder="请输入所属流程"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="发起时间" prop="subTime">
          <el-input
            v-model="queryParams.subTime"
            placeholder="请输入发起时间"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>

      <el-table v-loading="loading" :data="startList" border>
        <el-table-column label="流程" align="center" prop="proName">
          <template slot-scope="scope">
            <el-link :underline="false" type="primary" @click="openFpDesc(scope.row)">{{scope.row.proName}}</el-link>
          </template>
        </el-table-column>
        <el-table-column label="状态" align="center" prop="proGoStatus">
          <template slot-scope="scope">
            <el-tag color="#f8eaec" v-if="scope.row.proGoStatus == '5'">已转交</el-tag>
            <el-tag type="info" v-if="scope.row.proGoStatus == '4'">已撤回</el-tag>
            <el-tag type="danger" v-if="scope.row.proGoStatus == '3'">已驳回</el-tag>
            <el-tag type="success" v-if="scope.row.proGoStatus == '2'">已完成</el-tag>
            <el-tag type="warning" v-if="scope.row.proGoStatus == '1'">审批中</el-tag>
            <el-tag v-if="scope.row.proGoStatus == '0'">未审批</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="发起时间" align="center" prop="subTime"/>
        <el-table-column label="所属业务类型" align="center" prop="busType">
          <template slot-scope="scope">
            <el-tag>一般业务</el-tag>
          </template>
        </el-table-column>

      </el-table>

      <pagination
        v-show="total>0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </div>

    <!--审批详情-->
    <div v-if="flag == '2'">
      <el-row>
        <el-col :span="24">
          <el-card shadow="never" style="height: 62px;padding-top: 8px;">
            <el-row>
              <el-col :span="9">
                <el-button style="margin-top: -16px;" icon="el-icon-arrow-left" @click="callMainPage">返回</el-button>
              </el-col>
              <el-col :span="12">
                <p style="display: inline;line-height: 35px;">{{actionRow.groupName}}</p>
              </el-col>
              <el-col :span="3">
              </el-col>
            </el-row>
          </el-card>
          <el-card shadow="never">
            <el-row type="flex">
              <el-col :span="20" style="margin-right: 40px;">
                <parser :form-conf="formConf"></parser>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">状态</p>
                <el-tag color="#f8eaec" v-if="actionRow.proGoStatus == '5'">已转交</el-tag>
                <el-tag type="info" v-if="actionRow.proGoStatus == '4'">已撤回</el-tag>
                <el-tag type="danger" v-if="actionRow.proGoStatus == '3'">已驳回</el-tag>
                <el-tag type="success" v-if="actionRow.proGoStatus == '2'">已完成</el-tag>
                <el-tag type="warning" v-if="actionRow.proGoStatus == '1'">审批中</el-tag>
                <el-tag v-if="actionRow.proGoStatus == '0'">未审批</el-tag>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">提交时间</p>
                <p style="font-size: 13px;">{{actionRow.subTime}}</p>
              </el-col>
            </el-row>
          </el-card>
          <el-card class="fp-card">
            <div slot="header" class="clearfix">
              <span>流程进度</span>
            </div>
            <div class="text item">
              <fp-time-line ref="ftl" :activities="activities"></fp-time-line>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>

  </div>
</template>

<script>
  import Parser from 'form-gen-parser'
  import { listCcme } from '@/api/fp/start'
  import { listPs } from '@/api/fp/node'
  import FpTimeLine from '../model/tl/index'

  export default {
    name: 'Ccme',
    components: { Parser, FpTimeLine },
    data() {
      return {
        // 遮罩层
        loading: true,
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 总条数
        total: 0,
        // 流程启动信息表格数据
        startList: [],
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          proId: null,
          subTime: null
        },
        flag: '1',
        actionRow: {},
        formConf: {},
        setForm: { zf: 0 },
        activities: [],
        selectRow: [],
        taskId: ''
      }
    },
    created() {
      this.getList()
    },
    methods: {
      /** 查询流程启动信息列表 */
      getList() {
        this.loading = true
        listCcme(this.queryParams).then(response => {
          this.startList = response.rows
          this.total = response.total
          this.loading = false
        })
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      callMainPage() {
        this.flag = '1'
        this.actionRow = {}
        this.activities = []
      },
      /** 打开审批详情*/
      openFpDesc(row) {
        this.actionRow = row
        this.formData = JSON.parse(row.formData)
        //回显数据
        this.formConf = JSON.parse(row.formConf)
        this.callShowData()

        this.flag = '2'
        this.queryProcessSchedule()
      },
      queryProcessSchedule() {
        listPs({ startId: this.actionRow.id }).then(res => {
          if (res.data) {
            this.activities = res.data
          }
        })
      },
      callShowData() {
        this.formConf.fields.map(item => {
          var __config__ = item.__config__
          __config__.defaultValue = this.formData[item.__vModel__] ? this.formData[item.__vModel__] : __config__.defaultValue
          return item
        })
      }
    }
  }
</script>

  1. model —> tl —>index.vue
<template>
  <ul class="timeline">
    <li class="timeline-item" v-for="(item, index) in activities" :key="item.index">
      <div :class="[{ 'item-tail': true, 'item-tail-1': activities[index+1].status == '1',
         'item-tail-2': activities[index+1].status == '2', 'item-tail-3': activities[index+1].status == '3',
         'item-tail-4': activities[index+1].status == '4', 'item-tail-5': activities[index+1].status == '5',  }]"
           v-if="index != activities.length - 1"></div>
      <div :class="[{'item-node': true, 'item-node-1': item.status == '1' , 'item-node-2': item.status == '2' ,
         'item-node-3': item.status == '3' , 'item-node-4': item.status == '4' , 'item-node-5': item.status == '5' }]"></div>
      <div class="item-wrapper">
        <div class="item-content">{{item.title}}</div>
        <div class="item-time" v-if="item.timestamp">时间:{{ item.timestamp }}</div>
        <div class="item-approver"> {{item.approver}}</div>
        <div v-if="item.remark" class="item-result">理由:
          <div :class="[{'item-remark': item.status != '2','item-remark-2': item.status == '2'}]">
            {{item.remark}}
          </div>
        </div>
      </div>
      <fp-time-line-child v-if="item.pros && item.pros.length > 0" :childs="item.pros"></fp-time-line-child>
    </li>
  </ul>
</template>
<script>

  import FpTimeLineChild from './child'

  export default {
    name: 'FpTimeLine',
    components: { FpTimeLineChild },
    props: {
      activities: {
        default: function(arr) {
          return arr
        }
      }
    },
    data() {
      return {}
    }
  }
</script>

<style scoped lang='scss'>
  .timeline {
    margin: 0;
    font-size: 18px;
    list-style: none;
    background-color: #fff;
    width: 100%;
    height: 100vh;
    margin: 20px;
    overflow: auto;

    .timeline-item {
      position: relative;
      padding-bottom: 50px;

      .item-tail {
        position: absolute;
        left: 4px;
        height: 100%;
        border-left: 2px solid #e4e7ed;
      }

      .item-tail-1 {
        border-left: 2px solid #13ce66;
      }

      .item-tail-2 {
        border-left: 2px solid #f56c6c;
      }

      .item-tail-3 {
        border-left: 2px solid #409eff;
      }

      .item-tail-4 {
        border-left: 2px solid pink;
      }

      .item-tail-5 {
        border-left: 2px solid #e6a23c;
      }

      .item-node {
        left: -2px;
        width: 15px;
        height: 15px;
        position: absolute;
        background-color: #e4e7ed;
        border: 2px solid #ccc;
        border-radius: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .item-node-1 {
        border: 2px solid #13ce66;
        background-color: #13ce66;
      }

      .item-node-2 {
        border: 2px solid #f56c6c;
        background-color: #f56c6c;
      }

      .item-node-3 {
        border: 2px solid #409eff;
        background-color: #409eff;
      }

      .item-node-4 {
        border: 2px solid pink;
        background-color: pink;
      }

      .item-node-5 {
        border: 2px solid #e6a23c;
        background-color: #e6a23c;
      }

      .item-wrapper {
        position: relative;
        padding-left: 28px;

        .item-content {
          color: #6395f8;
          padding-top: 10px;
        }

        .item-time {
          margin-top: 8px;
          color: #909399;
          line-height: 1;
          font-size: 13px;
        }

        .item-approver {
          color: #909399;
        }

        .item-remark {
          display: inline;
        }

        .item-remark-2 {
          color: red;
          display: inline;
        }

        .item-result {
          margin-top: 8px;
        }
      }
    }
  }
</style>

  1. model —> tl —> child.vue
<template>
  <div :class="[{'item-top-tail': true, 'item-top-tail-1': childs[0].status == '1',
              'item-top-tail-2': childs[0].status == '2', 'item-top-tail-3': childs[0].status == '3',
              'item-top-tail-4': childs[0].status == '4', 'item-top-tail-5': childs[0].status == '5' }]">
    <ul class="timeline-child">
      <li class="timeline-item" v-for="(item, pindex) in childs" :key="pindex">
        <div :class="[{'item-tail': true, 'item-tail-1': childs[pindex+1].status == '1',
         'item-tail-2': childs[pindex+1].status == '2', 'item-tail-3': childs[pindex+1].status == '3',
         'item-tail-4': childs[pindex+1].status == '4', 'item-tail-5': childs[pindex+1].status == '5' }]"
             v-if="pindex != childs.length - 1"></div>
        <div :class="[{'item-node': true, 'item-node-1': item.status == '1' , 'item-node-2': item.status == '2' ,
         'item-node-3': item.status == '3' , 'item-node-4': item.status == '4' , 'item-node-5': item.status == '5'  }]"></div>
        <div class="item-wrapper">
          <div class="item-content">{{item.title}}</div>
          <div class="item-time" v-if="item.timestamp">时间:{{ item.timestamp }}</div>
          <div class="item-approver"> {{item.approver}}</div>
          <div v-if="item.remark" class="item-result">理由:
            <div :class="[{'item-remark': item.status != '2','item-remark-2': item.status == '2'}]">
              {{item.remark}}</div>
          </div>
        </div>
        <fp-time-line-child v-if="item.pros && item.pros.length > 0" :childs="item.pros"></fp-time-line-child>
      </li>
    </ul>
  </div>
</template>
<script>

  export default {
    name: 'FpTimeLineChild',
    props: {
      childs: {
        default: function(arr) {
          return arr
        }
      }
    },
    data() {
      return {}
    }
  }
</script>

<style scoped lang='scss'>
  .timeline-item {
    position: relative;
    padding-bottom: 50px;
    .item-tail {
      position: absolute;
      left: 4px;
      height: 175px;
      border-left: 2px solid #e4e7ed;
    }

    .item-tail-1 {
      border-left: 2px solid #13ce66;
    }

    .item-tail-2 {
      border-left: 2px solid #f56c6c;
    }

    .item-tail-3 {
      border-left: 2px solid #409eff;
    }

    .item-tail-4 {
      border-left: 2px solid pink;
    }

    .item-tail-5 {
      border-left: 2px solid #e6a23c;
    }

    .item-top-tail {
      position: absolute;
      left: 13px;
      top: 5px;
      width: 370px;
      height: 100%;
      float: left;
      border-top: 2px solid #e4e7ed;

      .timeline-child {
        margin: -6px 60px 0 320px;
        font-size: 18px;
        list-style: none;
        width: 100%;
        height: 100%;
      }
    }

    .item-top-tail-1 {
      border-top: 2px solid #13ce66;
    }

    .item-top-tail-2 {
      border-top: 2px solid #f56c6c;
    }

    .item-top-tail-3 {
      border-top: 2px solid #409eff;
    }

    .item-top-tail-4 {
      border-top: 2px solid pink;
    }

    .item-top-tail-5 {
      border-top: 2px solid #e6a23c;
    }

    .item-node {
      left: -2px;
      width: 15px;
      height: 15px;
      position: absolute;
      background-color: #e4e7ed;
      border: 2px solid #ccc;
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .item-node-1 {
      border: 2px solid #13ce66;
      background-color: #13ce66;
    }

    .item-node-2 {
      border: 2px solid #f56c6c;
      background-color: #f56c6c;
    }

    .item-node-3 {
      border: 2px solid #409eff;
      background-color: #409eff;
    }

    .item-node-4 {
      border: 2px solid pink;
      background-color: pink;
    }

    .item-node-5 {
      border: 2px solid #e6a23c;
      background-color: #e6a23c;
    }


    .item-wrapper {
      position: relative;
      padding-left: 28px;

      .item-content {
        color: #6395f8;
        padding-top: 10px;
      }

      .item-time {
        margin-top: 8px;
        color: #909399;
        line-height: 1;
        font-size: 13px;
      }

      .item-approver{
        color: #909399;
      }

      .item-remark{
        display: inline;
      }

      .item-remark-2{
        color: red;
        display: inline;
      }

      .item-result {
        margin-top: 8px;
      }
    }
  }
</style>

  1. mount —> index.vue
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="挂载流程" prop="proId">
        <el-select
          v-model="queryParams.proId"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
          placeholder="请选择挂载流程">
          <el-option
            v-for="dict in proces"
            :key="dict.proId"
            :label="dict.proName"
            :value="dict.proId"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="挂载状态" prop="mountStatus">
        <el-select
          v-model="queryParams.mountStatus"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
          placeholder="请选择挂载状态">
          <el-option
            v-for="dict in mountStatuss"
            :key="dict.dictValue"
            :label="dict.dictLabel"
            :value="dict.dictValue"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['fp:mount:add']"
        >新增
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['fp:mount:edit']"
        >修改
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['fp:mount:remove']"
        >删除
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" border :data="mountList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center"/>
      <el-table-column label="业务唯一编码" align="center" prop="busCode"/>
      <el-table-column label="挂载流程" align="center" prop="proId" :formatter="formatFp"/>
      <el-table-column label="挂载状态" align="center" prop="mountStatus"
                       :formatter="(v,c) => dictCodeFormat(v,c,mountStatuss)"/>
      <el-table-column label="业务描述" align="center" prop="busDesc"/>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['fp:mount:edit']"
          >修改
          </el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['fp:mount:remove']"
          >删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改流程挂载对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="160px" class="action-form">
        <el-form-item label="业务唯一编码" prop="busCode">
          <el-input v-model="form.busCode" :disabled="actionFlag == 'edit'" placeholder="请输入业务唯一编码"/>
        </el-form-item>
        <el-form-item label="挂载流程" prop="proId">
          <el-select
            v-model="form.proId"
            clearable
            size="small"
            placeholder="请选择挂载流程">
            <el-option
              v-for="dict in proces"
              :key="dict.proId"
              :label="dict.proName"
              :value="dict.proId"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="挂载状态" prop="mountStatus">
          <el-select
            v-model="form.mountStatus"
            clearable
            size="small"
            placeholder="请选择实控人证件类型">
            <el-option
              v-for="dict in mountStatuss"
              :key="dict.dictValue"
              :label="dict.dictLabel"
              :value="dict.dictValue"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="业务描述" prop="busDesc">
          <el-input v-model="form.busDesc" placeholder="请输入业务描述"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
  import { listMount, getMount, delMount, addMount, updateMount, exportMount } from '@/api/fp/mount'
  import { allProce } from '@/api/fp/proce'

  export default {
    name: 'Mount',
    components: {},
    data() {
      let validateBusCode = async (rule, value, callback) => {
        let flag = false
        await getMount(value).then(response => {
          if(response.data && response.data.busCode)flag = true
        })
        if (value === '') {
          callback(new Error('业务唯一编码不能为空!'))
        } else if (flag && this.actionFlag == 'add') {
          callback(new Error('该编码已存在,请更换业务唯一编码!'))
        } else {
          callback()
        }
      }
      return {
        // 遮罩层
        loading: true,
        // 导出遮罩层
        exportLoading: false,
        // 选中数组
        ids: [],
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 总条数
        total: 0,
        // 流程挂载表格数据
        mountList: [],
        // 弹出层标题
        title: '',
        actionFlag: '',//操作标识
        // 是否显示弹出层
        open: false,
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          busCode: null,
          busDesc: null,
          mountStatus: null
        },
        // 表单参数
        form: {},
        // 表单校验
        rules: {
          busCode: [
            { validator: validateBusCode, trigger: 'blur' },
            { required: true, message: '业务唯一编码不能为空!', trigger: 'blur' }
          ],
          proId: [
            { required: true, message: '挂载流程不能为空!', trigger: 'blur' }
          ],
          busDesc: [
            { required: true, message: '业务描述不能为空!', trigger: 'blur' }
          ],
          mountStatus: [
            { required: true, message: '挂载状态不能为空!', trigger: 'blur' }
          ]
        },
        mountStatuss: [],//流程挂载状态字典数据
        proces: []//流程字典数据
      }
    },
    created() {
      this.getList()
      this.initDictConfig()
    },
    methods: {
      /**基本字典类型数据初始化*/
      initDictConfig() {
        this.getDicts('fp_mount_status').then(response => {
          this.mountStatuss = response.data
        })
        allProce({ status: '1' }).then(response => {
          this.proces = response.data
        })
      },
      /** 查询流程挂载列表 */
      getList() {
        this.loading = true
        listMount(this.queryParams).then(response => {
          this.mountList = response.rows
          this.total = response.total
          this.loading = false
        })
      },
      // 取消按钮
      cancel() {
        this.open = false
        this.reset()
      },
      // 表单重置
      reset() {
        this.form = {
          proId: null,
          busCode: null,
          busDesc: null,
          mountStatus: null
        }
        this.resetForm('form')
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      // 多选框选中数据
      handleSelectionChange(selection) {
        this.ids = selection.map(item => item.busCode)
        this.single = selection.length !== 1
        this.multiple = !selection.length
      },
      /** 新增按钮操作 */
      handleAdd() {
        this.reset()
        this.open = true
        this.actionFlag = 'add'
        this.title = '添加流程挂载'
      },
      /** 修改按钮操作 */
      handleUpdate(row) {
        this.reset()
        this.actionFlag = 'edit'
        const busCode = row.busCode || this.ids
        getMount(busCode).then(response => {
          this.form = response.data
          this.open = true
          this.title = '修改流程挂载'
        })
      },
      /** 提交按钮 */
      submitForm() {
        this.$refs['form'].validate(valid => {
          if (valid) {
            if (this.actionFlag == 'edit') {
              updateMount(this.form).then(response => {
                this.msgSuccess('修改成功')
                this.open = false
                this.getList()
              })
            } else if (this.actionFlag == 'add') {
              addMount(this.form).then(response => {
                this.msgSuccess('新增成功')
                this.open = false
                this.getList()
              })
            }
          }
        })
      },
      /** 删除按钮操作 */
      handleDelete(row) {
        const busCodes = row.busCode || this.ids
        this.$confirm('是否确认删除流程挂载编号为"' + busCodes + '"的数据项?', '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(function() {
          return delMount(busCodes)
        }).then(() => {
          this.getList()
          this.msgSuccess('删除成功')
        }).catch(() => {
        })
      },
      /** 导出按钮操作 */
      handleExport() {
        const queryParams = this.queryParams
        this.$confirm('是否确认导出所有流程挂载数据项?', '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.exportLoading = true
          return exportMount(queryParams)
        }).then(response => {
          this.download(response.msg)
          this.exportLoading = false
        }).catch(() => {
        })
      },
      /** 字典翻译 */
      dictCodeFormat(row, column, dictDatas) {
        return this.selectDictLabel(dictDatas, eval('row.' + column.property))
      },
      formatFp(row, column) {
        let proName = ''
        this.proces.map(item => {
          if (item.proId == row.proId) proName = item.proName
        })
        return proName
      }
    }
  }
</script>

<style scoped lang="scss">
  .action-form {
    .el-input {
      width: 410px
    }

    .el-textarea {
      width: 410px
    }

    .el-select {
      width: 410px;
    }
  }

  .el-dropdown {
    font-size: 12px;

    .el-dropdown-link {
      cursor: pointer;
      color: #409EFF;
    }

    .el-icon-arrow-down {
      font-size: 12px;
    }
  }
</style>

  1. proce —> fg ----> CodeTypeDialog.vue
<template>
  <div>
    <el-dialog
      v-bind="$attrs"
      width="500px"
      :close-on-click-modal="false"
      :modal-append-to-body="false"
      v-on="$listeners"
      @open="onOpen"
      @close="onClose"
    >
      <el-row :gutter="15">
        <el-form
          ref="elForm"
          :model="formData"
          :rules="rules"
          size="medium"
          label-width="100px"
        >
          <el-col :span="24">
            <el-form-item label="生成类型" prop="type">
              <el-radio-group v-model="formData.type">
                <el-radio-button
                  v-for="(item, index) in typeOptions"
                  :key="index"
                  :label="item.value"
                  :disabled="item.disabled"
                >
                  {{ item.label }}
                </el-radio-button>
              </el-radio-group>
            </el-form-item>
            <el-form-item v-if="showFileName" label="文件名" prop="fileName">
              <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
            </el-form-item>
          </el-col>
        </el-form>
      </el-row>

      <div slot="footer">
        <el-button @click="close">
          取消
        </el-button>
        <el-button type="primary" @click="handelConfirm">
          确定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  inheritAttrs: false,
  props: ['showFileName'],
  data() {
    return {
      formData: {
        fileName: undefined,
        type: 'file'
      },
      rules: {
        fileName: [{
          required: true,
          message: '请输入文件名',
          trigger: 'blur'
        }],
        type: [{
          required: true,
          message: '生成类型不能为空',
          trigger: 'change'
        }]
      },
      typeOptions: [{
        label: '页面',
        value: 'file'
      }, {
        label: '弹窗',
        value: 'dialog'
      }]
    }
  },
  computed: {
  },
  watch: {},
  mounted() {},
  methods: {
    onOpen() {
      if (this.showFileName) {
        this.formData.fileName = `${+new Date()}.vue`
      }
    },
    onClose() {
    },
    close(e) {
      this.$emit('update:visible', false)
    },
    handelConfirm() {
      this.$refs.elForm.validate(valid => {
        if (!valid) return
        this.$emit('confirm', { ...this.formData })
        this.close()
      })
    }
  }
}
</script>

<style lang="scss" scoped>

</style>

  1. proce —> fg —> DraggableItem.vue
<script>
import draggable from 'vuedraggable'
import render from '@/components/render/render'

const components = {
  itemBtns(h, currentItem, index, list) {
    const { copyItem, deleteItem } = this.$listeners
    return [
      <span class="drawing-item-copy" title="复制" onClick={event => {
        copyItem(currentItem, list); event.stopPropagation()
      }}>
        <i class="el-icon-copy-document" />
      </span>,
      <span class="drawing-item-delete" title="删除" onClick={event => {
        deleteItem(index, list); event.stopPropagation()
      }}>
        <i class="el-icon-delete" />
      </span>
    ]
  }
}
const layouts = {
  colFormItem(h, currentItem, index, list) {
    const { activeItem } = this.$listeners
    const config = currentItem.__config__
    const child = renderChildren.apply(this, arguments)
    let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
    if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
    let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
    if (config.showLabel === false) labelWidth = '0'
    return (
      <el-col span={config.span} class={className}
        nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}>
        <el-form-item label-width={labelWidth}
          label={config.showLabel ? config.label : ''} required={config.required}>
          <render key={config.renderKey} conf={currentItem} onInput={ event => {
            this.$set(config, 'defaultValue', event)
          }}>
            {child}
          </render>
        </el-form-item>
        {components.itemBtns.apply(this, arguments)}
      </el-col>
    )
  },
  rowFormItem(h, currentItem, index, list) {
    const { activeItem } = this.$listeners
    const config = currentItem.__config__
    const className = this.activeId === config.formId
      ? 'drawing-row-item active-from-item'
      : 'drawing-row-item'
    let child = renderChildren.apply(this, arguments)
    if (currentItem.type === 'flex') {
      child = <el-row type={currentItem.type} justify={currentItem.justify} align={currentItem.align}>
              {child}
            </el-row>
    }
    return (
      <el-col span={config.span}>
        <el-row gutter={config.gutter} class={className}
          nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}>
          <span class="component-name">{config.componentName}</span>
          <draggable list={config.children || []} animation={340}
            group="componentsGroup" class="drag-wrapper">
            {child}
          </draggable>
          {components.itemBtns.apply(this, arguments)}
        </el-row>
      </el-col>
    )
  },
  raw(h, currentItem, index, list) {
    const config = currentItem.__config__
    const child = renderChildren.apply(this, arguments)
    return <render key={config.renderKey} conf={currentItem} onInput={ event => {
      this.$set(config, 'defaultValue', event)
    }}>
      {child}
    </render>
  }
}

function renderChildren(h, currentItem, index, list) {
  const config = currentItem.__config__
  if (!Array.isArray(config.children)) return null
  return config.children.map((el, i) => {
    const layout = layouts[el.__config__.layout]
    if (layout) {
      return layout.call(this, h, el, i, config.children)
    }
    return layoutIsNotFound.call(this)
  })
}

function layoutIsNotFound() {
  throw new Error(`没有与${this.currentItem.__config__.layout}匹配的layout`)
}

export default {
  components: {
    render,
    draggable
  },
  props: [
    'currentItem',
    'index',
    'drawingList',
    'activeId',
    'formConf'
  ],
  render(h) {
    const layout = layouts[this.currentItem.__config__.layout]

    if (layout) {
      return layout.call(this, h, this.currentItem, this.index, this.drawingList)
    }
    return layoutIsNotFound.call(this)
  }
}
</script>

  1. proce —> fg ----> FormDrawer.vue
<template>
  <div>
    <el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
      <div style="height:100%">
        <el-row style="height:100%;overflow:auto">
          <el-col :md="24" :lg="12" class="left-editor">
            <div class="setting" title="资源引用" @click="showResource">
              <el-badge :is-dot="!!resources.length" class="item">
                <i class="el-icon-setting" />
              </el-badge>
            </div>
            <el-tabs v-model="activeTab" type="card" class="editor-tabs">
              <el-tab-pane name="html">
                <span slot="label">
                  <i v-if="activeTab==='html'" class="el-icon-edit" />
                  <i v-else class="el-icon-document" />
                  template
                </span>
              </el-tab-pane>
              <el-tab-pane name="js">
                <span slot="label">
                  <i v-if="activeTab==='js'" class="el-icon-edit" />
                  <i v-else class="el-icon-document" />
                  script
                </span>
              </el-tab-pane>
              <el-tab-pane name="css">
                <span slot="label">
                  <i v-if="activeTab==='css'" class="el-icon-edit" />
                  <i v-else class="el-icon-document" />
                  css
                </span>
              </el-tab-pane>
            </el-tabs>
            <div v-show="activeTab==='html'" id="editorHtml" class="tab-editor" />
            <div v-show="activeTab==='js'" id="editorJs" class="tab-editor" />
            <div v-show="activeTab==='css'" id="editorCss" class="tab-editor" />
          </el-col>
          <el-col :md="24" :lg="12" class="right-preview">
            <div class="action-bar" :style="{'text-align': 'left'}">
              <span class="bar-btn" @click="runCode">
                <i class="el-icon-refresh" />
                刷新
              </span>
              <span class="bar-btn" @click="exportFile">
                <i class="el-icon-download" />
                导出vue文件
              </span>
              <span ref="copyBtn" class="bar-btn copy-btn">
                <i class="el-icon-document-copy" />
                复制代码
              </span>
              <span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
                <i class="el-icon-circle-close" />
                关闭
              </span>
            </div>
            <iframe
              v-show="isIframeLoaded"
              ref="previewPage"
              class="result-wrapper"
              frameborder="0"
              src="preview.html"
              @load="iframeLoad"
            />
            <div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" />
          </el-col>
        </el-row>
      </div>
    </el-drawer>
    <resource-dialog
      :visible.sync="resourceVisible"
      :origin-resource="resources"
      @save="setResource"
    />
  </div>
</template>
<script>
import { parse } from '@babel/parser'
import ClipboardJS from 'clipboard'
import { saveAs } from 'file-saver'
import {
  makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/generator/css'
import { exportDefault, beautifierConf, titleCase } from '@/utils/index'
import ResourceDialog from './ResourceDialog'
import loadMonaco from '@/utils/loadMonaco'
import loadBeautifier from '@/utils/loadBeautifier'

const editorObj = {
  html: null,
  js: null,
  css: null
}
const mode = {
  html: 'html',
  js: 'javascript',
  css: 'css'
}
let beautifier
let monaco

export default {
  components: { ResourceDialog },
  props: ['formData', 'generateConf'],
  data() {
    return {
      activeTab: 'html',
      htmlCode: '',
      jsCode: '',
      cssCode: '',
      codeFrame: '',
      isIframeLoaded: false,
      isInitcode: false, // 保证open后两个异步只执行一次runcode
      isRefreshCode: false, // 每次打开都需要重新刷新代码
      resourceVisible: false,
      scripts: [],
      links: [],
      monaco: null
    }
  },
  computed: {
    resources() {
      return this.scripts.concat(this.links)
    }
  },
  watch: {},
  created() {
  },
  mounted() {
    window.addEventListener('keydown', this.preventDefaultSave)
    const clipboard = new ClipboardJS('.copy-btn', {
      text: trigger => {
        const codeStr = this.generateCode()
        this.$notify({
          title: '成功',
          message: '代码已复制到剪切板,可粘贴。',
          type: 'success'
        })
        return codeStr
      }
    })
    clipboard.on('error', e => {
      this.$message.error('代码复制失败')
    })
  },
  beforeDestroy() {
    window.removeEventListener('keydown', this.preventDefaultSave)
  },
  methods: {
    preventDefaultSave(e) {
      if (e.key === 's' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
      }
    },
    onOpen() {
      const { type } = this.generateConf
      this.htmlCode = makeUpHtml(this.formData, type)
      this.jsCode = makeUpJs(this.formData, type)
      this.cssCode = makeUpCss(this.formData)

      loadBeautifier(btf => {
        beautifier = btf
        this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html)
        this.jsCode = beautifier.js(this.jsCode, beautifierConf.js)
        this.cssCode = beautifier.css(this.cssCode, beautifierConf.html)

        loadMonaco(val => {
          monaco = val
          this.setEditorValue('editorHtml', 'html', this.htmlCode)
          this.setEditorValue('editorJs', 'js', this.jsCode)
          this.setEditorValue('editorCss', 'css', this.cssCode)
          if (!this.isInitcode) {
            this.isRefreshCode = true
            this.isIframeLoaded && (this.isInitcode = true) && this.runCode()
          }
        })
      })
    },
    onClose() {
      this.isInitcode = false
      this.isRefreshCode = false
    },
    iframeLoad() {
      if (!this.isInitcode) {
        this.isIframeLoaded = true
        this.isRefreshCode && (this.isInitcode = true) && this.runCode()
      }
    },
    setEditorValue(id, type, codeStr) {
      if (editorObj[type]) {
        editorObj[type].setValue(codeStr)
      } else {
        editorObj[type] = monaco.editor.create(document.getElementById(id), {
          value: codeStr,
          theme: 'vs-dark',
          language: mode[type],
          automaticLayout: true
        })
      }
      // ctrl + s 刷新
      editorObj[type].onKeyDown(e => {
        if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
          this.runCode()
        }
      })
    },
    runCode() {
      const jsCodeStr = editorObj.js.getValue()
      try {
        const ast = parse(jsCodeStr, { sourceType: 'module' })
        const astBody = ast.program.body
        if (astBody.length > 1) {
          this.$confirm(
            'js格式不能识别,仅支持修改export default的对象内容',
            '提示',
            {
              type: 'warning'
            }
          )
          return
        }
        if (astBody[0].type === 'ExportDefaultDeclaration') {
          const postData = {
            type: 'refreshFrame',
            data: {
              generateConf: this.generateConf,
              html: editorObj.html.getValue(),
              js: jsCodeStr.replace(exportDefault, ''),
              css: editorObj.css.getValue(),
              scripts: this.scripts,
              links: this.links
            }
          }

          this.$refs.previewPage.contentWindow.postMessage(
            postData,
            location.origin
          )
        } else {
          this.$message.error('请使用export default')
        }
      } catch (err) {
        this.$message.error(`js错误:${err}`)
        console.error(err)
      }
    },
    generateCode() {
      const html = vueTemplate(editorObj.html.getValue())
      const script = vueScript(editorObj.js.getValue())
      const css = cssStyle(editorObj.css.getValue())
      return beautifier.html(html + script + css, beautifierConf.html)
    },
    exportFile() {
      this.$prompt('文件名:', '导出文件', {
        inputValue: `${+new Date()}.vue`,
        closeOnClickModal: false,
        inputPlaceholder: '请输入文件名'
      }).then(({ value }) => {
        if (!value) value = `${+new Date()}.vue`
        const codeStr = this.generateCode()
        const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
        saveAs(blob, value)
      })
    },
    showResource() {
      this.resourceVisible = true
    },
    setResource(arr) {
      const scripts = []; const
        links = []
      if (Array.isArray(arr)) {
        arr.forEach(item => {
          if (item.endsWith('.css')) {
            links.push(item)
          } else {
            scripts.push(item)
          }
        })
        this.scripts = scripts
        this.links = links
      } else {
        this.scripts = []
        this.links = []
      }
    }
  }
}
</script>

<style lang="scss" scoped>
  @mixin action-bar {
    .action-bar {
      height: 33px;
      background: #f2fafb;
      padding: 0 15px;
      box-sizing: border-box;

      .bar-btn {
        display: inline-block;
        padding: 0 6px;
        line-height: 32px;
        color: #8285f5;
        cursor: pointer;
        font-size: 14px;
        user-select: none;
        & i {
          font-size: 20px;
        }
        &:hover {
          color: #4348d4;
        }
      }
      .bar-btn + .bar-btn {
        margin-left: 8px;
      }
      .delete-btn {
        color: #f56c6c;
        &:hover {
          color: #ea0b30;
        }
      }
    }
  }

.tab-editor {
  position: absolute;
  top: 33px;
  bottom: 0;
  left: 0;
  right: 0;
  font-size: 14px;
}
.left-editor {
  position: relative;
  height: 100%;
  background: #1e1e1e;
  overflow: hidden;
}
.setting{
  position: absolute;
  right: 15px;
  top: 3px;
  color: #a9f122;
  font-size: 18px;
  cursor: pointer;
  z-index: 1;
}
.right-preview {
  height: 100%;
  .result-wrapper {
    height: calc(100vh - 33px);
    width: 100%;
    overflow: auto;
    padding: 12px;
    box-sizing: border-box;
  }
}
@include action-bar;
::v-deep .el-drawer__header {
  display: none;
}
</style>

  1. proce —> fg ----> IconsDialog.vue
<template>
  <div class="icon-dialog">
    <el-dialog
      v-bind="$attrs"
      width="980px"
      :modal-append-to-body="false"
      v-on="$listeners"
      @open="onOpen"
      @close="onClose"
    >
      <div slot="title">
        选择图标
        <el-input
          v-model="key"
          size="mini"
          :style="{width: '260px'}"
          placeholder="请输入图标名称"
          prefix-icon="el-icon-search"
          clearable
        />
      </div>
      <ul class="icon-ul">
        <li
          v-for="icon in iconList"
          :key="icon"
          :class="active===icon?'active-item':''"
          @click="onSelect(icon)"
        >
          <i :class="icon" />
          <div>{{ icon }}</div>
        </li>
      </ul>
    </el-dialog>
  </div>
</template>
<script>
import iconList from '@/utils/generator/icon.json'

const originList = iconList.map(name => `el-icon-${name}`)

export default {
  inheritAttrs: false,
  props: ['current'],
  data() {
    return {
      iconList: originList,
      active: null,
      key: ''
    }
  },
  watch: {
    key(val) {
      if (val) {
        this.iconList = originList.filter(name => name.indexOf(val) > -1)
      } else {
        this.iconList = originList
      }
    }
  },
  methods: {
    onOpen() {
      this.active = this.current
      this.key = ''
    },
    onClose() {},
    onSelect(icon) {
      this.active = icon
      this.$emit('select', icon)
      this.$emit('update:visible', false)
    }
  }
}
</script>
<style lang="scss" scoped>
.icon-ul {
  margin: 0;
  padding: 0;
  font-size: 0;
  li {
    list-style-type: none;
    text-align: center;
    font-size: 14px;
    display: inline-block;
    width: 16.66%;
    box-sizing: border-box;
    height: 108px;
    padding: 15px 6px 6px 6px;
    cursor: pointer;
    overflow: hidden;
    &:hover {
      background: #f2f2f2;
    }
    &.active-item{
      background: #e1f3fb;
      color: #7a6df0
    }
    > i {
      font-size: 30px;
      line-height: 50px;
    }
  }
}
.icon-dialog {
  ::v-deep .el-dialog {
    border-radius: 8px;
    margin-bottom: 0;
    margin-top: 4vh !important;
    display: flex;
    flex-direction: column;
    max-height: 92vh;
    overflow: hidden;
    box-sizing: border-box;
    .el-dialog__header {
      padding-top: 14px;
    }
    .el-dialog__body {
      margin: 0 20px 20px 20px;
      padding: 0;
      overflow: auto;
    }
  }
}
</style>

  1. proce —> fg ----> index.vue
<template>
  <div class="container">
    <div class="left-board">
      <div class="logo-wrapper">
        <div class="logo">
          基础控件
        </div>
      </div>
      <el-scrollbar class="left-scrollbar">
        <div class="components-list">
          <div v-for="(item, listIndex) in leftComponents" :key="listIndex">
            <div class="components-title">
              <svg-icon icon-class="component" />
              {{ item.title }}
            </div>
            <draggable
              class="components-draggable"
              :list="item.list"
              :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
              :clone="cloneComponent"
              draggable=".components-item"
              :sort="false"
              @end="onEnd"
            >
              <div
                v-for="(element, index) in item.list"
                :key="index"
                class="components-item"
                @click="addComponent(element)"
              >
                <div class="components-body">
                  <svg-icon :icon-class="element.__config__.tagIcon" />
                  {{ element.__config__.label }}
                </div>
              </div>
            </draggable>
          </div>
        </div>
      </el-scrollbar>
    </div>

    <div class="center-board">
      <div class="action-bar">
<!--        <el-button icon="el-icon-video-play" type="text" @click="run">
          运行
        </el-button>
        <el-button icon="el-icon-view" type="text" @click="showJson">
          查看json
        </el-button>
        <el-button icon="el-icon-download" type="text" @click="download">
          导出vue文件
        </el-button>
        <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">
          复制代码
        </el-button>-->
        <el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
          清空
        </el-button>
      </div>
      <el-scrollbar class="center-scrollbar">
        <el-row class="center-board-row" :gutter="formConf.gutter">
          <el-form
            :size="formConf.size"
            :label-position="formConf.labelPosition"
            :disabled="formConf.disabled"
            :label-width="formConf.labelWidth + 'px'"
          >
            <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
              <draggable-item
                v-for="(item, index) in drawingList"
                :key="item.renderKey"
                :drawing-list="drawingList"
                :current-item="item"
                :index="index"
                :active-id="activeId"
                :form-conf="formConf"
                @activeItem="activeFormItem"
                @copyItem="drawingItemCopy"
                @deleteItem="drawingItemDelete"
              />
            </draggable>
            <div v-show="!drawingList.length" class="empty-info">
              从左侧拖入或点选组件进行表单设计
            </div>
          </el-form>
        </el-row>
      </el-scrollbar>
    </div>

    <right-panel
      :active-data="activeData"
      :form-conf="formConf"
      :show-field="!!drawingList.length"
      @tag-change="tagChange"
      @fetch-data="fetchData"
    />

    <form-drawer
      :visible.sync="drawerVisible"
      :form-data="formData"
      size="100%"
      :generate-conf="generateConf"
    />
    <json-drawer
      size="60%"
      :visible.sync="jsonDrawerVisible"
      :json-str="JSON.stringify(formData)"
      @refresh="refreshJson"
    />
    <code-type-dialog
      :visible.sync="dialogVisible"
      title="选择生成类型"
      :show-file-name="showFileName"
      @confirm="generate"
    />
    <input id="copyNode" type="hidden">
  </div>
</template>

<script>
  import draggable from 'vuedraggable'
  import { debounce } from 'throttle-debounce'
  import { saveAs } from 'file-saver'
  import ClipboardJS from 'clipboard'
  import render from '@/components/render/render'
  import FormDrawer from './FormDrawer'
  import JsonDrawer from './JsonDrawer'
  import RightPanel from './RightPanel'
  import {
    inputComponents, selectComponents, layoutComponents, formConf
  } from '@/components/generator/config'
  import {
    exportDefault, beautifierConf, isNumberStr, titleCase, deepClone, isObjectObject
  } from '@/utils/index'
  import {
    makeUpHtml, vueTemplate, vueScript, cssStyle
  } from '@/components/generator/html'
  import { makeUpJs } from '@/components/generator/js'
  import { makeUpCss } from '@/components/generator/css'
  import drawingDefalut from '@/components/generator/drawingDefalut'
  import logo from '@/assets/logo/logo.png'
  import CodeTypeDialog from './CodeTypeDialog'
  import DraggableItem from './DraggableItem'
  import {
    getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf, getItem
  } from '@/utils/db'
  import loadBeautifier from '@/utils/loadBeautifier'

  let beautifier
  const emptyActiveData = { style: {}, autosize: {} }
  let oldActiveId
  let tempActiveData
  // const drawingListInDB = getDrawingList()
  // const formConfInDB = getFormConf()
  const idGlobal = getIdGlobal()

  export default {
    components: {
      draggable,
      render,
      FormDrawer,
      JsonDrawer,
      RightPanel,
      CodeTypeDialog,
      DraggableItem
    },
    data() {
      return {
        logo,
        idGlobal,
        formConf,
        inputComponents,
        selectComponents,
        layoutComponents,
        labelWidth: 100,
        drawingList: drawingDefalut,
        drawingData: {},
        activeId: drawingDefalut[0].formId,
        drawerVisible: false,
        formData: {},
        dialogVisible: false,
        jsonDrawerVisible: false,
        generateConf: null,
        showFileName: false,
        activeData: drawingDefalut[0],
        saveDrawingListDebounce: debounce(340, saveDrawingList),
        saveIdGlobalDebounce: debounce(340, saveIdGlobal),
        leftComponents: [
          {
            title: '输入型组件',
            list: inputComponents
          },
          {
            title: '选择型组件',
            list: selectComponents
          },
          {
            title: '布局型组件',
            list: layoutComponents
          }
        ]
      }
    },
    computed: {
    },
    watch: {
      // eslint-disable-next-line func-names
      'activeData.__config__.label': function (val, oldVal) {
        if (
          this.activeData.placeholder === undefined
          || !this.activeData.__config__.tag
          || oldActiveId !== this.activeId
        ) {
          return
        }
        this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
      },
      activeId: {
        handler(val) {
          oldActiveId = val
        },
        immediate: true
      },
      drawingList: {
        handler(val) {
          this.saveDrawingListDebounce(val)
          if (val.length === 0) this.idGlobal = 100
        },
        deep: true
      },
      idGlobal: {
        handler(val) {
          this.saveIdGlobalDebounce(val)
        },
        immediate: true
      }
    },
    mounted() {
      const drawingListInDB = getItem()
      if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
        this.drawingList = drawingListInDB
      } else {
        this.drawingList = drawingDefalut
      }

      const formConfInDB = getFormConf()
      this.activeFormItem(this.drawingList[0])
      if (formConfInDB) {
        this.formConf = formConfInDB
      }
      loadBeautifier(btf => {
        beautifier = btf
      })
      const clipboard = new ClipboardJS('#copyNode', {
        text: trigger => {
          const codeStr = this.generateCode()
          this.$notify({
            title: '成功',
            message: '代码已复制到剪切板,可粘贴。',
            type: 'success'
          })
          return codeStr
        }
      })
      clipboard.on('error', e => {
        this.$message.error('代码复制失败')
      })
    },
    methods: {
      setObjectValueReduce(obj, strKeys, data) {
        const arr = strKeys.split('.')
        arr.reduce((pre, item, i) => {
          if (arr.length === i + 1) {
            pre[item] = data
          } else if (!isObjectObject(pre[item])) {
            pre[item] = {}
          }
          return pre[item]
        }, obj)
      },
      setRespData(component, resp) {
        const { dataPath, renderKey, dataConsumer } = component.__config__
        if (!dataPath || !dataConsumer) return
        const respData = dataPath.split('.').reduce((pre, item) => pre[item], resp)

        // 将请求回来的数据,赋值到指定属性。
        // 以el-tabel为例,根据Element文档,应该将数据赋值给el-tabel的data属性,所以dataConsumer的值应为'data';
        // 此时赋值代码可写成 component[dataConsumer] = respData;
        // 但为支持更深层级的赋值(如:dataConsumer的值为'options.data'),使用setObjectValueReduce
        this.setObjectValueReduce(component, dataConsumer, respData)
        const i = this.drawingList.findIndex(item => item.__config__.renderKey === renderKey)
        if (i > -1) this.$set(this.drawingList, i, component)
      },
      fetchData(component) {
        const { dataType, method, url } = component.__config__
        if (dataType === 'dynamic' && method && url) {
          this.setLoading(component, true)
          this.$axios({
            method,
            url
          }).then(resp => {
            this.setLoading(component, false)
            this.setRespData(component, resp.data)
          })
        }
      },
      setLoading(component, val) {
        const { directives } = component
        if (Array.isArray(directives)) {
          const t = directives.find(d => d.name === 'loading')
          if (t) t.value = val
        }
      },
      activeFormItem(currentItem) {
        this.activeData = currentItem
        this.activeId = currentItem.__config__.formId
      },
      onEnd(obj) {
        if (obj.from !== obj.to) {
          this.fetchData(tempActiveData)
          this.activeData = tempActiveData
          this.activeId = this.idGlobal
        }
      },
      addComponent(item) {
        const clone = this.cloneComponent(item)
        this.fetchData(clone)
        this.drawingList.push(clone)
        this.activeFormItem(clone)
      },
      cloneComponent(origin) {
        const clone = deepClone(origin)
        const config = clone.__config__
        config.span = this.formConf.span // 生成代码时,会根据span做精简判断
        this.createIdAndKey(clone)
        clone.placeholder !== undefined && (clone.placeholder += config.label)
        tempActiveData = clone
        return tempActiveData
      },
      createIdAndKey(item) {
        const config = item.__config__
        config.formId = ++this.idGlobal
        config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
        if (config.layout === 'colFormItem') {
          item.__vModel__ = `field${this.idGlobal}`
        } else if (config.layout === 'rowFormItem') {
          config.componentName = `row${this.idGlobal}`
          !Array.isArray(config.children) && (config.children = [])
          delete config.label // rowFormItem无需配置label属性
        }
        if (Array.isArray(config.children)) {
          config.children = config.children.map(childItem => this.createIdAndKey(childItem))
        }
        return item
      },
      AssembleFormData() {
        this.formData = {
          fields: deepClone(this.drawingList),
          // ...this.formConf
        }
      },
      generate(data) {
        const func = this[`exec${titleCase(this.operationType)}`]
        this.generateConf = data
        func && func(data)
      },
      execRun(data) {
        this.AssembleFormData()
        this.drawerVisible = true
      },
      execDownload(data) {
        const codeStr = this.generateCode()
        const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
        saveAs(blob, data.fileName)
      },
      execCopy(data) {
        document.getElementById('copyNode').click()
      },
      empty() {
        this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
          () => {
            this.drawingList = []
            this.idGlobal = 100
          }
        )
      },
      drawingItemCopy(item, list) {
        let clone = deepClone(item)
        clone = this.createIdAndKey(clone)
        list.push(clone)
        this.activeFormItem(clone)
      },
      drawingItemDelete(index, list) {
        list.splice(index, 1)
        this.$nextTick(() => {
          const len = this.drawingList.length
          if (len) {
            this.activeFormItem(this.drawingList[len - 1])
          }
        })
      },
      generateCode() {
        const { type } = this.generateConf
        this.AssembleFormData()
        const script = vueScript(makeUpJs(this.formData, type))
        const html = vueTemplate(makeUpHtml(this.formData, type))
        const css = cssStyle(makeUpCss(this.formData))
        return beautifier.html(html + script + css, beautifierConf.html)
      },
      showJson() {
        this.AssembleFormData()
        this.jsonDrawerVisible = true
      },
      download() {
        this.dialogVisible = true
        this.showFileName = true
        this.operationType = 'download'
      },
      run() {
        this.dialogVisible = true
        this.showFileName = false
        this.operationType = 'run'
      },
      copy() {
        this.dialogVisible = true
        this.showFileName = false
        this.operationType = 'copy'
      },
      tagChange(newTag) {
        newTag = this.cloneComponent(newTag)
        const config = newTag.__config__
        newTag.__vModel__ = this.activeData.__vModel__
        config.formId = this.activeId
        config.span = this.activeData.__config__.span
        this.activeData.__config__.tag = config.tag
        this.activeData.__config__.tagIcon = config.tagIcon
        this.activeData.__config__.document = config.document
        if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
          config.defaultValue = this.activeData.__config__.defaultValue
        }
        Object.keys(newTag).forEach(key => {
          if (this.activeData[key] !== undefined) {
            newTag[key] = this.activeData[key]
          }
        })
        this.activeData = newTag
        this.updateDrawingList(newTag, this.drawingList)
      },
      updateDrawingList(newTag, list) {
        const index = list.findIndex(item => item.__config__.formId === this.activeId)
        if (index > -1) {
          list.splice(index, 1, newTag)
        } else {
          list.forEach(item => {
            if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
          })
        }
      },
      refreshJson(data) {
        this.drawingList = deepClone(data.fields)
        delete data.fields
        this.formConf = data
      },
      getJson(){
        this.AssembleFormData()
        this.formData.formBtns = false
        return this.formData
      }
    }
  }
</script>

<style lang='scss'>
  $selectedColor: #f6f7ff;
  $lighterBlue: #409EFF;

  .container {
    position: relative;
    width: 100%;
    height: 100%;
  }

  .components-list {
    padding: 8px;
    box-sizing: border-box;
    height: 100%;
    .components-item {
      display: inline-block;
      width: 48%;
      margin: 1%;
      transition: transform 0ms !important;
    }
  }
  .components-draggable{
    padding-bottom: 20px;
  }
  .components-title{
    font-size: 14px;
    color: #222;
    margin: 6px 2px;
    .svg-icon{
      color: #666;
      font-size: 18px;
    }
  }

  .components-body {
    padding: 8px 10px;
    background: $selectedColor;
    font-size: 12px;
    cursor: move;
    border: 1px dashed $selectedColor;
    border-radius: 3px;
    .svg-icon{
      color: #777;
      font-size: 15px;
    }
    &:hover {
      border: 1px dashed #787be8;
      color: #787be8;
      .svg-icon {
        color: #787be8;
      }
    }
  }

  .left-board {
    width: 260px;
    position: absolute;
    left: 0;
    top: 0;
    height: 100vh;
  }
  .left-scrollbar{
    height: calc(100vh - 42px);
    overflow: hidden;
  }
  .center-scrollbar {
    height: calc(100vh - 42px);
    overflow: hidden;
    border-left: 1px solid #f1e8e8;
    border-right: 1px solid #f1e8e8;
    box-sizing: border-box;
  }
  .center-board {
    height: 100vh;
    width: auto;
    margin: 0 350px 0 260px;
    box-sizing: border-box;
  }
  .empty-info{
    position: absolute;
    top: 46%;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 18px;
    color: #ccb1ea;
    letter-spacing: 4px;
  }
  .action-bar{
    position: relative;
    height: 42px;
    text-align: right;
    padding: 0 15px;
    box-sizing: border-box;;
    border: 1px solid #f1e8e8;
    border-top: none;
    border-left: none;
    .delete-btn{
      color: #F56C6C;
    }
  }
  .logo-wrapper{
    position: relative;
    height: 42px;
    background: #fff;
    border-bottom: 1px solid #f1e8e8;
    box-sizing: border-box;
  }
  .logo{
    position: absolute;
    left: 12px;
    top: 6px;
    line-height: 30px;
    color: #00afff;
    font-weight: 600;
    font-size: 17px;
    white-space: nowrap;
    > img{
      width: 30px;
      height: 30px;
      vertical-align: top;
    }
    .github{
      display: inline-block;
      vertical-align: sub;
      margin-left: 15px;
      > img{
        height: 22px;
      }
    }
  }

  .center-board-row {
    padding: 12px 12px 15px 12px;
    box-sizing: border-box;
    & > .el-form {
      // 69 = 12+15+42
      height: calc(100vh - 69px);
    }
  }
  .drawing-board {
    height: 100%;
    position: relative;
    .components-body {
      padding: 0;
      margin: 0;
      font-size: 0;
    }
    .sortable-ghost {
      position: relative;
      display: block;
      overflow: hidden;
      &::before {
        content: " ";
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        height: 3px;
        background: rgb(89, 89, 223);
        z-index: 2;
      }
    }
    .components-item.sortable-ghost {
      width: 100%;
      height: 60px;
      background-color: $selectedColor;
    }
    .active-from-item {
      & > .el-form-item{
        background: $selectedColor;
        border-radius: 6px;
      }
      & > .drawing-item-copy, & > .drawing-item-delete{
        display: initial;
      }
      & > .component-name{
        color: $lighterBlue;
      }
    }
    .el-form-item{
      margin-bottom: 15px;
    }
  }
  .drawing-item{
    position: relative;
    cursor: move;
    &.unfocus-bordered:not(.active-from-item) > div:first-child {
      border: 1px dashed #ccc;
    }
    .el-form-item{
      padding: 12px 10px;
    }
  }
  .drawing-row-item{
    position: relative;
    cursor: move;
    box-sizing: border-box;
    border: 1px dashed #ccc;
    border-radius: 3px;
    padding: 0 2px;
    margin-bottom: 15px;
    .drawing-row-item {
      margin-bottom: 2px;
    }
    .el-col{
      margin-top: 22px;
    }
    .el-form-item{
      margin-bottom: 0;
    }
    .drag-wrapper{
      min-height: 80px;
    }
    &.active-from-item{
      border: 1px dashed $lighterBlue;
    }
    .component-name{
      position: absolute;
      top: 0;
      left: 0;
      font-size: 12px;
      color: #bbb;
      display: inline-block;
      padding: 0 6px;
    }
  }
  .drawing-item, .drawing-row-item{
    &:hover {
      & > .el-form-item{
        background: $selectedColor;
        border-radius: 6px;
      }
      & > .drawing-item-copy, & > .drawing-item-delete{
        display: initial;
      }
    }
    & > .drawing-item-copy, & > .drawing-item-delete{
      display: none;
      position: absolute;
      top: -10px;
      width: 22px;
      height: 22px;
      line-height: 22px;
      text-align: center;
      border-radius: 50%;
      font-size: 12px;
      border: 1px solid;
      cursor: pointer;
      z-index: 1;
    }
    & > .drawing-item-copy{
      right: 56px;
      border-color: $lighterBlue;
      color: $lighterBlue;
      background: #fff;
      &:hover{
        background: $lighterBlue;
        color: #fff;
      }
    }
    & > .drawing-item-delete{
      right: 24px;
      border-color: #F56C6C;
      color: #F56C6C;
      background: #fff;
      &:hover{
        background: #F56C6C;
        color: #fff;
      }
    }
  }

</style>

  1. proce —> fg ----> JsonDrawer.vue
<template>
  <div>
    <el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
      <div class="action-bar" :style="{'text-align': 'left'}">
        <span class="bar-btn" @click="refresh">
          <i class="el-icon-refresh" />
          刷新
        </span>
        <span ref="copyBtn" class="bar-btn copy-json-btn">
          <i class="el-icon-document-copy" />
          复制JSON
        </span>
        <span class="bar-btn" @click="exportJsonFile">
          <i class="el-icon-download" />
          导出JSON文件
        </span>
        <span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
          <i class="el-icon-circle-close" />
          关闭
        </span>
      </div>
      <div id="editorJson" class="json-editor" />
    </el-drawer>
  </div>
</template>

<script>
import { beautifierConf } from '@/utils/index'
import ClipboardJS from 'clipboard'
import { saveAs } from 'file-saver'
import loadMonaco from '@/utils/loadMonaco'
import loadBeautifier from '@/utils/loadBeautifier'

let beautifier
let monaco

export default {
  components: {},
  props: {
    jsonStr: {
      type: String,
      required: true
    }
  },
  data() {
    return {}
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    window.addEventListener('keydown', this.preventDefaultSave)
    const clipboard = new ClipboardJS('.copy-json-btn', {
      text: trigger => {
        this.$notify({
          title: '成功',
          message: '代码已复制到剪切板,可粘贴。',
          type: 'success'
        })
        return this.beautifierJson
      }
    })
    clipboard.on('error', e => {
      this.$message.error('代码复制失败')
    })
  },
  beforeDestroy() {
    window.removeEventListener('keydown', this.preventDefaultSave)
  },
  methods: {
    preventDefaultSave(e) {
      if (e.key === 's' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
      }
    },
    onOpen() {
      loadBeautifier(btf => {
        beautifier = btf
        this.beautifierJson = beautifier.js(this.jsonStr, beautifierConf.js)

        loadMonaco(val => {
          monaco = val
          this.setEditorValue('editorJson', this.beautifierJson)
        })
      })
    },
    onClose() {},
    setEditorValue(id, codeStr) {
      if (this.jsonEditor) {
        this.jsonEditor.setValue(codeStr)
      } else {
        this.jsonEditor = monaco.editor.create(document.getElementById(id), {
          value: codeStr,
          theme: 'vs-dark',
          language: 'json',
          automaticLayout: true
        })
        // ctrl + s 刷新
        this.jsonEditor.onKeyDown(e => {
          if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
            this.refresh()
          }
        })
      }
    },
    exportJsonFile() {
      this.$prompt('文件名:', '导出文件', {
        inputValue: `${+new Date()}.json`,
        closeOnClickModal: false,
        inputPlaceholder: '请输入文件名'
      }).then(({ value }) => {
        if (!value) value = `${+new Date()}.json`
        const codeStr = this.jsonEditor.getValue()
        const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
        saveAs(blob, value)
      })
    },
    refresh() {
      try {
        this.$emit('refresh', JSON.parse(this.jsonEditor.getValue()))
      } catch (error) {
        this.$notify({
          title: '错误',
          message: 'JSON格式错误,请检查',
          type: 'error'
        })
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import '@/styles/mixin.scss';

::v-deep .el-drawer__header {
  display: none;
}
@include action-bar;

.json-editor{
  height: calc(100vh - 33px);
}
</style>

  1. proce —> fg ----> ResourceDialog.vue
<template>
  <div>
    <el-dialog
      v-bind="$attrs"
      title="外部资源引用"
      width="600px"
      :close-on-click-modal="false"
      v-on="$listeners"
      @open="onOpen"
      @close="onClose"
    >
      <el-input
        v-for="(item, index) in resources"
        :key="index"
        v-model="resources[index]"
        class="url-item"
        placeholder="请输入 css 或 js 资源路径"
        prefix-icon="el-icon-link"
        clearable
      >
        <el-button
          slot="append"
          icon="el-icon-delete"
          @click="deleteOne(index)"
        />
      </el-input>
      <el-button-group class="add-item">
        <el-button
          plain
          @click="addOne('https://lib.baomitu.com/jquery/1.8.3/jquery.min.js')"
        >
          jQuery1.8.3
        </el-button>
        <el-button
          plain
          @click="addOne('https://unpkg.com/http-vue-loader')"
        >
          http-vue-loader
        </el-button>
        <el-button
          icon="el-icon-circle-plus-outline"
          plain
          @click="addOne('')"
        >
          添加其他
        </el-button>
      </el-button-group>
      <div slot="footer">
        <el-button @click="close">
          取消
        </el-button>
        <el-button
          type="primary"
          @click="handelConfirm"
        >
          确定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { deepClone } from '@/utils/index'

export default {
  components: {},
  inheritAttrs: false,
  props: ['originResource'],
  data() {
    return {
      resources: null
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {
    onOpen() {
      this.resources = this.originResource.length ? deepClone(this.originResource) : ['']
    },
    onClose() {
    },
    close() {
      this.$emit('update:visible', false)
    },
    handelConfirm() {
      const results = this.resources.filter(item => !!item) || []
      this.$emit('save', results)
      this.close()
      if (results.length) {
        this.resources = results
      }
    },
    deleteOne(index) {
      this.resources.splice(index, 1)
    },
    addOne(url) {
      if (this.resources.indexOf(url) > -1) {
        this.$message('资源已存在')
      } else {
        this.resources.push(url)
      }
    }
  }
}

</script>
<style lang="scss" scoped>
.add-item{
  margin-top: 8px;
}
.url-item{
  margin-bottom: 12px;
}
</style>

  1. proce —> fg ----> RightPanel.vue
<template>
  <div class="right-board">
    <el-tabs v-model="currentTab" class="center-tabs">
      <el-tab-pane label="组件属性" name="field" />
      <el-tab-pane label="表单属性" name="form" />
    </el-tabs>
    <div class="field-box">
      <a class="document-link" target="_blank" :href="documentLink" title="查看组件文档">
        <i class="el-icon-link" />
      </a>
      <el-scrollbar class="right-scrollbar">
        <!-- 组件属性 -->
        <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px">
          <el-form-item v-if="activeData.__config__.changeTag" label="组件类型">
            <el-select
              v-model="activeData.__config__.tagIcon"
              placeholder="请选择组件类型"
              :style="{width: '100%'}"
              @change="tagChange"
            >
              <el-option-group v-for="group in tagList" :key="group.label" :label="group.label">
                <el-option
                  v-for="item in group.options"
                  :key="item.__config__.label"
                  :label="item.__config__.label"
                  :value="item.__config__.tagIcon"
                >
                  <svg-icon class="node-icon" :icon-class="item.__config__.tagIcon" />
                  <span> {{ item.__config__.label }}</span>
                </el-option>
              </el-option-group>
            </el-select>
          </el-form-item>
          <el-form-item v-if="activeData.__vModel__!==undefined" label="字段名">
            <el-input v-model="activeData.__vModel__" placeholder="请输入字段名(v-model)" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.componentName!==undefined" label="组件名">
            {{ activeData.__config__.componentName }}
          </el-form-item>
          <el-form-item v-if="activeData.__config__.label!==undefined" label="标题">
            <el-input v-model="activeData.__config__.label" placeholder="请输入标题" @input="changeRenderKey" />
          </el-form-item>
          <el-form-item v-if="activeData.placeholder!==undefined" label="占位提示">
            <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" @input="changeRenderKey" />
          </el-form-item>
          <el-form-item v-if="activeData['start-placeholder']!==undefined" label="开始占位">
            <el-input v-model="activeData['start-placeholder']" placeholder="请输入占位提示" />
          </el-form-item>
          <el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位">
            <el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.span!==undefined" label="表单栅格">
            <el-slider v-model="activeData.__config__.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.layout==='rowFormItem'&&activeData.gutter!==undefined" label="栅格间隔">
            <el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.layout==='rowFormItem'&&activeData.type!==undefined" label="布局模式">
            <el-radio-group v-model="activeData.type">
              <el-radio-button label="default" />
              <el-radio-button label="flex" />
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="activeData.justify!==undefined&&activeData.type==='flex'" label="水平排列">
            <el-select v-model="activeData.justify" placeholder="请选择水平排列" :style="{width: '100%'}">
              <el-option
                v-for="(item, index) in justifyOptions"
                :key="index"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item v-if="activeData.align!==undefined&&activeData.type==='flex'" label="垂直排列">
            <el-radio-group v-model="activeData.align">
              <el-radio-button label="top" />
              <el-radio-button label="middle" />
              <el-radio-button label="bottom" />
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="activeData.__config__.labelWidth!==undefined" label="标签宽度">
            <el-input v-model.number="activeData.__config__.labelWidth" type="number" placeholder="请输入标签宽度" />
          </el-form-item>
          <el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度">
            <el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable />
          </el-form-item>
          <el-form-item v-if="activeData.__vModel__!==undefined" label="默认值">
            <el-input
              :value="setDefaultValue(activeData.__config__.defaultValue)"
              placeholder="请输入默认值"
              @input="onDefaultValueInput"
            />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag==='el-checkbox-group'" label="至少应选">
            <el-input-number
              :value="activeData.min"
              :min="0"
              placeholder="至少应选"
              @input="$set(activeData, 'min', $event?$event:undefined)"
            />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag==='el-checkbox-group'" label="最多可选">
            <el-input-number
              :value="activeData.max"
              :min="0"
              placeholder="最多可选"
              @input="$set(activeData, 'max', $event?$event:undefined)"
            />
          </el-form-item>
          <el-form-item v-if="activeData.__slot__&&activeData.__slot__.prepend!==undefined" label="前缀">
            <el-input v-model="activeData.__slot__.prepend" placeholder="请输入前缀" />
          </el-form-item>
          <el-form-item v-if="activeData.__slot__&&activeData.__slot__.append!==undefined" label="后缀">
            <el-input v-model="activeData.__slot__.append" placeholder="请输入后缀" />
          </el-form-item>
          <el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标">
            <el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称">
              <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('prefix-icon')">
                选择
              </el-button>
            </el-input>
          </el-form-item>
          <el-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标">
            <el-input v-model="activeData['suffix-icon']" placeholder="请输入后图标名称">
              <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('suffix-icon')">
                选择
              </el-button>
            </el-input>
          </el-form-item>
          <el-form-item
            v-if="activeData['icon']!==undefined && activeData.__config__.tag === 'el-button'"
            label="按钮图标"
          >
            <el-input v-model="activeData['icon']" placeholder="请输入按钮图标名称">
              <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('icon')">
                选择
              </el-button>
            </el-input>
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="选项分隔符">
            <el-input v-model="activeData.separator" placeholder="请输入选项分隔符" />
          </el-form-item>
          <el-form-item v-if="activeData.autosize !== undefined" label="最小行数">
            <el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="最小行数" />
          </el-form-item>
          <el-form-item v-if="activeData.autosize !== undefined" label="最大行数">
            <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
          </el-form-item>
          <el-form-item v-if="isShowMin" label="最小值">
            <el-input-number v-model="activeData.min" placeholder="最小值" />
          </el-form-item>
          <el-form-item v-if="isShowMax" label="最大值">
            <el-input-number v-model="activeData.max" placeholder="最大值" />
          </el-form-item>
          <el-form-item v-if="activeData.height!==undefined" label="组件高度">
            <el-input-number v-model="activeData.height" placeholder="高度" @input="changeRenderKey" />
          </el-form-item>
          <el-form-item v-if="isShowStep" label="步长">
            <el-input-number v-model="activeData.step" placeholder="步数" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="精度">
            <el-input-number v-model="activeData.precision" :min="0" placeholder="精度" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="按钮位置">
            <el-radio-group v-model="activeData['controls-position']">
              <el-radio-button label="">
                默认
              </el-radio-button>
              <el-radio-button label="right">
                右侧
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="activeData.maxlength !== undefined" label="最多输入">
            <el-input v-model="activeData.maxlength" placeholder="请输入字符长度">
              <template slot="append">
                个字符
              </template>
            </el-input>
          </el-form-item>
          <el-form-item v-if="activeData['active-text'] !== undefined" label="开启提示">
            <el-input v-model="activeData['active-text']" placeholder="请输入开启提示" />
          </el-form-item>
          <el-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示">
            <el-input v-model="activeData['inactive-text']" placeholder="请输入关闭提示" />
          </el-form-item>
          <el-form-item v-if="activeData['active-value'] !== undefined" label="开启值">
            <el-input
              :value="setDefaultValue(activeData['active-value'])"
              placeholder="请输入开启值"
              @input="onSwitchValueInput($event, 'active-value')"
            />
          </el-form-item>
          <el-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值">
            <el-input
              :value="setDefaultValue(activeData['inactive-value'])"
              placeholder="请输入关闭值"
              @input="onSwitchValueInput($event, 'inactive-value')"
            />
          </el-form-item>
          <el-form-item
            v-if="activeData.type !== undefined && 'el-date-picker' === activeData.__config__.tag"
            label="时间类型"
          >
            <el-select
              v-model="activeData.type"
              placeholder="请选择时间类型"
              :style="{ width: '100%' }"
              @change="dateTypeChange"
            >
              <el-option
                v-for="(item, index) in dateOptions"
                :key="index"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item v-if="activeData.name !== undefined" label="文件字段名">
            <el-input v-model="activeData.name" placeholder="请输入上传文件字段名" />
          </el-form-item>
          <el-form-item v-if="activeData.accept !== undefined" label="文件类型">
            <el-select
              v-model="activeData.accept"
              placeholder="请选择文件类型"
              :style="{ width: '100%' }"
              clearable
            >
              <el-option label="图片" value="image/*" />
              <el-option label="视频" value="video/*" />
              <el-option label="音频" value="audio/*" />
              <el-option label="excel" value=".xls,.xlsx" />
              <el-option label="word" value=".doc,.docx" />
              <el-option label="pdf" value=".pdf" />
              <el-option label="txt" value=".txt" />
            </el-select>
          </el-form-item>
          <el-form-item v-if="activeData.__config__.fileSize !== undefined" label="文件大小">
            <el-input v-model.number="activeData.__config__.fileSize" placeholder="请输入文件大小">
              <el-select slot="append" v-model="activeData.__config__.sizeUnit" :style="{ width: '66px' }">
                <el-option label="KB" value="KB" />
                <el-option label="MB" value="MB" />
                <el-option label="GB" value="GB" />
              </el-select>
            </el-input>
          </el-form-item>
          <el-form-item v-if="activeData.action !== undefined" label="上传地址">
            <el-input v-model="activeData.action" placeholder="请输入上传地址" clearable />
          </el-form-item>
          <el-form-item v-if="activeData['list-type'] !== undefined" label="列表类型">
            <el-radio-group v-model="activeData['list-type']" size="small">
              <el-radio-button label="text">
                text
              </el-radio-button>
              <el-radio-button label="picture">
                picture
              </el-radio-button>
              <el-radio-button label="picture-card">
                picture-card
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item
            v-if="activeData.type !== undefined && activeData.__config__.tag === 'el-button'"
            label="按钮类型"
          >
            <el-select v-model="activeData.type" :style="{ width: '100%' }">
              <el-option label="primary" value="primary" />
              <el-option label="success" value="success" />
              <el-option label="warning" value="warning" />
              <el-option label="danger" value="danger" />
              <el-option label="info" value="info" />
              <el-option label="text" value="text" />
            </el-select>
          </el-form-item>
          <el-form-item
            v-if="activeData.__config__.buttonText !== undefined"
            v-show="'picture-card' !== activeData['list-type']"
            label="按钮文字"
          >
            <el-input v-model="activeData.__config__.buttonText" placeholder="请输入按钮文字" />
          </el-form-item>
          <el-form-item
            v-if="activeData.__config__.tag === 'el-button'"
            label="按钮文字"
          >
            <el-input v-model="activeData.__slot__.default" placeholder="请输入按钮文字" />
          </el-form-item>
          <el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
            <el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" />
          </el-form-item>
          <el-form-item v-if="activeData['picker-options'] !== undefined" label="时间段">
            <el-input
              v-model="activeData['picker-options'].selectableRange"
              placeholder="请输入时间段"
            />
          </el-form-item>
          <el-form-item v-if="activeData.format !== undefined" label="时间格式">
            <el-input
              :value="activeData.format"
              placeholder="请输入时间格式"
              @input="setTimeValue($event)"
            />
          </el-form-item>
          <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.__config__.tag) > -1">
            <el-divider>选项</el-divider>
            <draggable
              :list="activeData.__slot__.options"
              :animation="340"
              group="selectItem"
              handle=".option-drag"
            >
              <div v-for="(item, index) in activeData.__slot__.options" :key="index" class="select-item">
                <div class="select-line-icon option-drag">
                  <i class="el-icon-s-operation" />
                </div>
                <el-input v-model="item.label" placeholder="选项名" size="small" />
                <el-input
                  placeholder="选项值"
                  size="small"
                  :value="item.value"
                  @input="setOptionValue(item, $event)"
                />
                <div class="close-btn select-line-icon" @click="activeData.__slot__.options.splice(index, 1)">
                  <i class="el-icon-remove-outline" />
                </div>
              </div>
            </draggable>
            <div style="margin-left: 20px;">
              <el-button
                style="padding-bottom: 0"
                icon="el-icon-circle-plus-outline"
                type="text"
                @click="addSelectItem"
              >
                添加选项
              </el-button>
            </div>
            <el-divider />
          </template>

          <template v-if="['el-cascader', 'el-table'].includes(activeData.__config__.tag)">
            <el-divider>选项</el-divider>
            <el-form-item v-if="activeData.__config__.dataType" label="数据类型">
              <el-radio-group v-model="activeData.__config__.dataType" size="small">
                <el-radio-button label="dynamic">
                  动态数据
                </el-radio-button>
                <el-radio-button label="static">
                  静态数据
                </el-radio-button>
              </el-radio-group>
            </el-form-item>

            <template v-if="activeData.__config__.dataType === 'dynamic'">
              <el-form-item label="接口地址">
                <el-input
                  v-model="activeData.__config__.url"
                  :title="activeData.__config__.url"
                  placeholder="请输入接口地址"
                  clearable
                  @blur="$emit('fetch-data', activeData)"
                >
                  <el-select
                    slot="prepend"
                    v-model="activeData.__config__.method"
                    :style="{width: '85px'}"
                    @change="$emit('fetch-data', activeData)"
                  >
                    <el-option label="get" value="get" />
                    <el-option label="post" value="post" />
                    <el-option label="put" value="put" />
                    <el-option label="delete" value="delete" />
                  </el-select>
                </el-input>
              </el-form-item>
              <el-form-item label="数据位置">
                <el-input
                  v-model="activeData.__config__.dataPath"
                  placeholder="请输入数据位置"
                  @blur="$emit('fetch-data', activeData)"
                />
              </el-form-item>

              <template v-if="activeData.props && activeData.props.props">
                <el-form-item label="标签键名">
                  <el-input v-model="activeData.props.props.label" placeholder="请输入标签键名" />
                </el-form-item>
                <el-form-item label="值键名">
                  <el-input v-model="activeData.props.props.value" placeholder="请输入值键名" />
                </el-form-item>
                <el-form-item label="子级键名">
                  <el-input v-model="activeData.props.props.children" placeholder="请输入子级键名" />
                </el-form-item>
              </template>
            </template>

            <!-- 级联选择静态树 -->
            <el-tree
              v-if="activeData.__config__.dataType === 'static'"
              draggable
              :data="activeData.options"
              node-key="id"
              :expand-on-click-node="false"
              :render-content="renderContent"
            />
            <div v-if="activeData.__config__.dataType === 'static'" style="margin-left: 20px">
              <el-button
                style="padding-bottom: 0"
                icon="el-icon-circle-plus-outline"
                type="text"
                @click="addTreeItem"
              >
                添加父级
              </el-button>
            </div>
            <el-divider />
          </template>

          <el-form-item v-if="activeData.__config__.optionType !== undefined" label="选项样式">
            <el-radio-group v-model="activeData.__config__.optionType">
              <el-radio-button label="default">
                默认
              </el-radio-button>
              <el-radio-button label="button">
                按钮
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色">
            <el-color-picker v-model="activeData['active-color']" />
          </el-form-item>
          <el-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色">
            <el-color-picker v-model="activeData['inactive-color']" />
          </el-form-item>

          <el-form-item v-if="activeData.__config__.showLabel !== undefined
            && activeData.__config__.labelWidth !== undefined" label="显示标签"
          >
            <el-switch v-model="activeData.__config__.showLabel" />
          </el-form-item>
          <el-form-item v-if="activeData.branding !== undefined" label="品牌烙印">
            <el-switch v-model="activeData.branding" @input="changeRenderKey" />
          </el-form-item>
          <el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
            <el-switch v-model="activeData['allow-half']" />
          </el-form-item>
          <el-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字">
            <el-switch v-model="activeData['show-text']" @change="rateTextChange" />
          </el-form-item>
          <el-form-item v-if="activeData['show-score'] !== undefined" label="显示分数">
            <el-switch v-model="activeData['show-score']" @change="rateScoreChange" />
          </el-form-item>
          <el-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点">
            <el-switch v-model="activeData['show-stops']" />
          </el-form-item>
          <el-form-item v-if="activeData.range !== undefined" label="范围选择">
            <el-switch v-model="activeData.range" @change="rangeChange" />
          </el-form-item>
          <el-form-item
            v-if="activeData.__config__.border !== undefined && activeData.__config__.optionType === 'default'"
            label="是否带边框"
          >
            <el-switch v-model="activeData.__config__.border" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-color-picker'" label="颜色格式">
            <el-select
              v-model="activeData['color-format']"
              placeholder="请选择颜色格式"
              :style="{ width: '100%' }"
              clearable
              @change="colorFormatChange"
            >
              <el-option
                v-for="(item, index) in colorFormatOptions"
                :key="index"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item
            v-if="activeData.size !== undefined &&
              (activeData.__config__.optionType === 'button' ||
                activeData.__config__.border ||
                activeData.__config__.tag === 'el-color-picker' ||
                activeData.__config__.tag === 'el-button')"
            label="组件尺寸"
          >
            <el-radio-group v-model="activeData.size">
              <el-radio-button label="medium">
                中等
              </el-radio-button>
              <el-radio-button label="small">
                较小
              </el-radio-button>
              <el-radio-button label="mini">
                迷你
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
            <el-switch v-model="activeData['show-word-limit']" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="严格步数">
            <el-switch v-model="activeData['step-strictly']" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="任选层级">
            <el-switch v-model="activeData.props.props.checkStrictly" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="是否多选">
            <el-switch v-model="activeData.props.props.multiple" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="展示全路径">
            <el-switch v-model="activeData['show-all-levels']" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="可否筛选">
            <el-switch v-model="activeData.filterable" />
          </el-form-item>
          <el-form-item v-if="activeData.clearable !== undefined" label="能否清空">
            <el-switch v-model="activeData.clearable" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.showTip !== undefined" label="显示提示">
            <el-switch v-model="activeData.__config__.showTip" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-upload'" label="多选文件">
            <el-switch v-model="activeData.multiple" />
          </el-form-item>
          <el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
            <el-switch v-model="activeData['auto-upload']" />
          </el-form-item>
          <el-form-item v-if="activeData.readonly !== undefined" label="是否只读">
            <el-switch v-model="activeData.readonly" />
          </el-form-item>
          <el-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
            <el-switch v-model="activeData.disabled" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-select'" label="能否搜索">
            <el-switch v-model="activeData.filterable" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.tag === 'el-select'" label="是否多选">
            <el-switch v-model="activeData.multiple" @change="multipleChange" />
          </el-form-item>
          <el-form-item v-if="activeData.__config__.required !== undefined" label="是否必填">
            <el-switch v-model="activeData.__config__.required" />
          </el-form-item>

          <template v-if="activeData.__config__.layoutTree">
            <el-divider>布局结构树</el-divider>
            <el-tree
              :data="[activeData.__config__]"
              :props="layoutTreeProps"
              node-key="renderKey"
              default-expand-all
              draggable
            >
              <span slot-scope="{ node, data }">
                <span class="node-label">
                  <svg-icon class="node-icon" :icon-class="data.__config__?data.__config__.tagIcon:data.tagIcon" />
                  {{ node.label }}
                </span>
              </span>
            </el-tree>
          </template>

          <template v-if="Array.isArray(activeData.__config__.regList)">
            <el-divider>正则校验</el-divider>
            <div
              v-for="(item, index) in activeData.__config__.regList"
              :key="index"
              class="reg-item"
            >
              <span class="close-btn" @click="activeData.__config__.regList.splice(index, 1)">
                <i class="el-icon-close" />
              </span>
              <el-form-item label="表达式">
                <el-input v-model="item.pattern" placeholder="请输入正则" />
              </el-form-item>
              <el-form-item label="错误提示" style="margin-bottom:0">
                <el-input v-model="item.message" placeholder="请输入错误提示" />
              </el-form-item>
            </div>
            <div style="margin-left: 20px">
              <el-button icon="el-icon-circle-plus-outline" type="text" @click="addReg">
                添加规则
              </el-button>
            </div>
          </template>
        </el-form>
        <!-- 表单属性 -->
        <el-form v-show="currentTab === 'form'" size="small" label-width="90px">
          <el-form-item label="表单名">
            <el-input v-model="formConf.formRef" placeholder="请输入表单名(ref)" />
          </el-form-item>
          <el-form-item label="表单模型">
            <el-input v-model="formConf.formModel" placeholder="请输入数据模型" />
          </el-form-item>
          <el-form-item label="校验模型">
            <el-input v-model="formConf.formRules" placeholder="请输入校验模型" />
          </el-form-item>
          <el-form-item label="表单尺寸">
            <el-radio-group v-model="formConf.size">
              <el-radio-button label="medium">
                中等
              </el-radio-button>
              <el-radio-button label="small">
                较小
              </el-radio-button>
              <el-radio-button label="mini">
                迷你
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="标签对齐">
            <el-radio-group v-model="formConf.labelPosition">
              <el-radio-button label="left">
                左对齐
              </el-radio-button>
              <el-radio-button label="right">
                右对齐
              </el-radio-button>
              <el-radio-button label="top">
                顶部对齐
              </el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="标签宽度">
            <el-input v-model.number="formConf.labelWidth" type="number" placeholder="请输入标签宽度" />
          </el-form-item>
          <el-form-item label="栅格间隔">
            <el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" />
          </el-form-item>
          <el-form-item label="禁用表单">
            <el-switch v-model="formConf.disabled" />
          </el-form-item>
          <el-form-item label="表单按钮">
            <el-switch v-model="formConf.formBtns" />
          </el-form-item>
          <el-form-item label="显示未选中组件边框">
            <el-switch v-model="formConf.unFocusedComponentBorder" />
          </el-form-item>
        </el-form>
      </el-scrollbar>
    </div>

    <treeNode-dialog :visible.sync="dialogVisible" title="添加选项" @commit="addNode" />
    <icons-dialog :visible.sync="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" />
  </div>
</template>

<script>
import { isArray } from 'util'
import TreeNodeDialog from './TreeNodeDialog'
import { isNumberStr } from '@/utils/index'
import IconsDialog from './IconsDialog'
import {
  inputComponents, selectComponents, layoutComponents
} from '@/components/generator/config'
import { saveFormConf } from '@/utils/db'

const dateTimeFormat = {
  date: 'yyyy-MM-dd',
  week: 'yyyy 第 WW 周',
  month: 'yyyy-MM',
  year: 'yyyy',
  datetime: 'yyyy-MM-dd HH:mm:ss',
  daterange: 'yyyy-MM-dd',
  monthrange: 'yyyy-MM',
  datetimerange: 'yyyy-MM-dd HH:mm:ss'
}

// 使changeRenderKey在目标组件改变时可用
const needRerenderList = ['tinymce']

export default {
  components: {
    TreeNodeDialog,
    IconsDialog
  },
  props: ['showField', 'activeData', 'formConf'],
  data() {
    return {
      currentTab: 'field',
      currentNode: null,
      dialogVisible: false,
      iconsVisible: false,
      currentIconModel: null,
      dateTypeOptions: [
        {
          label: '日(date)',
          value: 'date'
        },
        {
          label: '周(week)',
          value: 'week'
        },
        {
          label: '月(month)',
          value: 'month'
        },
        {
          label: '年(year)',
          value: 'year'
        },
        {
          label: '日期时间(datetime)',
          value: 'datetime'
        }
      ],
      dateRangeTypeOptions: [
        {
          label: '日期范围(daterange)',
          value: 'daterange'
        },
        {
          label: '月范围(monthrange)',
          value: 'monthrange'
        },
        {
          label: '日期时间范围(datetimerange)',
          value: 'datetimerange'
        }
      ],
      colorFormatOptions: [
        {
          label: 'hex',
          value: 'hex'
        },
        {
          label: 'rgb',
          value: 'rgb'
        },
        {
          label: 'rgba',
          value: 'rgba'
        },
        {
          label: 'hsv',
          value: 'hsv'
        },
        {
          label: 'hsl',
          value: 'hsl'
        }
      ],
      justifyOptions: [
        {
          label: 'start',
          value: 'start'
        },
        {
          label: 'end',
          value: 'end'
        },
        {
          label: 'center',
          value: 'center'
        },
        {
          label: 'space-around',
          value: 'space-around'
        },
        {
          label: 'space-between',
          value: 'space-between'
        }
      ],
      layoutTreeProps: {
        label(data, node) {
          const config = data.__config__
          return data.componentName || `${config.label}: ${data.__vModel__}`
        }
      }
    }
  },
  computed: {
    documentLink() {
      return (
        this.activeData.__config__.document
        || 'https://element.eleme.cn/#/zh-CN/component/installation'
      )
    },
    dateOptions() {
      if (
        this.activeData.type !== undefined
        && this.activeData.__config__.tag === 'el-date-picker'
      ) {
        if (this.activeData['start-placeholder'] === undefined) {
          return this.dateTypeOptions
        }
        return this.dateRangeTypeOptions
      }
      return []
    },
    tagList() {
      return [
        {
          label: '输入型组件',
          options: inputComponents
        },
        {
          label: '选择型组件',
          options: selectComponents
        }
      ]
    },
    activeTag() {
      return this.activeData.__config__.tag
    },
    isShowMin() {
      return ['el-input-number', 'el-slider'].indexOf(this.activeTag) > -1
    },
    isShowMax() {
      return ['el-input-number', 'el-slider', 'el-rate'].indexOf(this.activeTag) > -1
    },
    isShowStep() {
      return ['el-input-number', 'el-slider'].indexOf(this.activeTag) > -1
    }
  },
  watch: {
    formConf: {
      handler(val) {
        saveFormConf(val)
      },
      deep: true
    }
  },
  methods: {
    addReg() {
      this.activeData.__config__.regList.push({
        pattern: '',
        message: ''
      })
    },
    addSelectItem() {
      this.activeData.__slot__.options.push({
        label: '',
        value: ''
      })
    },
    addTreeItem() {
      ++this.idGlobal
      this.dialogVisible = true
      this.currentNode = this.activeData.options
    },
    renderContent(h, { node, data, store }) {
      return (
        <div class="custom-tree-node">
          <span>{node.label}</span>
          <span class="node-operation">
            <i on-click={() => this.append(data)}
              class="el-icon-plus"
              title="添加"
            ></i>
            <i on-click={() => this.remove(node, data)}
              class="el-icon-delete"
              title="删除"
            ></i>
          </span>
        </div>
      )
    },
    append(data) {
      if (!data.children) {
        this.$set(data, 'children', [])
      }
      this.dialogVisible = true
      this.currentNode = data.children
    },
    remove(node, data) {
      this.activeData.__config__.defaultValue = [] // 避免删除时报错
      const { parent } = node
      const children = parent.data.children || parent.data
      const index = children.findIndex(d => d.id === data.id)
      children.splice(index, 1)
    },
    addNode(data) {
      this.currentNode.push(data)
    },
    setOptionValue(item, val) {
      item.value = isNumberStr(val) ? +val : val
    },
    setDefaultValue(val) {
      if (Array.isArray(val)) {
        return val.join(',')
      }
      // if (['string', 'number'].indexOf(typeof val) > -1) {
      //   return val
      // }
      if (typeof val === 'boolean') {
        return `${val}`
      }
      return val
    },
    onDefaultValueInput(str) {
      if (isArray(this.activeData.__config__.defaultValue)) {
        // 数组
        this.$set(
          this.activeData.__config__,
          'defaultValue',
          str.split(',').map(val => (isNumberStr(val) ? +val : val))
        )
      } else if (['true', 'false'].indexOf(str) > -1) {
        // 布尔
        this.$set(this.activeData.__config__, 'defaultValue', JSON.parse(str))
      } else {
        // 字符串和数字
        this.$set(
          this.activeData.__config__,
          'defaultValue',
          isNumberStr(str) ? +str : str
        )
      }
    },
    onSwitchValueInput(val, name) {
      if (['true', 'false'].indexOf(val) > -1) {
        this.$set(this.activeData, name, JSON.parse(val))
      } else {
        this.$set(this.activeData, name, isNumberStr(val) ? +val : val)
      }
    },
    setTimeValue(val, type) {
      const valueFormat = type === 'week' ? dateTimeFormat.date : val
      this.$set(this.activeData.__config__, 'defaultValue', null)
      this.$set(this.activeData, 'value-format', valueFormat)
      this.$set(this.activeData, 'format', val)
    },
    spanChange(val) {
      this.formConf.span = val
    },
    multipleChange(val) {
      this.$set(this.activeData.__config__, 'defaultValue', val ? [] : '')
    },
    dateTypeChange(val) {
      this.setTimeValue(dateTimeFormat[val], val)
    },
    rangeChange(val) {
      this.$set(
        this.activeData.__config__,
        'defaultValue',
        val ? [this.activeData.min, this.activeData.max] : this.activeData.min
      )
    },
    rateTextChange(val) {
      if (val) this.activeData['show-score'] = false
    },
    rateScoreChange(val) {
      if (val) this.activeData['show-text'] = false
    },
    colorFormatChange(val) {
      this.activeData.__config__.defaultValue = null
      this.activeData['show-alpha'] = val.indexOf('a') > -1
      this.activeData.__config__.renderKey = +new Date() // 更新renderKey,重新渲染该组件
    },
    openIconsDialog(model) {
      this.iconsVisible = true
      this.currentIconModel = model
    },
    setIcon(val) {
      this.activeData[this.currentIconModel] = val
    },
    tagChange(tagIcon) {
      let target = inputComponents.find(item => item.__config__.tagIcon === tagIcon)
      if (!target) target = selectComponents.find(item => item.__config__.tagIcon === tagIcon)
      this.$emit('tag-change', target)
    },
    changeRenderKey() {
      if (needRerenderList.includes(this.activeData.__config__.tag)) {
        this.activeData.__config__.renderKey = +new Date()
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.right-board {
  width: 350px;
  position: absolute;
  right: 0;
  top: 0;
  padding-top: 3px;
  .field-box {
    position: relative;
    height: calc(100vh - 42px);
    box-sizing: border-box;
    overflow: hidden;
  }
  .el-scrollbar {
    height: 100%;
  }
}
.select-item {
  display: flex;
  border: 1px dashed #fff;
  box-sizing: border-box;
  & .close-btn {
    cursor: pointer;
    color: #f56c6c;
  }
  & .el-input + .el-input {
    margin-left: 4px;
  }
}
.select-item + .select-item {
  margin-top: 4px;
}
.select-item.sortable-chosen {
  border: 1px dashed #409eff;
}
.select-line-icon {
  line-height: 32px;
  font-size: 22px;
  padding: 0 4px;
  color: #777;
}
.option-drag {
  cursor: move;
}
.time-range {
  .el-date-editor {
    width: 227px;
  }
  ::v-deep .el-icon-time {
    display: none;
  }
}
.document-link {
  position: absolute;
  display: block;
  width: 26px;
  height: 26px;
  top: 0;
  left: 0;
  cursor: pointer;
  background: #409eff;
  z-index: 1;
  border-radius: 0 0 6px 0;
  text-align: center;
  line-height: 26px;
  color: #fff;
  font-size: 18px;
}
.node-label{
  font-size: 14px;
}
.node-icon{
  color: #bebfc3;
}
</style>

  1. proce —> fg ----> TreeNodeDialog.vue
<template>
  <div>
    <el-dialog
      v-bind="$attrs"
      :close-on-click-modal="false"
      :modal-append-to-body="false"
      v-on="$listeners"
      @open="onOpen"
      @close="onClose"
    >
      <el-row :gutter="0">
        <el-form
          ref="elForm"
          :model="formData"
          :rules="rules"
          size="small"
          label-width="100px"
        >
          <el-col :span="24">
            <el-form-item
              label="选项名"
              prop="label"
            >
              <el-input
                v-model="formData.label"
                placeholder="请输入选项名"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item
              label="选项值"
              prop="value"
            >
              <el-input
                v-model="formData.value"
                placeholder="请输入选项值"
                clearable
              >
                <el-select
                  slot="append"
                  v-model="dataType"
                  :style="{width: '100px'}"
                >
                  <el-option
                    v-for="(item, index) in dataTypeOptions"
                    :key="index"
                    :label="item.label"
                    :value="item.value"
                    :disabled="item.disabled"
                  />
                </el-select>
              </el-input>
            </el-form-item>
          </el-col>
        </el-form>
      </el-row>
      <div slot="footer">
        <el-button
          type="primary"
          @click="handelConfirm"
        >
          确定
        </el-button>
        <el-button @click="close">
          取消
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { isNumberStr } from '@/utils/index'
import { getTreeNodeId, saveTreeNodeId } from '@/utils/db'

const id = getTreeNodeId()

export default {
  components: {},
  inheritAttrs: false,
  props: [],
  data() {
    return {
      id,
      formData: {
        label: undefined,
        value: undefined
      },
      rules: {
        label: [
          {
            required: true,
            message: '请输入选项名',
            trigger: 'blur'
          }
        ],
        value: [
          {
            required: true,
            message: '请输入选项值',
            trigger: 'blur'
          }
        ]
      },
      dataType: 'string',
      dataTypeOptions: [
        {
          label: '字符串',
          value: 'string'
        },
        {
          label: '数字',
          value: 'number'
        }
      ]
    }
  },
  computed: {},
  watch: {
    // eslint-disable-next-line func-names
    'formData.value': function (val) {
      this.dataType = isNumberStr(val) ? 'number' : 'string'
    },
    id(val) {
      saveTreeNodeId(val)
    }
  },
  created() {},
  mounted() {},
  methods: {
    onOpen() {
      this.formData = {
        label: undefined,
        value: undefined
      }
    },
    onClose() {},
    close() {
      this.$emit('update:visible', false)
    },
    handelConfirm() {
      this.$refs.elForm.validate(valid => {
        if (!valid) return
        if (this.dataType === 'number') {
          this.formData.value = parseFloat(this.formData.value)
        }
        this.formData.id = this.id++
        this.$emit('commit', this.formData)
        this.close()
      })
    }
  }
}
</script>

<style lang="scss" scoped>
</style>

  1. proce —> form —> PermisForm.vue
<template>
  <el-dialog title="谁可以发起" :visible.sync="visible" width="1440px" top="5vh" append-to-body>
    <el-row type="flex">
      <el-col :span="3"></el-col>
      <el-col :span="14">
        <el-radio-group v-model="pType" @change="selectType" size="medium">
          <el-radio-button label="1">成员</el-radio-button>
          <el-radio-button label="2">角色</el-radio-button>
        </el-radio-group>
      </el-col>
      <el-col :span="3">
        <el-button type="primary" @click="subData">确 定</el-button>
        <el-button style="margin-left: 0px;" @click="cancleSub">取 消</el-button>
      </el-col>
    </el-row>
    <el-row type="flex" style="margin-top: 25px; margin-bottom: 80px;" shadow="never">
      <el-col :span="3"></el-col>
      <el-col :span="20">
        <el-transfer v-model="value" :data="datas" :titles="titles"></el-transfer>
      </el-col>
      <el-col :span="1"></el-col>
    </el-row>
  </el-dialog>
</template>

<script>
  import { listUser } from '@/api/system/user'
  import { listRole } from "@/api/system/role"
  import { updateProce } from '@/api/fp/proce'

  export default {
    name: 'PermisForm',
    data() {
      return {
        row: {},
        value: [],
        userVal: [],
        roleVal: [],
        datas: [],
        loading: false,
        visible: false,
        pType: '1',
        titles: ['未选成员', '已选成员']
      }
    },
    created() {
    },
    methods: {
      show(row) {
        this.row = row
        this.datas = []
        this.userVal = this.formatVal(row.actionUserId)
        this.roleVal = this.formatVal(row.actionRoleId)
        this.value = this.userVal
        this.titles = ['未选成员', '已选成员']
        this.pType = '1'
        this.visible = true
        this.getList('1')
      },
      async getList(flag) {
        this.loading = true
        flag == '1' ? await listUser().then(response => {
          let datas = []
          response.rows.map(item => {
            datas = [...datas, { key: item.userId, label: item.nickName }]
          })
          this.datas = datas
          this.loading = false
        }) : await listRole().then(response => {
          let datas = []
          response.rows.map(item => {
            datas = [...datas, { key: item.roleId, label: item.roleName }]
          })
          this.datas = datas
          this.loading = false
        })
      },
      selectType(flag) {
        let data = this.value
        this.titles = flag == '1' ? ['未选成员', '已选成员'] : ['未选角色', '已选角色']
        this.value = flag == '1'?this.userVal:this.roleVal
        flag == '1'?this.roleVal = data:this.userVal = data
        this.getList(flag)
      },
      cancleSub(){
        this.visible = false
        this.row = {}
      },
      subData(){
        let ids = ''
        this.value.map(d => {
          ids += d + ','
        })
        let param = {
          proId: this.row.proId,
          actionUserId: this.pType == '1' && ids?ids.substring(0,ids.length-1):'',
          actionRoleId: this.pType == '2' && ids?ids.substring(0,ids.length-1):'',
        }
        updateProce(param).then(res =>{
          this.msgSuccess('设置成功!')
          this.cancleSub()
          this.$emit('getList')
        })
      },
      formatVal(val){
        let data = []
        if(val){
          val.split(',').map(item => {
            data = [...data, Number(item)]
          })
        }
        return data
      },
    }
  }

</script>
<style>

  .el-transfer-panel {
    width: 391px;
  }
</style>


  1. proce —> form —> UserForm.vue
<template>
  <el-dialog :title="title" :visible.sync="visible" width="625px" top="5vh" append-to-body>
    <el-transfer v-model="value" :data="datas" :titles="titles"></el-transfer>
    <div slot="footer" class="dialog-footer">
      <el-button type="primary" @click="subUser">确 定</el-button>
      <el-button @click="cancleSub">取 消</el-button>
    </div>
  </el-dialog>
</template>

<script>

  import { listUser } from '@/api/system/user'
  import { listRole } from "@/api/system/role"

  export default {
    name: 'UserForm',
    data() {
      return {
        value: [],
        datas: [],
        loading: false,
        visible: false,
        titles: ['未选成员', '已选成员'],
        title: '添加成员',

      }
    },
    created() {
    },
    methods: {
      show(row){
        this.visible = true
        this.row = row
        this.value = row.value
        this.getList(row.flag)
        this.initBaseInfo(row.flag)
      },
      initBaseInfo(flag){
        this.titles = ['未选角色', '已选角色']
        this.title = '添加角色'
        if(flag == '1'){
          this.titles = ['未选成员', '已选成员']
          this.title = '添加成员'
        }
      },
      async getList(flag) {
        this.loading = true
        flag == '1' ? await listUser().then(response => {
          let datas = []
          response.rows.map(item => {
            datas = [...datas, { key: item.userId, label: item.nickName }]
          })
          this.datas = datas
          this.loading = false
        }) : await listRole().then(response => {
          let datas = []
          response.rows.map(item => {
            datas = [...datas, { key: item.roleId, label: item.roleName }]
          })
          this.datas = datas
          this.loading = false
        })
      },
      /*添加用户*/
      subUser(){
        let fpDefExams = []
        this.value.map(item =>{
          fpDefExams = [...fpDefExams, {
            userId: this.row.flag == '1'?item:null,
            roleId: this.row.flag == '2'?item:null,
            emptyType: this.row.emptyType,
            userNameDictText: this.row.flag == '1'?this.datas.filter(d => d.key == item)[0].label:null,
            roleNameDictText: this.row.flag == '2'?this.datas.filter(d => d.key == item)[0].label:null
          }]
        })
        this.$emit('setUserVal', {...this.row, userId: this.row.flag == '1'?this.value:null,roleId: this.row.flag == '2'?this.value:null, fpDefExams})
        this.cancleSub()
      },
      cancleSub(){
        this.visible = false
        this.row = null
        this.value = []
      }
    }
  }

</script>


  1. proce —> index.vue
<template>
  <div class="app-container">
    <div v-if="flag=='1'">
      <div class="demo-input-suffix">
        <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"  @submit.native.prevent>
          <el-form-item label="流程设计" prop="proName">
            <el-input
              placeholder="请输入流程名称进行搜索"
              v-model="queryParams.proName"
              prefix-icon="el-icon-search"
              clearable
              size="small"
              @keyup.enter.native="handleQuery"
            />
          </el-form-item>
          <el-row style="float: right;">
            <el-button
              type="primary"
              plain
              @click="handleAdd"
              v-hasPermi="['fp:group:add']"
            >新建分组
            </el-button>
            <!--<el-button>分组排序</el-button>-->
            <el-button type="primary" icon="el-icon-plus" @click="createFp">创建新流程</el-button>
          </el-row>
        </el-form>
      </div>
      <div v-for="(item, index) in datas">
        <el-card class="box-card">
          <div slot="header" class="clearfix">
            <span>{{item.groupName}}({{item.procs.length}})</span>
            <el-link icon="el-icon-sort-up" :underline="false" style="float: right;"
                     @click="orderGroup(true, item, index)">向上
            </el-link>
            <el-link icon="el-icon-sort-down" :underline="false" style="float: right;"
                     @click="orderGroup(false, item, index)">向下
            </el-link>
            <el-dropdown style="float: right; padding: 0px 15px 0px 0">
            <span class="el-dropdown-link">
              <el-link :underline="false" icon="el-icon-setting">编辑分组</el-link>
            </span>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item>
                  <el-link :underline="false" @click="reName(item)">重命名</el-link>
                </el-dropdown-item>
                <el-dropdown-item>
                  <el-link :underline="false" @click="delGroupData(item)">删除分组</el-link>
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
          <div v-for="(proc,index) in item.procs" :key="proc.proId" class="text item">
            <el-row style="margin-top: 10px;">
              <el-col :span="6">
                <!--<el-image
                  style="float: left;"
                  :src="url"
                  fit="cover"></el-image>-->
                <el-avatar shape="square" size="large" :src="url" style="margin-top: 14px;float: left;"></el-avatar>
                <p style="margin-left: 45px;color: #282727;line-height: 36px;font-size: 16px">{{proc.proName}}</p>
              </el-col>
              <el-col :span="8"><span>
               <p style="margin-top: -1px;color: #9B9B9B">权限:</p><div>
              谁可以发起:
              <span v-if="proc.actionRoleId_dictTexts">
                {{proc.actionRoleId_dictTexts|nameFormat}}
              </span>
              <span v-if="proc.actionUserId_dictTexts">
                {{proc.actionUserId_dictTexts|nameFormat}}
              </span>
                <span v-if="!proc.actionRoleId_dictTexts && !proc.actionUserId_dictTexts">
                  无
                </span>
              <el-button
                size="mini"
                type="text" @click="addFpPermis(proc)">修改</el-button>
            </div>
            </span>
              </el-col>
              <el-col :span="4"><p style="margin-top: -1px;color: #9B9B9B">最后更新:</p>
                <div>{{proc.lastActionTime}}</div>
              </el-col>
              <el-col :span="6">
                <el-link :underline="false" type="primary" style="margin: 15px 0px 15px 250px " @click="editFp(proc)">
                  编辑
                </el-link>
                <el-divider direction="vertical"></el-divider>
                <el-link :underline="false" type="primary" @click="groupMove(proc)">移动到</el-link>
                <el-divider direction="vertical"></el-divider>
                <el-dropdown>
              <span class="el-dropdown-link">
                <el-link :underline="false" type="primary">数据管理</el-link>
              </span>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item>
                      <el-link :underline="false" type="primary" @click="actionProcStatus(proc)">{{proc.status ==
                        '1'?'停用':'发布'}}
                      </el-link>
                    </el-dropdown-item>
                    <el-dropdown-item>
                      <el-link :underline="false" type="primary" @click="delFp(proc)">删除</el-link>
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </el-col>
            </el-row>
            <el-divider v-if="index != item.procs.length-1"/>
          </div>
        </el-card>
      </div>
    </div>
    <div v-if="flag=='2'">
      <el-row>
        <el-col :span="24">
          <el-card class="box-card">
            <el-row>
              <el-col :span="9">
                <el-button style="margin-top: 20px" icon="el-icon-arrow-left" @click="callMainPage">返回</el-button>
                <span style="margin-left: 35px;font-size: 18px;font-weight: bold;">{{baseForm.proName}}</span>
              </el-col>
              <el-col :span="10">
                <ul>
                  <li style="margin-top: 28px;list-style: none;display: inline">1.
                    <el-link
                      @click="showTag('base')"
                      :style="selectFlag=='base'?'font-size: 16px; border-bottom: 1px solid #409EFF;':'font-size: 16px;'">
                      <p style="margin-top: 14px; margin-right: 14px;">基础设置</p></el-link>
                  </li>
                  <li style="margin-top: 28px;list-style: none;display: inline">2.
                    <el-link
                      @click="showTag('form')"
                      :style="selectFlag=='form'?'font-size: 16px; border-bottom: 1px solid #409EFF;':'font-size: 16px;'">
                      <p style="margin-top: 14px;margin-right: 14px">表单设计</p></el-link>
                  </li>
                  <li style="margin-top: 28px;list-style: none;display: inline">3.
                    <el-link
                      @click="showTag('proc')"
                      :style="selectFlag=='proc'?'font-size: 16px; border-bottom: 1px solid #409EFF;':'font-size: 16px;'">
                      <p style="margin-top: 14px;">流程设计</p></el-link>
                  </li>
                </ul>

              </el-col>
              <el-col :span="5">
                <el-button style="float: right;margin: 25px 2px;" :type="baseForm.status == '1'?'danger':'info'"
                           @click="actionProcStatus({proName: baseForm.proName, proId: baseForm.proId, status: baseForm.status})">
                  {{ baseForm.status ==
                  '1'?'停用':'发布'}}
                </el-button>
                <el-button style="float: right;margin: 25px 2px;" type="primary" @click="saveDatas">保存</el-button>
                <el-button style="float: right;margin: 25px 2px;" @click="showTag('show')">预览</el-button>
              </el-col>
            </el-row>
          </el-card>
        </el-col>
      </el-row>

      <el-row type="flex" class="row-bg" style="margin-top: 10px" v-if="selectFlag=='base'">
        <el-col :span="7">
        </el-col>
        <el-col :span="10">
          <el-card class="box-card">
            <el-form ref="baseForm" style="margin-top: 60px;margin-bottom: 80px;" :model="baseForm" :rules="baseRules"
                     label-width="120px">
              <!--              <el-form-item label="图标" prop="imageUrl">
                              <el-upload
                                class="avatar-uploader"
                                action="https://jsonplaceholder.typicode.com/posts/"
                                :show-file-list="false"
                                :on-success="handleAvatarSuccess"
                                :before-upload="beforeAvatarUpload">
                                <img v-if="baseForm.imageUrl" :src="baseForm.imageUrl" class="avatar">
                                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                              </el-upload>
                            </el-form-item>-->
              <el-form-item label="流程名称" prop="proName">
                <el-input
                  style="width: 520px"
                  v-model="baseForm.proName"
                  placeholder="请输入流程名称"/>
              </el-form-item>
              <el-form-item label="所在分组" prop="proGroup">
                <el-select
                  v-model="baseForm.proGroup"
                  clearable
                  style="width: 520px;"
                  size="small"
                  placeholder="请选择所在分组">
                  <el-option
                    v-for="data in datas"
                    :key="data.id"
                    :label="data.groupName"
                    :value="data.id"
                  ></el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="流程说明" prop="proDesc">
                <el-input
                  style="width: 520px"
                  type="textarea"
                  :rows="4"
                  v-model="baseForm.proDesc"
                  placeholder="请输入流程说明"/>
              </el-form-item>
            </el-form>
          </el-card>
        </el-col>
        <el-col :span="7">
        </el-col>
      </el-row>

      <el-row type="flex" class="row-bg" style="margin-top: 10px" v-if="selectFlag=='form'">
        <fg ref="fg"></fg>
      </el-row>

      <el-row v-if="selectFlag=='proc'">
        <el-card class="box-card" style="margin-top: 10px;height: 95vh;">
          <el-row style="margin-top: 20px">
            <el-alert
              title="需要分支可以设置审批条件,可作为审批条件的控件包含:数字输入框、单选框、下拉选择框,且控件需为必填字段"
              type="warning"
              :closable="false"
              show-icon>
            </el-alert>
          </el-row>
          <el-row>
            <el-card style="margin-top: 20px;margin-right: 80px;height: 16vh" :border="true" shadow="never">
              <div style="margin: 30px 0px">
                <div style="float: left; margin: 40px 20px 0px 20px">
                  <el-popover
                    placement="right"
                    width="300"
                    trigger="click">
                    <el-row>
                      <el-col :span="12" v-for="item in nodeTypes">
                        <el-link :underline="false" @click="actionNode(item.dictValue, '0')">
                          <el-card shadow="never" class="my-card">
                            {{item.dictLabel}}
                          </el-card>
                        </el-link>
                      </el-col>
                    </el-row>
                    <el-link
                      slot="reference"
                      type="primary"
                      :underline="false"
                      icon="el-icon-circle-plus"
                      style="font-size: 14px;">
                    </el-link>
                  </el-popover>
                </div>

                <div style="float: left;" v-for="(nodeData, i) in nodeDatas">
                  <div style="float: left;margin-top: -15px;" :class="[{'item-card': true, 'item-card-0': nodeData.nodeType == '0', 'item-card-1': nodeData.nodeType == '1', 'item-card': nodeData.nodeType == '2'}]">
                    <el-card class="box-card" style="width: 150px;">
                      <div slot="header" class="clearfix" style="height: 8px;font-size: 8px;">
                        <span>{{nodeData.nodeName}}</span>
                        <el-link style="float: right; padding: 3px 0" icon="el-icon-close" :underline="false"
                                 type="warning" @click="removeNode(i)"></el-link>
                      </div>
                      <div :key="1" class="text item" style="height: 80px;">
                        <el-link :underline="false" @click="showNodeAction(nodeData, i)">
                          <p style="display: inline;" v-if="nodeData.nodeType != '2'">{{nodeData |
                            filterExams}}</p>
                          <p style="display: inline;" v-if="nodeData.nodeType == '2'">
                            {{nodeData.fpDefCallBack?nodeData.fpDefCallBack.callbackName:''}}</p>
                          <i class="el-icon-arrow-right"
                             style="display: inline;float: right;margin-top: 4px;margin-left: 8px;"></i>
                        </el-link>
                      </div>
                    </el-card>
                  </div>

                  <div style="float: left; margin: 40px 20px 0px 20px" v-if="i != nodeDatas.length-1">
                    <el-popover
                      placement="right"
                      width="300"
                      trigger="click">
                      <el-row>
                        <el-col :span="12" v-for="item in nodeTypes">
                          <el-link :underline="false" @click="actionNode(item.dictValue, (i + 1) + '')">
                            <el-card shadow="never" class="my-card">
                              {{item.dictLabel}}
                            </el-card>
                          </el-link>
                        </el-col>
                      </el-row>
                      <el-link
                        slot="reference"
                        type="primary"
                        :underline="false"
                        :style="linkIcon.length > 0 && linkIcon[i + '']?'font-size: 14px; padding-right: 10px;':'font-size: 24px;'">
                        <i
                          :class="linkIcon.length > 0 && linkIcon[i + '']?linkIcon[i + '']:'el-icon-caret-right'"
                          @mouseover="mouseAction(true, i + '')"
                          @mouseleave="mouseAction(false, i + '')"></i>
                      </el-link>
                    </el-popover>
                  </div>
                </div>

                <div style="float: left;margin: 12px 20px 0px">
                  <el-popover
                    placement="right"
                    width="300"
                    trigger="click">
                    <el-row>
                      <el-col :span="12" v-for="item in nodeTypes">
                        <el-link :underline="false" @click="actionNode(item.dictValue, nodeDatas.length)">
                          <el-card shadow="never" class="my-card">
                            {{item.dictLabel}}
                          </el-card>
                        </el-link>
                      </el-col>
                    </el-row>
                    <el-button
                      slot="reference"
                      class="add-node"
                      icon="el-icon-plus"
                      style="height:75px;width:75px"
                      circle></el-button>
                  </el-popover>
                  <div style="font-size: 10px;">添加审批节点</div>
                </div>

              </div>
            </el-card>
            <el-card v-if="showNode" style="margin-top: 20px;margin-right: 80px;height: 52vh" :border="true"
                     shadow="never">
              <div v-if="nodeType=='0'">
                <el-tabs v-model="activeName" @tab-click="handleClick">
                  <el-tab-pane label="设置审批人" name="first"/>
                  <el-tab-pane label="表单操作权限" name="second"/>
                </el-tabs>
                <div v-if="activeName == 'first'">
                  <el-row class="radio-row">
                    <el-radio-group v-model="radio">
                      <el-radio-button label="成员"></el-radio-button>
                      <el-radio-button label="角色"></el-radio-button>
                    </el-radio-group>
                  </el-row>

                  <el-row class="radio-row" v-if="radio == '成员'">
                    <el-button type="primary" icon="el-icon-plus" size="small" @click="addUser">添加成员</el-button>
                    <p class="row-p">不能超过20人</p>
                    <div v-if="nodeDatas[currentNode].fpDefExams.length > 0" style="margin-top: 5px;">
                      <el-tag v-for="fde in nodeDatas[currentNode].fpDefExams" v-if="fde.userNameDictText" size="mini"
                              closable>{{fde.userNameDictText}}
                      </el-tag>
                    </div>
                  </el-row>

                  <el-row class="radio-row" v-else>
                    <el-button type="primary" icon="el-icon-plus" size="small" @click="addUser">添加角色</el-button>
                    <p class="row-p">点击添加审批角色</p>
                    <div v-if="nodeDatas[currentNode].fpDefExams.length > 0" style="margin-top: 5px;">
                      <el-tag v-for="fde in nodeDatas[currentNode].fpDefExams" v-if="fde.roleNameDictText" size="mini"
                              closable>{{fde.roleNameDictText}}
                      </el-tag>
                    </div>
                  </el-row>

                  <el-divider/>
                  <el-row class="radio-row">
                    <p class="empty-p">审批人为空时</p>

                    <el-radio-group v-model="emptyType" @change="emptyTypeChange">
                      <el-radio :label="0">自动通过</el-radio>
                      <el-radio :label="1">自动拒绝</el-radio>
                      <!-- <el-radio :label="2">指定成员</el-radio>-->
                    </el-radio-group>

                  </el-row>
                </div>
                <div v-else class="serve-form">
                  <el-row class="radio-row">
                    <p class="empty-p">节点操作</p>
                    <el-checkbox-group
                      @change="nodeActionTypeChange"
                      v-model="nodeActionType">
                      <el-checkbox v-for="dict in nodeActionTypes" :disabled="dict.dictValue == '0'"
                                   :label="dict.dictValue" :key="dict.dictValue">{{dict.dictLabel}}
                      </el-checkbox>
                    </el-checkbox-group>
                  </el-row>
                  <el-divider/>
                  <el-row class="radio-row">
                    <p class="empty-p">节点权限</p>
                    <p style="display: inline;font-size: 10px;">表单字段:</p>
                    <el-checkbox :value="true" disabled>只读</el-checkbox>
                  </el-row>
                  <el-row class="radio-row"
                          v-if="nodeDatas[currentNode].nodeAction.indexOf('3') == -1 && nodeDatas[currentNode].nodeSort != nodeDatas.filter(o => o.nodeSort == '0')[0].nodeSort">
                    <p style="display: inline;font-size: 10px;">标记为:</p>
                    <el-radio-group @change="markedAsChange" v-model="markedAs">
                      <el-radio :label="0">普通节点</el-radio>
                      <el-radio :label="1">处理异议节点</el-radio>
                    </el-radio-group>
                  </el-row>
                  <el-divider/>
                  <el-row class="radio-row">
                    <p class="empty-p">节点描述</p>
                    <el-input
                      type="textarea"
                      :rows="2"
                      placeholder="请输入节点描述"
                      v-model="nodeDatas[currentNode].remark">
                    </el-input>
                  </el-row>
                </div>
              </div>
              <div v-if="nodeType=='1'">
                <el-tabs v-model="activeName" @tab-click="handleClick">
                  <el-tab-pane label="设置抄送人" name="first"/>
                  <el-tab-pane label="表单操作权限" name="second"/>
                </el-tabs>
                <div v-if="activeName == 'first'">
                  <el-row class="radio-row">
                    <el-radio-group v-model="radio" v-if="activeName == 'first'">
                      <el-radio-button label="成员"></el-radio-button>
                      <el-radio-button label="角色"></el-radio-button>
                    </el-radio-group>
                  </el-row>

                  <el-row class="radio-row" v-if="radio == '成员'">
                    <el-button type="primary" icon="el-icon-plus" size="small" @click="addUser({nodeType: '1'})">添加成员
                    </el-button>
                    <p class="row-p">不能超过20人</p>
                  </el-row>

                  <el-row class="radio-row" v-else>
                    <el-button type="primary" icon="el-icon-plus" size="small" @click="addUser({nodeType: '1'})">添加角色
                    </el-button>
                    <p class="row-p">点击添加审批角色</p>
                  </el-row>
                </div>
                <div v-else class="serve-form">
                  <el-row class="radio-row">
                    <p class="empty-p">节点权限</p>
                    <p style="display: inline;font-size: 10px;">表单字段:</p>
                    <el-checkbox :value="true" disabled>只读</el-checkbox>
                  </el-row>
                  <el-divider/>
                  <el-row class="radio-row">
                    <p class="empty-p">节点描述</p>
                    <el-input
                      type="textarea"
                      :rows="2"
                      placeholder="请输入节点描述"
                      v-model="nodeDatas[currentNode].remark">
                    </el-input>
                  </el-row>
                </div>
              </div>
              <div v-if="nodeType=='2'">
                <el-tabs v-model="activeName" @tab-click="handleClick">
                  <el-tab-pane label="普通模式" name="first"/>
                </el-tabs>
                <el-form ref="serveForm" :model="serveForm" :rules="serveRules" label-width="160px" class="serve-form">
                  <el-form-item label="表达式" prop="javaBean">
                    <el-select
                      v-model="serveForm.javaBean"
                      clearable
                      size="small"
                      @change="setFpCallBacks"
                      placeholder="请选择表达式">
                      <el-option
                        v-for="dict in javaBeans"
                        :key="dict.javaBean"
                        :label="dict.callbackName"
                        :value="dict.javaBean"
                      ></el-option>
                    </el-select>
                  </el-form-item>
                  <el-form-item label="节点描述" prop="remark">
                    <el-input
                      type="textarea"
                      :rows="2"
                      placeholder="请输入节点描述"
                      v-model="nodeDatas[currentNode].remark">
                    </el-input>
                  </el-form-item>
                </el-form>
              </div>
              <div style="text-align:left;margin-top: 50px;margin-left: 100px;">
                <el-button type="primary" @click="submitNodeForm">确 定</el-button>
                <el-button>取 消</el-button>
              </div>
            </el-card>
          </el-row>
        </el-card>

      </el-row>

      <el-row type="flex" class="row-bg" style="margin-top: 10px" v-if="selectFlag=='show'">
        <el-col :span="24">
          <el-card class="fp-card" v-if="activities.length > 0">
            <div slot="header" class="clearfix">
              <span>预览流程样式</span>
            </div>
            <div class="text" style="margin-top: 18px;">
              <el-timeline>
                <el-timeline-item
                  v-for="(activity, index) in activities"
                  :key="index"
                  :type="activity.status=='1'?'primary':''"
                  :timestamp="activity.timestamp">
                  <p style="font-size: 18px;">{{activity.title}}</p>
                  <p style="color: #909399;"> {{activity.approver}}</p>
                  <p v-if="activity.remark" style="display: inline;font-size: 16px;">描述:</p>
                  <p style="display: inline;">{{activity.remark}}</p>
                </el-timeline-item>
              </el-timeline>
            </div>
          </el-card>
          <el-card class="fp-card" v-else>
            <el-empty description="请先设计流程!"></el-empty>
          </el-card>
        </el-col>
      </el-row>
    </div>

    <!-- 添加或修改流程分组对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="组名" prop="groupName">
          <el-input v-model="form.groupName" placeholder="请输入组名"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

    <!--分组重命名-->
    <el-dialog title="分组重命名" :visible.sync="opengroup" width="680px" append-to-body>
      <el-form ref="formgroup" :model="formgroup" :rules="rulegroups" label-width="120px" class="action-form">
        <el-form-item label="新的分组名称" prop="groupName">
          <el-input v-model="formgroup.groupName" placeholder="请输入新的分组名称"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="subData">确 定</el-button>
        <el-button @click="cancelgroup">取 消</el-button>
      </div>
    </el-dialog>

    <!--移动到分组-->
    <el-dialog title="移动到分组" :visible.sync="openMove" width="680px" append-to-body>
      <el-form ref="formMove" :model="formMove" :rules="ruleMoves" label-width="120px" class="action-form">
        <el-form-item label="分组名称" prop="proGroup">
          <el-select
            v-model="formMove.proGroup"
            clearable
            style="width: 520px;"
            size="small"
            placeholder="请选择分组">
            <el-option
              v-for="data in datas"
              :key="data.id"
              :label="data.groupName"
              :value="data.id"
            ></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="subMove">确 定</el-button>
        <el-button @click="cancelMove">取 消</el-button>
      </div>
    </el-dialog>

    <user-form ref="userForm" @setUserVal="setUserVal"></user-form>

    <permis-form ref="permisForm" @getList="getList"></permis-form>
  </div>
</template>

<script>
  import { addProce, delProce, getProce, queryPs, setStatus, updateProce } from '@/api/fp/proce'
  import { allCallback, insertNode, listNodeDatas } from '@/api/fp/node'
  import { addGroup, allGroup, delGroup, sortGroup, updateGroup } from '@/api/fp/group'
  import fg from './fg/index'
  import UserForm from './form/UserForm'
  import PermisForm from './form/PermisForm'
  import { removeDrawingList, removeFormConf, saveDrawingList, saveFormConf } from '@/utils/db'

  export default {
    name: 'Proce',
    components: { fg, UserForm, PermisForm },
    data() {
      return {
        url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAGgElEQVR4Xu2beXDNVxTHP79EQixFpESYNragtpZExdCOpRRtTSqYRlOtQUvRTFFT+odpTTvVIjOUGVWjllYZW221pLRqp5bIlFoSQSVMSMSSIHmdkyveE0neu7/3exlZzkwm80vOueec7+/ec84993cNClDgTFsrmxfDDXgFeAaoUZCnlD1nAsk22GbksiDlYyPB0X7j4cNUm29gLWbYYJQB3qXMSZfMtUGOAfNS0hnPVOOuCCkAxPnabMJGD5dGKu1MBnEp1+krIOQBEBhrmw2MKe1+ado/JyXGGGs8WPPHyuq0LwqUvOWQSzujXqxtlgExmuiVCXYbxBqBsbYTQKsy4ZG+EwkCwI0ykOr0XVcSmQKAzax0WZCrAKBiBlQsgZKJAXX8oH8I9GoETWrD01UhKwcuZMC+/2DVSTiaWvJRxeMxoJIXjAuFUR2gum/xDu5Igik7ITGj5IDwKAD+VWBBPwhv6LpDN7Lhg82w47zrMu5wegyAyt6wJhJeCNQ3L/s+vLUW9l7Sl9WV8BgAX3eHd9rommPnT7kJ3ZZBepb5MVyR9AgAzetAXBR4e7liQtE8cw/DF3+5N4YzaY8A8G0PGNLamWrn/795F9p8D1n3nfOa5bAcAHnpR0eoNGcFDf0VtiZaMVLhY1gOQMMacHCYdQbP2Aff7rduvIIjWQ5Ah0DYMNg6g5eegIlx1o3ncQA6BsG6gdYZvOwETChNADSqCXvetQ6AUrcEpMua8D7UrmINCNHrYHuSNWMVNorlMUCUzOgJURY02aQsfn4B3ClNaVAAaBUAW6PAy37sYuoVltpCSLyd3h2i3SiFU2/CS0vgRt75jefII0tAzK1SSWWDtnX1jZfN0MDVcPCyvqyuhMcAEENqVYYl/SG0vutmybofsRH+vOC6jDucHgVADJOGyEdhMKo9VHPSEIlLgsk7IFka9SVEHgcg3w9pjkQ0hx6NoEUdqFkZ7ubA+Qy17199EuKvlpDXDmo8DoBfJahfXf1Ik8Qw1JG0/JaN0z2b2vOfvgaZhQQ8kalXDXy94eptyMi2FiSPACAGD2wJrzZWHSFn6TAzGwavgSMPmqJVfVQdITOmTV3wcegrpN6C7YmwOB6OX3EfDEsBqOELY8NgeDvw83HNOIn4kavgUIri79MEvuqm3npxJL3sdf/CpN/dS5WWAdDMH358HRrVcs3xfK7Pd8G8v9VTTBh8Eq6Wh6t09jpErTUfOC0B4MUgle5kBujQsVTo+wvk2iC6NUw3+X3KqTR4bQVIB0mX3AYguCZsHAz+frqqVb7fcAaerQk731bFk1ladBw+3aEv7RYA3gZsi4KWAfqKpesbuhBybDC7F0S21B/DUeJeLnRdrNKqDrkFwHtt4ctuOursvCv/gXFbQdJkwkjXg6aMcOWWCn5Na8OULvYxp++FWQf07DENgEzXw8PMTX0xccJ2WJYAPYNV/HCVLmXCoNVwLh0C/CB+pF3y8GUVC3TINAARITC3j46qR3klcsvxV8FZlJgOabchNOjxsZPS1SbpYiY85QtL+0OYA9/VW9B2gZ5NpgGY0xsGtNBT5sjdd7kqfCT1Teps/0/r+ZB2ByaFQ0xH+98l0kuxJIWQlNU/Rzy+08zOgeA5ejaZBuCPaAjx11PmyD1gFey5+Hj6G78dfnrwMeuYUJjcGeKvqLPCa1mqQFoRASF1Htd9ORPaL9SzyTQAZ0Y5390VZ8qHv8HqU9C5AayKtHNKTSDpTEpdoTeaqaUi+wQ5c1j5JgQXUWztSoZBa0oIgOQx4OPGF8XfHYJpu0FS6fERjwZTKXPlTDC/QhSXGtdSzgcV8+n2Zzvhh2MlBEDcEHjORP7PN+9kGnRbqp5kmsseoiDN3A/f7FN6lkcUf9wmjZTwRWqZ6JDpJRDZAmb31lH1KK+85e7LQICQiL57KAQUcp6YnKHWfWUnVaLMGGmi6pJpAETR6A4wsZP5Enb9aRi5SZncqYEKbmaW1ZZzMGw95Op6L30Jdz+Tk75f+0CQPbwuScDbfBbyv9Ts3RgkvTr7lshRz5azMHoL3L6nq13xuw2AObVFS0lqnfYydJW7KsWQdJFiD8D8I3YAzdjyxAGQ74QcsvZrCl0aqnaaNFikJSYF0bZE1Qyxoj32xAJg5m2akakAwN0gaAb1J0mmYgZUXJgo71dmKi5Nqauy5ffanETkcn1xMi8llfersw9BKLeXpx0qk/J2ff5/0SNYsfy1VQMAAAAASUVORK5CYII=',
        imageUrl: '',
        // 遮罩层
        loading: true,
        // 导出遮罩层
        exportLoading: false,
        // 选中数组
        ids: [],
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 显示搜索条件
        showSearch: true,
        // 提示
        pvisible: false,
        showNode: false,
        // 总条数
        total: 0,
        // 表格数据
        datas: [],
        // 节点数据
        nodeDatas: [],
        // 弹出层标题
        title: '',
        // 是否显示弹出层
        open: false,
        opengroup: false,
        openMove: false,
        // 查询参数
        queryParams: {
          proName: null
        },
        // 表单参数
        form: {
          groupSort: null,
          groupName: null
        },
        formgroup: {},
        // 基础设置参数
        baseForm: {},
        // 移动到
        formMove: {},
        // 服务节点设置参数
        serveForm: {},
        // 表单校验
        ruleMoves: { proGroup: [{ required: true, message: '分组不能为空', trigger: 'blur' }] },
        rulegroups: {
          groupName: [{ required: true, message: '分组名称不能为空', trigger: 'blur' }]
        },
        rules: {
          groupName: [
            { required: true, message: '流程名称不能为空', trigger: 'blur' }
          ]
        },
        // 基础设置参数校验
        baseRules: {
          proName: [
            { required: true, message: '流程名称不能为空', trigger: 'blur' }
          ],
          proGroup: [
            { required: true, message: '流程分组不能为空', trigger: 'blur' }
          ],
          imageUrl: [
            { required: false, message: '图标不能为空', trigger: 'blur' }
          ]
        },
        // 服务节点参数校验
        serveRules: {
          javaBean: [
            { required: true, message: '表达式不能为空', trigger: 'blur' }
          ]
        },
        userId: [],
        roleId: [],
        flag: '1',
        selectFlag: 'base',
        activeName: 'first',
        radio: '成员',
        emptyType: 0,
        nodeType: '',
        actionType: '',
        markedAs: 0,//标记为
        linkIcon: [],
        currentNode: '0',//当前操作节点
        nodeActionType: ['0', '1'],
        javaBeans: [],//表达式字典数据
        nodeTypes: [],//节点类型字典数据
        nodeActionTypes: [],//节点操作类型字典数据
        activities: []
      }
    },
    created() {
      this.getList()
      this.initDictConfig()
    },
    filters: {
      nameFormat(dictTexts) {
        let text = ''
        dictTexts.map(item => text += item + ',')
        return text.substring(0, text.length - 1)
      },
      filterExams(data) {
        let text = ''
        data.fpDefExams.map(item => {
          if (item.roleNameDictText) {
            text += item.roleNameDictText + ','
          }
          if (item.userNameDictText) {
            text += item.userNameDictText + ','
          }
        })
        return text ? text.substring(0, text.length - 1) : data.nodeType == '0' ? '添加审批人' : data.nodeType == '1' ? '添加抄送人' : ''
      }
    },
    methods: {
      /**基本字典类型数据初始化*/
      initDictConfig() {
        allCallback('fp_java_bean').then(response => {
          this.javaBeans = response.data
        })
        this.getDicts('fp_node_type').then(response => {
          this.nodeTypes = response.data
        })
        this.getDicts('fp_node_action_type').then(response => {
          this.nodeActionTypes = response.data
        })
      },
      /** 查询流程定义列表 */
      getList() {
        this.loading = true
        allGroup(this.queryParams).then(response => {
          this.datas = response.data
          this.loading = false
        })
      },
      // 取消按钮
      cancel() {
        this.open = false
        this.reset()
      },
      // 表单重置
      reset() {
        this.form = {
          proId: null,
          status: '0',
          proName: null,
          proGroup: null,
          groupSort: 1
        }
        this.resetForm('form')
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      // 多选框选中数据
      handleSelectionChange(selection) {
        this.ids = selection.map(item => item.proId)
        this.single = selection.length !== 1
        this.multiple = !selection.length
      },
      /** 新增按钮操作 */
      handleAdd() {
        this.reset()
        this.open = true
        this.title = '新建分组'
      },
      /** 修改按钮操作 */
      handleUpdate(row) {
        this.reset()
        const proId = row.proId || this.ids
        getProce(proId).then(response => {
          this.form = response.data
          this.open = true
          this.title = '修改流程定义'
        })
      },
      /** 提交按钮 */
      submitForm() {
        this.$refs['form'].validate(valid => {
          if (valid) {
            if (this.form.proId != null) {
              updateGroup(this.form).then(response => {
                this.msgSuccess('修改成功')
                this.open = false
                this.getList()
              })
            } else {
              addGroup(this.form).then(response => {
                this.msgSuccess('新增成功')
                this.open = false
                this.getList()
              })
            }
          }
        })
      },
      handleAvatarSuccess(res, file) {
        this.imageUrl = URL.createObjectURL(file.raw)
      },
      beforeAvatarUpload(file) {
        const isPNG = file.type === 'image/png'
        const isLt2M = file.size / 1024 / 1024 < 2

        if (!isPNG) {
          this.$message.error('上传头像图片只能是 png 格式!')
        }
        if (!isLt2M) {
          this.$message.error('上传头像图片大小不能超过 2MB!')
        }
        return isJPG && isLt2M
      },
      handleClick(tab, event) {
        console.log(tab, event)
      },
      showNodeAction(nodeData, i) {
        this.nodeType = nodeData.nodeType
        this.currentNode = i
        this.actionType = 'edit'
        let userId = []
        let roleId = []
        nodeData.fpDefExams.length > 0 ? nodeData.fpDefExams.map(item => {
          userId = item.userId ? [...userId, Number(item.userId)] : []
          roleId = item.roleId ? [...roleId, Number(item.roleId)] : []
          this.emptyType = Number(item.emptyType)
        }) : this.emptyType = 0
        this.serveForm.javaBean = nodeData.fpDefCallBack ? nodeData.fpDefCallBack.javaBean : null
        this.userId = userId
        this.roleId = roleId
        this.radio = roleId.length > 0 ? '角色' : '成员'
        this.nodeActionType = [...nodeData.nodeAction.split(',')]
        this.markedAs = Number(nodeData.markedAs)
        this.activeName = 'first'
        this.showNode = true
      },
      actionNode(nodeType, currentNode) {
        this.nodeType = nodeType
        this.currentNode = currentNode
        this.actionType = 'add'
        this.value = []
        this.emptyType = 0
        this.nodeActionType = ['0', '1']
        this.markedAs = '0'
        this.serveForm.javaBean = null
        let nodeData = {
          nodeId: '',
          nodeSort: '2',
          nodeName: nodeType == '0' ? '审批人' : nodeType == '1' ? '抄送人' : '服务节点',
          nodeType: nodeType,
          signCode: '',
          callbackId: '',
          fpDefCallBack: null,
          nodeAction: '0,1',
          nodePermis: '0',
          markedAs: '0',
          fpDefExams: []
        }

        this.nodeDatas.splice(currentNode, 0, nodeData)
        let index = 0
        this.nodeDatas.map(item => {
          item.nodeSort = index + ''
          index++
        })

        this.activeName = 'first'
        this.showNode = true
      },
      removeNode(i){
        this.nodeDatas = this.nodeDatas.filter(item => item.nodeSort != i)
        let index = 0
        this.nodeDatas.map(item => {
          item.nodeSort = index + ''
          index++
        })
      },
      submitNodeForm() {
        this.showNode = false
      },
      mouseAction(flag, i) {
        this.linkIcon = []
        if (flag) this.linkIcon[i] = 'el-icon-circle-plus'
      },
      /*创建流程*/
      createFp() {
        this.flag = '2'
      },
      /*修改流程*/
      editFp(row) {
        this.flag = '2'
        this.baseForm = { ...row }
      },
      /*返回主页面*/
      callMainPage() {
        this.flag = '1'
        this.selectFlag = 'base'
        this.baseForm = {}
        this.showNode = false
        this.getList()
      },
      /*保存数据*/
      saveDatas() {
        if (this.selectFlag == 'base') {
          this.$refs['baseForm'].validate(valid => {
            if (valid) {
              if (this.baseForm.proId) {
                updateProce({ ...this.baseForm }).then(res => {
                  this.baseForm.status = '0'
                  this.msgSuccess('修改成功!')
                  this.getList()
                })
              } else if (!this.baseForm.proId) {
                addProce({ ...this.baseForm }).then(res => {
                  this.baseForm.status = '0'
                  this.baseForm.proId = res.msg
                  this.msgSuccess('创建成功!')
                  this.getList()
                })
              }
            }
          })
        }
        if (this.selectFlag == 'form') {
          this.baseForm.formJson = JSON.stringify(this.$refs.fg.getJson())
          updateProce({ ...this.baseForm }).then(response => {
            this.msgSuccess('操作成功!')
            this.getList()
          })
        }
        if (this.selectFlag == 'proc') {
          let thiz = this
          this.$confirm('是否确认保存' + thiz.baseForm.proName + '?,更改节点可能会导致已存在的流程无法使用!', '警告', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            insertNode({ nodeDatas: thiz.nodeDatas, proId: thiz.baseForm.proId }).then(res => {
              thiz.msgSuccess('操作成功!')
              thiz.listNodeData()
            })
          }).catch(() => {
          })
        }
      },
      showTag(flag) {
        if ((flag == 'show' && this.selectFlag == 'show') || flag == 'proc') {
          flag = 'proc'
          if (this.baseForm.proId) {
            this.showNode = false
            this.selectFlag = flag
            this.listNodeData()
          } else {
            this.msgError('请先保存基础参数设置!')
          }
        }
        if (flag == 'form') {
          if (this.baseForm.proId) {
            removeDrawingList()
            removeFormConf()
            let formJson = JSON.parse(this.baseForm.formJson)
            let drawingList = []
            formJson && formJson.fields.length > 0 ? formJson.fields.map(fileds => {
              drawingList = [...drawingList, { __config__: fileds.__config__ }]
            }) : ''
            saveDrawingList(drawingList)
            saveFormConf(formJson)
            this.selectFlag = flag
          } else {
            this.msgError('请先保存基础参数设置!')
          }
        }
        if (flag == 'base') {
          this.selectFlag = flag
        }

        if (this.selectFlag != 'show' && flag == 'show') {
          this.activities = []
          this.selectFlag = flag
          this.baseForm.proId ? queryPs({ proId: this.baseForm.proId }).then(res => {
            if (res.data) {
              this.activities = res.data
            }
          }) : ''
        }
      },
      listNodeData() {
        listNodeDatas(this.baseForm.proId).then(res => {
          this.nodeDatas = res.data
        })
      },
      /*添加成员*/
      addUser(row) {
        row.value = this.radio == '成员' ? this.userId : this.roleId
        row.flag = this.radio == '成员' ? '1' : '2'
        row.currentNode = this.currentNode
        row.actionType = this.actionType
        row.nodeType = this.nodeType
        row.emptyType = this.emptyType
        this.$refs.userForm.show(row)
      },
      setUserVal(val) {
        this.nodeDatas[val.currentNode].fpDefExams = val.fpDefExams
      },
      emptyTypeChange(val) {
        this.emptyType = val
        this.nodeDatas[this.currentNode].fpDefExams.map(item => item.emptyType = val)
      },
      nodeActionTypeChange(val) {
        let nodeAction = ''
        val.map(item => {
          nodeAction += item + ','
        })
        this.nodeDatas[this.currentNode].nodeAction = nodeAction.substring(0, nodeAction.length - 1)
      },
      markedAsChange(val) {
        this.nodeDatas[this.currentNode].markedAs = val
      },
      setFpCallBacks() {
        this.nodeDatas[this.currentNode].fpDefCallBack = {
          callbackName: this.javaBeans.filter(item => item.javaBean == this.serveForm.javaBean)[0].callbackName,
          javaBean: this.serveForm.javaBean,
          callbackStatus: '1'
        }
      },
      cancelgroup() {
        this.formgroup = {}
      },
      reName(item) {
        this.opengroup = true
        this.formgroup.id = item.id
      },
      subData() {
        this.$refs['formgroup'].validate(valid => {
          if (valid) {
            updateGroup({ ...this.formgroup }).then(res => {
              this.msgSuccess('操作成功!')
              this.opengroup = false
              this.getList()
            })
          }
        })
      },
      /** 删除流程*/
      delFp(row) {
        let thiz = this
        this.$confirm('是否确认删除' + row.proName + '?,删除可能会导致已存在的流程消失!', '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          delProce(row.proId).then(res => {
            thiz.msgSuccess('操作成功!')
            thiz.getList()
          })
        }).catch(() => {
        })
      },
      actionProcStatus(row) {
        let thiz = this
        let title = ''
        let param = {
          proId: row.proId
        }
        if (row.status == '1') {
          param.status = '0'
          title = '是否确认停用' + row.proName + '?,停用可能会导致已存在的流程无法再进行!'
        } else if (row.status == '0') {
          param.status = '1'
          title = '是否确认发布' + row.proName + '?'
        }

        this.$confirm(title, '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          setStatus(param).then(res => {
            if (thiz.flag == '2') thiz.baseForm.status = param.status
            thiz.msgSuccess('操作成功!')
            thiz.getList()
          })
        }).catch(() => {
        })
      },
      /*删除分组*/
      delGroupData(row) {
        let thiz = this
        this.$confirm('是否确认删除分组' + row.groupName + '?,删除分组也会删除分组中的流程,可能导致已存在的流程无法再进行!', '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          delGroup(row.id).then(res => {
            thiz.msgSuccess('操作成功!')
            thiz.getList()
          })
        }).catch(() => {
        })
      },
      /*移动到*/
      subMove() {
        let thiz = this
        this.$refs['formMove'].validate(valid => {
          if (valid) {
            this.$confirm('是否确认将' + this.formMove.item.proName + '移动到所选择的分组!', '警告', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(() => {
              updateProce({ proId: this.formMove.item.proId, proGroup: this.formMove.proGroup }).then(res => {
                thiz.msgSuccess('操作成功!')
                thiz.getList()
                thiz.cancelMove()
              })
            }).catch(() => {
            })
          }
        })
      },
      groupMove(item) {
        this.formMove.item = item
        this.openMove = true
      },
      cancelMove() {
        this.formMove = {}
        this.openMove = false
      },
      /*分组排序*/
      orderGroup(flag, row, index) {
        if (this.datas && this.datas.length > 1) {
          let params = []
          let data = null
          /*向上移动*/
          if (flag && index > 0) {
            data = this.datas[index - 1]
          }
          /*向下移动*/
          if (!flag && index < this.datas.length - 1) {
            data = this.datas[index + 1]
          }
          if (data) {
            params = [{
              id: data.id,
              groupSort: row.groupSort
            }, {
              id: row.id,
              groupSort: data.groupSort
            }]
            sortGroup({ gpvs: params }).then(res => {
              this.getList()
            })
          }
        }
      },
      /*设置流程权限*/
      addFpPermis(item) {
        this.$refs.permisForm.show(item)
      }
    }
  }
</script>

<style>

  .text {
    font-size: 13px;
  }

  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }

  .clearfix:after {
    clear: both
  }

  body {
    background-color: #f8fafc
  }

  .el-card__body {
    padding: 0px 20px 0px 20px;
  }

  .avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }

  .avatar-uploader .el-upload:hover {
    border-color: #409EFF;
  }

  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 64px;
    height: 64px;
    line-height: 64px;
    text-align: center;
  }

  .avatar {
    width: 64px;
    height: 64px;
    display: block;
  }

  .base-link {
    width: 64px;
    height: 1px;
    background: #1890FF;
    bottom: 0;
    transition: left ease-in-out 0.5s;
  }

  .el-alert--warning.is-light {
    background-color: #ffffff;
  }

  .el-alert__title {
    color: rgba(0, 0, 0, 0.65);
  }

  .add-node {
    border: 1px dashed #E3E3E3;
  }

  .add-node:hover {
    border: 1px dashed #1890FF;
  }

  .add-node .el-icon-plus {
    font-size: 30px;
    color: #E3E3E3;
  }

  .add-node .el-icon-plus:hover {
    font-size: 30px;
    color: #1890FF;
  }

  .item-card .el-card__header {
    padding: 14px 15px 7px;
    min-height: 40px;
    background-color: #ccb1ea;
  }

  .item-card-0 .el-card__header {
    background-color: #13ce66;
  }

  .item-card-1 .el-card__header {
    background-color: #e6a23c;
  }


  .item-card .el-card__body {
    padding: 8px;
  }

  .my-card {
    margin: 5px;
    width: 125px;
    height: 25px;
    text-align: center;
    line-height: 25px;
  }

  .radio-row {
    margin-bottom: 20px;
  }

  .row-p {
    font-size: 10px;
    color: rgba(51, 42, 42, 0.45);
    margin-left: 8px;
    display: inline;
  }

  .empty-p {
    font-size: 14px;
    color: rgba(0, 0, 0, 0.85);
    margin-top: 0;
    margin-bottom: 1em;
  }

  .serve-form .el-select {
    width: 410px;
    text-align: center;
  }

  .serve-form .el-textarea {
    width: 410px;
    text-align: center;
  }

</style>

  1. start —> index.vue
<template>
  <div class="app-container" :style="flag == '1'?'':'background-color: #f0f2f5;'">
    <!--我的待办-->
    <div v-if="flag == '1'">
      <p style="font-size: 18px; font-weight: bold;">待我处理</p>
      <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
        <el-form-item label="所属流程" prop="proId">
          <el-input
            v-model="queryParams.proId"
            placeholder="请输入所属流程"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="流程发起人" prop="proOriginator" label-width="89px">
          <el-input
            v-model="queryParams.proOriginator"
            placeholder="请输入流程发起人"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="发起时间" prop="subTime">
          <el-input
            v-model="queryParams.subTime"
            placeholder="请输入发起时间"
            clearable
            size="small"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>

      <el-table v-loading="loading" :data="startList" border>
        <el-table-column label="发起人" align="center" prop="proOriginatorDictText"/>
        <el-table-column label="流程" align="center" prop="proName">
          <template slot-scope="scope">
            <el-link :underline="false" type="primary" @click="openFpDesc(scope.row)">{{scope.row.proName}}</el-link>
          </template>
        </el-table-column>
        <el-table-column label="发起时间" align="center" prop="subTime"/>
        <el-table-column label="所属业务类型" align="center" prop="busType">
          <template slot-scope="scope">
            <el-tag>一般业务</el-tag>
          </template>
        </el-table-column>
      </el-table>

      <pagination
        v-show="total>0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </div>

    <!--审批详情-->
    <div v-if="flag == '2'">
      <el-row>
        <el-col :span="24">
          <el-card shadow="never" style="height: 62px;padding-top: 8px;">
            <el-row>
              <el-col :span="9">
                <el-button style="margin-top: -16px;" icon="el-icon-arrow-left" @click="callMainPage">返回</el-button>
              </el-col>
              <el-col :span="10">
                <p style="display: inline;">{{actionRow.groupName}}</p>
              </el-col>
              <el-col :span="5" style="margin-top: -10px;">
                <div style="float: right;margin-right: -4%;">
                  <el-button style="margin-right: -2%" type="warning"
                             v-if="actionRow.nodeActionType.indexOf('3') != -1" @click="openForm('3')">异议
                  </el-button>
                  <el-button style="margin-right: -2%" type="success"
                             v-if="actionRow.nodeActionType.indexOf('2') != -1">转交
                  </el-button>
                  <el-button style="margin-right: -2%" type="primary" v-if="actionRow.nodeActionType.indexOf('0') != -1"
                             @click="openForm('1')">同意
                  </el-button>
                  <el-button style="margin-right: -2%" v-if="actionRow.nodeActionType.indexOf('1') != -1" type="danger"
                             @click="openForm('2')">驳回
                  </el-button>
                </div>
              </el-col>
            </el-row>
          </el-card>
          <el-card shadow="never">
            <el-row type="flex">
              <el-col :span="20" style="margin-right: 40px;">
                <parser :form-conf="formConf"></parser>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">状态</p>
                <el-tag color="#f8eaec" v-if="actionRow.proGoStatus == '5'">已转交</el-tag>
                <el-tag type="info" v-if="actionRow.proGoStatus == '4'">已撤回</el-tag>
                <el-tag type="danger" v-if="actionRow.proGoStatus == '3'">已驳回</el-tag>
                <el-tag type="success" v-if="actionRow.proGoStatus == '2'">已完成</el-tag>
                <el-tag type="warning" v-if="actionRow.proGoStatus == '1'">审批中</el-tag>
                <el-tag v-if="actionRow.proGoStatus == '0'">未审批</el-tag>
              </el-col>
              <el-col :span="2">
                <p style="font-size: 13px;color: #909399;">提交时间</p>
                <p style="font-size: 13px;">{{actionRow.subTime}}</p>
              </el-col>
            </el-row>
          </el-card>
          <el-card class="fp-card">
            <div slot="header" class="clearfix">
              <span>流程进度</span>
            </div>
            <div class="text item">
              <fp-time-line ref="ftl" :activities="activities"></fp-time-line>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>

    <el-dialog :title="title" :visible.sync="open" width="720px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="10px" class="action-form">
        <el-form-item label="" prop="remark">
          <el-input type="textarea" :rows="8" v-model="form.remark" placeholder="请输入审批意见!"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="subData">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>

  import Parser from 'form-gen-parser'
  import { listStart, executorFp } from '@/api/fp/start'
  import { listPs } from '@/api/fp/node'
  import FpTimeLine from '../model/tl/index'

  export default {
    name: 'Start',
    components: { Parser, FpTimeLine },
    data() {
      return {
        // 遮罩层
        loading: true,
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 是否打开弹窗
        open: false,
        // 显示搜索条件
        showSearch: true,
        // 弹窗标题
        title: '审批意见',
        // 总条数
        total: 0,
        // 流程启动信息表格数据
        startList: [],
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          proId: null,
          proOriginator: null,
          subTime: null
        },
        flag: '1',
        actionRow: {},
        formConf: {},
        activities: [],
        form: { remark: '' },
        setForm: { zf: 0 },
        rules: {},
        formData: {},
        selectRow: [],
        taskId: ''
      }
    },
    created() {
      this.getList()
    },
    methods: {
      /** 查询流程启动信息列表 */
      getList() {
        this.loading = true
        listStart(this.queryParams).then(response => {
          this.startList = response.rows
          this.total = response.total
          this.loading = false
        })
      },
      /** 搜索按钮操作 */
      handleQuery() {
        this.queryParams.pageNum = 1
        this.getList()
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.resetForm('queryForm')
        this.handleQuery()
      },
      callMainPage() {
        this.flag = '1'
        this.actionRow = {}
        this.activities = []
      },
      /** 打开审批详情*/
      openFpDesc(row) {
        this.actionRow = row
        this.formData = JSON.parse(row.formData)
        //回显数据
        this.formConf = JSON.parse(row.formConf)
        this.callShowData()
        this.flag = '2'
        this.queryProcessSchedule()

      },
      queryProcessSchedule() {
        listPs({ startId: this.actionRow.id }).then(res => {
          if (res.data) {
            this.activities = res.data
          }
        })
      },
      callShowData() {
        this.formConf.fields.map(item => {
          var __config__ = item.__config__
          __config__.defaultValue = this.formData[item.__vModel__] ? this.formData[item.__vModel__] : __config__.defaultValue
          return item
        })
      },
      subData() {
        executorFp({
          nodeId: this.actionRow.todoNodeId,
          startId: this.actionRow.id,
          nodeStatus: this.form.type,
          remark: this.form.remark
        }).then(res => {
          this.open = false
          this.msgSuccess('操作成功!')
          this.getList()
          this.callMainPage()
        })
      },
      openForm(flag) {
        this.open = true
        this.form = {}
        this.form.type = flag
      },
      cancel() {
        this.open = false
        this.form.type = undefined
      }
    }
  }
</script>

<style scoped lang="scss">
  .text {
    font-size: 13px;
  }

  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }

  .clearfix:after {
    clear: both
  }

  .fp-card {
    margin-top: 10px;
  }

  .action-form {
    .el-input {
      width: 660px
    }

    .el-textarea {
      width: 660px
    }

    .el-select {
      width: 660px;
    }
  }

</style>

添加菜单



INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2163, '流程设计', 2161, 1, 'proce', 'fp/proce/index', 1, 0, 'C', '0', '0', 'fp:proce:list', 'button', 'admin', TIMESTAMP '2021-09-28 18:00:50', 'admin', TIMESTAMP '2021-09-28 18:01:17', null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2191, '我的申请', 2161, 3, 'apply', 'fp/apply/index', 1, 1, 'C', '0', '0', 'fp:start:list', 'select', 'admin', TIMESTAMP '2021-10-12 16:59:59', 'admin', TIMESTAMP '2021-10-12 17:00:39', null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2196, '查询节点', 2190, 1, null, null, 1, 0, 'F', '0', '0', 'fp:busNode:query', '#', 'tj', TIMESTAMP '2021-10-19 16:08:51', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2197, '处理流程', 2190, 2, null, null, 1, 0, 'F', '0', '0', 'fp:start:executorFp', '#', 'tj', TIMESTAMP '2021-10-19 16:16:42', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2198, '查询抄送我的', 2195, 1, null, null, 1, 0, 'F', '0', '0', 'fp:start:ccme', '#', 'tj', TIMESTAMP '2021-10-19 16:28:32', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2199, '查询单个', 2194, 1, null, null, 1, 0, 'F', '0', '0', 'fp:mount:query', '#', 'tj', TIMESTAMP '2021-10-19 16:29:30', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2200, '新增', 2194, 2, null, null, 1, 0, 'F', '0', '0', 'fp:mount:add', '#', 'tj', TIMESTAMP '2021-10-19 16:29:51', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2201, '修改', 2194, 3, null, null, 1, 0, 'F', '0', '0', 'fp:mount:edit', '#', 'tj', TIMESTAMP '2021-10-19 16:30:08', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2202, '删除', 2194, 4, null, null, 1, 0, 'F', '0', '0', 'fp:mount:remove', '#', 'tj', TIMESTAMP '2021-10-19 16:30:28', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2204, '撤回', 2191, 2, null, null, 1, 0, 'F', '0', '0', 'fp:start:initiateFp', '#', 'admin', TIMESTAMP '2021-10-19 16:34:04', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2323, '开启流程', 2009, 26, null, null, 1, 0, 'F', '0', '0', 'fp:start:startFp', '#', 'tangjing', TIMESTAMP '2021-10-25 16:25:36', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2331, '查询流程', 2194, 5, null, null, 1, 0, 'F', '0', '0', 'fp:proce:all', '#', 'tangjing', TIMESTAMP '2021-10-25 16:40:51', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2402, '提交', 2191, 3, null, null, 1, 0, 'F', '0', '0', 'fp:start:rejectedFp', '#', 'tangjing', TIMESTAMP '2021-11-19 14:02:08', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2194, '流程挂载', 2161, 4, 'mount', 'fp/mount/index', 1, 1, 'C', '0', '0', 'fp:mount:list', 'swagger', 'admin', TIMESTAMP '2021-10-15 13:36:41', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2195, '抄送我的', 2161, 5, 'ccme', 'fp/ccme/index', 1, 1, 'C', '0', '0', 'fp:start:query', 'form', 'admin', TIMESTAMP '2021-10-18 13:10:51', 'admin', TIMESTAMP '2021-10-18 13:12:33', null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2203, '查询流程节点数据', 2191, 1, null, null, 1, 0, 'F', '0', '0', 'fp:busNode:query', '#', 'admin', TIMESTAMP '2021-10-19 16:33:09', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2205, '查询流程回调处理列表', 2163, 1, null, null, 1, 0, 'F', '0', '0', 'fp:callback:all', '#', 'admin', TIMESTAMP '2021-10-19 16:42:50', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2206, '查询流程分组列表', 2163, 2, null, null, 1, 0, 'F', '0', '0', 'fp:group:all', '#', 'admin', TIMESTAMP '2021-10-19 16:43:11', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2207, '新增流程分组', 2163, 3, null, null, 1, 0, 'F', '0', '0', 'fp:group:add', '#', 'admin', TIMESTAMP '2021-10-19 16:43:32', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2208, '修改流程分组', 2163, 4, null, null, 1, 0, 'F', '0', '0', 'fp:group:edit', '#', 'admin', TIMESTAMP '2021-10-19 16:43:50', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2209, '删除流程分组', 2163, 5, null, null, 1, 0, 'F', '0', '0', 'fp:group:remove', '#', 'admin', TIMESTAMP '2021-10-19 16:44:12', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2210, '分组排序', 2163, 6, null, null, 1, 0, 'F', '0', '0', 'fp:group:sort', '#', 'admin', TIMESTAMP '2021-10-19 16:44:32', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2211, '查询流程定义信息', 2163, 7, null, null, 1, 0, 'F', '0', '0', 'fp:proce:query', '#', 'admin', TIMESTAMP '2021-10-19 16:44:52', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2212, '新增流程定义', 2163, 8, null, null, 1, 0, 'F', '0', '0', 'fp:proce:add', '#', 'admin', TIMESTAMP '2021-10-19 16:45:11', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2213, '修改流程定义', 2163, 9, null, null, 1, 0, 'F', '0', '0', 'fp:proce:edit', '#', 'admin', TIMESTAMP '2021-10-19 16:45:29', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2214, '删除流程定义', 2163, 10, null, null, 1, 0, 'F', '0', '0', 'fp:proce:remove', '#', 'admin', TIMESTAMP '2021-10-19 16:45:49', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2215, '停用或启用流程', 2163, 11, null, null, 1, 0, 'F', '0', '0', 'fp:proce:setStatus', '#', 'admin', TIMESTAMP '2021-10-19 16:46:10', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2216, '操作流程节点', 2163, 12, null, null, 1, 0, 'F', '0', '0', 'fp:node:insert', '#', 'admin', TIMESTAMP '2021-10-19 16:47:09', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2217, '查询流程节点信息', 2163, 13, null, null, 1, 0, 'F', '0', '0', 'fp:node:query', '#', 'admin', TIMESTAMP '2021-10-19 16:47:34', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2228, '由我参与', 2161, 6, 'bmpt', 'fp/bmpt/index', 1, 1, 'C', '0', '0', 'fp:start:bmpt', 'component', 'admin', TIMESTAMP '2021-10-23 14:19:35', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2161, '流程管理', 0, 4, 'fp', null, 1, 0, 'M', '0', '0', null, 'cascader', 'admin', TIMESTAMP '2021-09-28 17:38:49', 'admin', TIMESTAMP '2021-10-15 09:54:45', null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2190, '我的待办', 2161, 2, 'start', 'fp/start/index', 1, 1, 'C', '0', '0', 'fp:start:query', 'message', 'admin', TIMESTAMP '2021-10-12 16:08:48', null, null, null);
INSERT INTO SYS_MENU (MENU_ID, MENU_NAME, PARENT_ID, ORDER_NUM, PATH, COMPONENT, IS_FRAME, IS_CACHE, MENU_TYPE, VISIBLE, STATUS, PERMS, ICON, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2284, '查询由我参与的', 2228, 1, null, null, 1, 0, 'F', '0', '0', 'fp:start:bmpt', '#', 'admin', TIMESTAMP '2021-10-25 15:41:26', null, null, null);

新增字典数据

  1. 字典主表
INSERT INTO SYS_DICT_TYPE (DICT_ID, DICT_NAME, DICT_TYPE, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (262, '流程节点权限操作类型', 'fp_node_action_type', '0', 'admin', TIMESTAMP '2021-10-09 11:37:47', null, null, '流程节点权限操作类型列表');
INSERT INTO SYS_DICT_TYPE (DICT_ID, DICT_NAME, DICT_TYPE, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (260, '流程节点类型', 'fp_node_type', '0', 'admin', TIMESTAMP '2021-10-09 11:05:23', null, null, '流程节点类型列表');
INSERT INTO SYS_DICT_TYPE (DICT_ID, DICT_NAME, DICT_TYPE, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (280, '流程挂载状态', 'fp_mount_status', '0', 'admin', TIMESTAMP '2021-10-15 13:43:18', null, null, '流程挂载状态列表');
  1. 字典副表

INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2719, 1, '审批人', '0', 'fp_node_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '审批人');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2720, 2, '抄送人', '1', 'fp_node_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '抄送人');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2721, 3, '服务节点', '2', 'fp_node_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '服务节点');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2722, 1, '同意', '0', 'fp_node_action_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '同意');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2723, 2, '拒绝', '1', 'fp_node_action_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '拒绝');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2724, 3, '转交(暂不支持)', '2', 'fp_node_action_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '转交');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2725, 4, '异议', '3', 'fp_node_action_type', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '异议');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2726, 1, '关闭', '0', 'fp_mount_status', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '关闭');
INSERT INTO SYS_DICT_DATA (DICT_CODE, DICT_SORT, DICT_LABEL, DICT_VALUE, DICT_TYPE, CSS_CLASS, LIST_CLASS, IS_DEFAULT, STATUS, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, REMARK) VALUES (2727, 2, '开启', '1', 'fp_mount_status', null, null, 'N', '0', 'admin', TIMESTAMP '2021-06-26 16:24:36', null, null, '开启');


  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你的 ruoyi 项目是前后台不分离的,你可以按照以下步骤来添加前端页面: 1. 在 ruoyi-admin 模块下的 resources/static 目录下创建一个新的文件夹,用于存放你的前端页面和相关资源文件。 2. 在创建好的文件夹中编写你的前端页面代码,该页面可以是一个 HTML 文件或者是 Vue/React 等前端框架的代码。 3. 如果你使用了 Vue/React 等前端框架,可以通过修改 webpack.config.js 等配置文件,将编译后的前端代码打包到 resources/static 目录下。 4. 在 ruoyi-admin 模块下的 controller 包中创建一个新的 Controller 类,用于处理前端页面的请求。 5. 在 Controller 类中添加一个方法,该方法用于返回前端页面的视图。可以使用 @GetMapping 注解来映射该方法的请求路径。 6. 在方法中使用 ModelAndView 对象来指定前端页面的视图名称和模型数据。 7. 在 ruoyi-admin 模块下的 resources/templates 目录下创建一个与前端页面名称相同的 Thymeleaf 模板文件,该文件用于渲染前端页面。 8. 在 Thymeleaf 模板文件中使用 Thymeleaf 模板语法来引入前端页面的资源文件,例如 CSS 文件、JavaScript 文件等。 完成以上步骤后,你就可以在 ruoyi添加自己的前端页面了。需要注意的是,在前后台不分离的情况下,前端页面和后端代码是在同一个项目中,因此在开发时需要注意代码的组织和管理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值