Files
art-design/src/views/admin/approval/approval_edit.vue
T
浅念 c7cdec27e0 Squashed commit of the following:
commit a514bf4009
Author: 浅念 <email@sample.com>
Date:   Thu Sep 18 09:55:10 2025 +0800

    Squashed commit of the following:

    commit 9e260ddcd8
    Author: 浅念 <email@sample.com>
    Date:   Thu Sep 18 09:49:33 2025 +0800

        修改 kpi

    commit 6feb3ace75
    Author: 浅念 <email@sample.com>
    Date:   Thu Sep 18 09:42:44 2025 +0800

        kpi 统计

    commit ec257a246d
    Author: 浅念 <email@sample.com>
    Date:   Wed Sep 17 17:04:35 2025 +0800

        修改 弹框表格

    commit fa764852ab
    Author: 浅念 <email@sample.com>
    Date:   Wed Sep 17 15:54:35 2025 +0800

        添加 账户简称

    commit 65f7f4a123
    Author: 浅念 <email@sample.com>
    Date:   Wed Sep 17 15:10:28 2025 +0800

        品牌加热门  修改 仪表盘接口

    commit 922cfec81d
    Author: 浅念 <email@sample.com>
    Date:   Tue Sep 16 15:57:17 2025 +0800

        调整备货单  crm 端页面

    commit 5c8df36f12
    Author: 浅念 <email@sample.com>
    Date:   Mon Sep 15 17:14:38 2025 +0800

        调整备货单 及各页面名称

    commit 5503a14408
    Author: 浅念 <email@sample.com>
    Date:   Fri Sep 12 18:10:03 2025 +0800

        账户添加公司

    commit 3439b45d9a
    Author: 浅念 <email@sample.com>
    Date:   Fri Sep 12 17:53:26 2025 +0800

        修改终版备货单

    commit 045d13eb7b
    Author: 浅念 <email@sample.com>
    Date:   Fri Sep 12 17:49:07 2025 +0800

        修改备货单 等页面

    commit da690dfa06
    Author: 浅念 <email@sample.com>
    Date:   Thu Sep 11 17:44:37 2025 +0800

        修改 备货单 详情

    commit 853e9591b3
    Author: 浅念 <email@sample.com>
    Date:   Wed Sep 10 18:11:39 2025 +0800

        修改 备货单页面

    commit 9052bd1027
    Author: 浅念 <email@sample.com>
    Date:   Tue Sep 9 20:53:30 2025 +0800

        备货单  审核功能

    commit 079d9babb8
    Merge: ebd7479 91d810b
    Author: 浅念 <email@sample.com>
    Date:   Tue Sep 9 14:51:43 2025 +0800

        Merge branch 'feature/crm_pc' of http://219.157.255.213:25311/xinyouhe/art-design into feature/crm_vehicle

    commit ebd7479573
    Merge: 64e53b6 459d306
    Author: 浅念 <email@sample.com>
    Date:   Tue Sep 9 13:57:28 2025 +0800

        Merge branch 'feature/crm_pc' of http://219.157.255.213:25311/xinyouhe/art-design into feature/crm_vehicle

    commit 64e53b6ee9
    Author: 浅念 <email@sample.com>
    Date:   Mon Sep 8 19:09:55 2025 +0800

        备货单 加 审批流程

    commit 9bd7c89cb4
    Author: 浅念 <email@sample.com>
    Date:   Thu Sep 4 15:09:17 2025 +0800

        车辆信息,备货单  等页面

commit 91d810b33a
Author: 浅念 <email@sample.com>
Date:   Tue Sep 9 14:51:23 2025 +0800

    添加公司搜索

commit 459d306504
Author: 浅念 <email@sample.com>
Date:   Tue Sep 9 13:56:43 2025 +0800

    用户添加 公司搜索

commit 8a0d4fc516
Author: 浅念 <email@sample.com>
Date:   Thu Sep 4 14:38:09 2025 +0800

    自定义跟进时间  处理

commit 58f7d56f40
Author: 浅念 <email@sample.com>
Date:   Wed Sep 3 15:13:10 2025 +0800

    工单去掉 移动端不显示限制

commit a91e1d7820
Author: 浅念 <email@sample.com>
Date:   Tue Sep 2 14:13:15 2025 +0800

    将编号 后移

commit a9f8c5c4dc
Author: 浅念 <email@sample.com>
Date:   Wed Aug 27 09:20:56 2025 +0800

    客户重复通知

commit aefd43f79d
Author: 浅念 <email@sample.com>
Date:   Tue Aug 26 09:55:42 2025 +0800

    修改 通知详情样式  和 即时通讯

commit 1bd9d163ec
Author: 浅念 <email@sample.com>
Date:   Mon Aug 25 15:31:14 2025 +0800

    通知 列表富文本内容显示

commit be6093e57d
Author: 浅念 <email@sample.com>
Date:   Mon Aug 25 13:34:13 2025 +0800

    crm 客户添加自定义列  跟进添加图片粘贴上传

commit d83246ba8c
Author: 浅念 <email@sample.com>
Date:   Fri Aug 22 18:33:58 2025 +0800

    显示编号

commit 329acaf68d
Author: 浅念 <email@sample.com>
Date:   Fri Aug 22 18:27:26 2025 +0800

    通知板块 及 功能调整

commit b319944007
Author: 浅念 <email@sample.com>
Date:   Thu Aug 21 11:12:12 2025 +0800

    调整联系方式字段为电话  列表中多个联系方式纵向排列显示   会增加公司信息的自定义输入框  排序搜索 等功能

commit 70d26f0829
Author: 浅念 <email@sample.com>
Date:   Tue Aug 19 16:18:15 2025 +0800

    1.0.3  的相关功能

commit 0e84bedb67
Author: 浅念 <email@sample.com>
Date:   Fri Aug 15 16:31:55 2025 +0800

    v1.0.2 更新内容

commit f5deb7b8f7
Author: 浅念 <email@sample.com>
Date:   Wed Aug 13 19:27:49 2025 +0800

    添加菜单 权限 等页面

commit e797cb24de
Author: 浅念 <email@sample.com>
Date:   Mon Aug 11 19:24:24 2025 +0800

    添加工单附件  及样式

commit 619311dfbe
Author: chenlan <suya123456>
Date:   Fri Aug 8 22:13:25 2025 +0800

    添加crm  归属客户

commit b402e522bf
Author: 浅念 <email@sample.com>
Date:   Thu Aug 7 10:51:17 2025 +0800

    询单权限校验

commit f950f665a9
Author: 浅念 <email@sample.com>
Date:   Thu Aug 7 10:10:45 2025 +0800

    添加 询单图片预览

commit 8deecc8197
Author: 浅念 <email@sample.com>
Date:   Wed Aug 6 18:36:42 2025 +0800

    修改用户页面 询单

commit 3e24d1905f
Author: 浅念 <email@sample.com>
Date:   Tue Aug 5 18:25:32 2025 +0800

    添加用户页面 客户页面接口 等

commit 7542d80e38
Author: 浅念 <email@sample.com>
Date:   Mon Aug 4 18:05:07 2025 +0800

    修改 仪表盘页面样式

commit e5548f95e4
Author: 浅念 <email@sample.com>
Date:   Mon Aug 4 14:50:55 2025 +0800

    修改询单字段  仪表盘页面  及添加管理端客户页面

commit d30ea5b7bf
Author: 浅念 <email@sample.com>
Date:   Mon Aug 4 14:48:58 2025 +0800

    添加echarts 插件

commit 0e4160c5ca
Author: 浅念 <email@sample.com>
Date:   Fri Aug 1 18:27:22 2025 +0800

    仪表盘接口

commit bd23288acd
Author: 浅念 <email@sample.com>
Date:   Fri Aug 1 18:26:46 2025 +0800

    修改 仪表盘

commit 6bfb940881
Author: 浅念 <email@sample.com>
Date:   Fri Aug 1 10:01:35 2025 +0800

    客户列表 添加公海详情展示

commit b50f650565
Author: 浅念 <email@sample.com>
Date:   Thu Jul 31 17:26:36 2025 +0800

    修改 客户标签样式  个人信息添加邮箱 修复关闭全部页面404

commit ea1dbb7af4
Author: 浅念 <email@sample.com>
Date:   Wed Jul 30 18:50:31 2025 +0800

    修改公海和询单页面

commit 1bfbb95bd9
Author: 浅念 <email@sample.com>
Date:   Wed Jul 30 17:23:09 2025 +0800

    修改样式和添加用户数据显示

commit a9f5f76b35
Author: 浅念 <email@sample.com>
Date:   Wed Jul 30 15:06:03 2025 +0800

    设置默认页面

commit 3ea9d3d5df
Author: 浅念 <email@sample.com>
Date:   Wed Jul 30 11:46:31 2025 +0800

    修改飞书登录 公司id 系统id 获取不到问题  客户及公海添加用户信息显示

commit 0cef82279f
Author: 浅念 <email@sample.com>
Date:   Tue Jul 29 16:42:36 2025 +0800

    修改工单 客户等页面

commit 695ebbcc71
Author: 浅念 <email@sample.com>
Date:   Fri Jul 25 17:50:04 2025 +0800

    修改默认值

commit ffc194a1c1
Author: 浅念 <email@sample.com>
Date:   Fri Jul 25 17:05:33 2025 +0800

    飞书授权添加系统码  及登录进入页面路由

commit 48b95e252f
Author: 浅念 <email@sample.com>
Date:   Fri Jul 25 15:48:49 2025 +0800

    回收站添加占位

commit 382011d592
Author: 浅念 <email@sample.com>
Date:   Fri Jul 25 15:11:50 2025 +0800

    修改登录页样式公海数据 个人信息默认图

commit 0f790269db
Author: 浅念 <email@sample.com>
Date:   Fri Jul 25 09:16:55 2025 +0800

    修改字段名称

commit f18cfdb69e
Author: 浅念 <email@sample.com>
Date:   Thu Jul 24 17:28:51 2025 +0800

    调整代码格式

commit 858147fbbd
Author: 浅念 <email@sample.com>
Date:   Thu Jul 24 10:17:22 2025 +0800

    修改token刷新 及修改询单详情展示

commit c333b907e6
Author: 浅念 <email@sample.com>
Date:   Wed Jul 23 18:22:56 2025 +0800

    对接询单接口

commit 7979a31e36
Author: 浅念 <email@sample.com>
Date:   Wed Jul 23 14:40:02 2025 +0800

    修改代码格式

commit 4a39387865
Author: 浅念 <email@sample.com>
Date:   Wed Jul 23 14:07:13 2025 +0800

    设置客户列表权限

commit fc4de02582
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 17:08:13 2025 +0800

    feat(router): 调整菜单

commit 2ffe0c9753
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 17:04:16 2025 +0800

    feat(crmApi.ts): 调整结构

commit c3fb2c48d3
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 17:00:25 2025 +0800

    feat(router): 路由调整

commit c5b0a8265a
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 16:54:06 2025 +0800

    chore: 添加询单页面及调整菜单

commit ca06c9bc46
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 16:35:43 2025 +0800

    询单页面及菜单

commit 3b6de4c7fa
Merge: d3bb4ec 85e8b9b
Author: 浅念 <email@sample.com>
Date:   Tue Jul 22 09:22:20 2025 +0800

    Merge branch 'master' into feature/crm_pc

commit d3bb4ec6e5
Merge: c7d8908 d7ab6f1
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 18:22:55 2025 +0800

    Merge branch 'master' into feature/crm_pc

    # Conflicts:
    #	.gitignore
    #	src/api/crmApi.ts
    #	src/api/usersApi.ts
    #	src/store/modules/user.ts
    #	src/utils/http/index.ts
    #	src/views/auth/login/index.vue
    #	src/views/crmpc/customer/Show.vue
    #	src/views/crmpc/customer/index.vue

commit c7d890809c
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 18:12:16 2025 +0800

    添加系统码

commit 31cd18fade
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 18:02:48 2025 +0800

    Merge branch 'master' into feature/crm_pc

    # Conflicts:
    #	.gitignore

commit 59c9c97c08
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 17:56:52 2025 +0800

    Merge branch 'master' into feature/crm_pc

    # Conflicts:
    #	.gitignore

commit d4b577c42e
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 13:03:56 2025 +0800

    feat(crm): 添加相关图片

commit e4967760b7
Merge: 0c67666 85102fc
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 11:25:49 2025 +0800

    Finish crm_pc

commit 85102fcf32
Author: 浅念 <email@sample.com>
Date:   Mon Jul 21 11:24:06 2025 +0800

    crm 接口对接

commit 0c67666793
Author: 浅念 <email@sample.com>
Date:   Wed Jul 16 17:18:14 2025 +0800

    添加上传头部信息

commit a41c75f703
Author: 浅念 <email@sample.com>
Date:   Tue Jul 15 09:40:53 2025 +0800

    修改页面

commit 22ae0e86c8
Author: 浅念 <email@sample.com>
Date:   Mon Jul 14 17:59:02 2025 +0800

    修改页面

commit 812e3c4b0c
Author: 浅念 <email@sample.com>
Date:   Mon Jul 14 10:09:10 2025 +0800

    修改菜单问题

commit 2ad479b006
Author: 浅念 <email@sample.com>
Date:   Fri Jul 11 19:45:57 2025 +0800

    修改样式

commit 5802e157bf
Author: 浅念 <email@sample.com>
Date:   Fri Jul 11 19:43:34 2025 +0800

    修改工单

commit 713d43f7b4
Author: 浅念 <email@sample.com>
Date:   Fri Jul 11 18:37:16 2025 +0800

    添加登录

commit 719911918f
Author: 浅念 <email@sample.com>
Date:   Fri Jul 4 17:16:38 2025 +0800

    项目创建

commit ce47b39fa5
Author: 浅念 <email@sample.com>
Date:   Wed Jul 2 09:37:51 2025 +0800

    删除

# Conflicts:
#	src/App.vue
#	src/api/adminApi.ts
#	src/api/crmApi.ts
#	src/api/menuApi.ts
#	src/api/systemApi.ts
#	src/api/usersApi.ts
#	src/components/core/layouts/art-header-bar/index.vue
#	src/components/core/layouts/art-page-content/index.vue
#	src/router/routes/asyncRoutes.ts
#	src/router/routesAlias.ts
#	src/views/admin/customer/customeraudit.vue
#	src/views/admin/customer/highseas.vue
#	src/views/admin/customer/index.vue
#	src/views/admin/inquiry_system/index.vue
#	src/views/admin/system/company.vue
#	src/views/admin/system/feishu_app.vue
#	src/views/admin/system/menu/index.vue
#	src/views/admin/system/role/index.vue
#	src/views/admin/system/systems.vue
#	src/views/admin/user/index.vue
#	src/views/admin/user/user_info.vue
#	src/views/auth/login/index.vue
#	src/views/crmpc/customer/Add.vue
#	src/views/crmpc/customer/Edit.vue
#	src/views/crmpc/customer/Followadd.vue
#	src/views/crmpc/customer/Show.vue
#	src/views/crmpc/customer/index.vue
#	src/views/crmpc/highseas/index.vue
#	src/views/crmpc/recycle/index.vue
#	src/views/crmpc/team/team_customer.vue
#	src/views/crmpc/team/user_customer.vue
#	src/views/dashboard/console/index.vue
#	src/views/system/user-center/index.vue
#	src/views/work/myOrder/index.vue
#	src/views/work/order/index.vue
2025-09-18 10:57:17 +08:00

1374 lines
34 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="approval-flow-container">
<div class="main-content">
<!-- 左侧基本信息 -->
<div class="left-panel">
<h3 class="panel-title">基本信息</h3>
<el-form :model="form" label-width="80px">
<el-form-item label="名称">
<el-input v-model="form.name" placeholder="请输入流程名称" />
</el-form-item>
<el-form-item label="业务类型">
<el-input v-model="form.type" placeholder="请输入业务类型" />
</el-form-item>
<el-form-item label="超时时间">
<ElInput v-model="form.timeout_hours" placeholder="请输入超时时间">
<template #suffix>
<span>h</span>
</template>
</ElInput>
</el-form-item>
<el-form-item label="超时动作">
<el-select v-model="form.timeout_action" placeholder="请选择超时动作">
<el-option label="请选择" value="" />
<el-option label="全部同意" value="all_agree" />
<el-option label="全部拒绝" value="all_reject" />
</el-select>
</el-form-item>
<el-form-item label="是否启用">
<el-switch v-model="form.is_active" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-form>
</div>
<!-- 中间审批流程 -->
<div class="middle-panel">
<h3 class="panel-title">审批流</h3>
<div class="flow-diagram">
<template v-for="(node, index) in form.nodes" :key="node.id">
<!-- 节点按钮 -->
<div class="node-container">
<el-button
type="primary"
icon="Plus"
@click="selectNode(node)"
class="node-btn"
:class="{
selected: selectedNodeId === node.id,
disabled: index === 0
}"
:disabled="index === 0"
>
</el-button>
<div class="node-info" @click="selectNode(node)">
<div class="node-title">{{ node.title }}</div>
<div class="node-subtitle">{{ node.subtitle }}</div>
</div>
<!-- 删除按钮 -->
<i
v-if="form.nodes.length >= 3 && index > 0"
class="iconfont-sys danger-color"
@click="removeNode(node.id)"
>&#xe6d4;</i
>
</div>
<!-- 连接线 -->
<div v-if="index < form.nodes.length" class="connection-line">
<div class="dotted-line"></div>
<el-button
type="primary"
icon="Plus"
@click="addNodeAfter(node.id)"
class="add-connection-btn"
size="small"
circle
/>
</div>
</template>
</div>
</div>
<!-- 右侧审批人配置 -->
<div class="right-panel">
<div class="panel-header">
<h3 class="panel-title">审批人</h3>
</div>
<!-- 提示信息 -->
<div v-if="!selectedNode || selectedNodeId === form.nodes[0]?.id" class="no-selection">
<div class="no-selection-content">
<i class="el-icon-info"></i>
<p v-if="!selectedNode">请选择一个节点进行配置</p>
<p v-else>申请人节点为默认节点,不可配置</p>
</div>
</div>
<div v-if="selectedNode && selectedNodeId !== form.nodes[0]?.id" class="approver-config">
<!-- 节点基本信息 -->
<div class="config-section">
<h4>节点信息</h4>
<el-form :model="selectedNode" label-width="60px" size="small">
<el-form-item label="名称">
<el-input
v-model="selectedNode.title"
placeholder="请输入节点名称"
@blur="updateNodeLabel"
/>
</el-form-item>
<el-form-item label="类型">
<el-select
v-model="selectedNode.type"
@change="onNodeTypeChange"
placeholder="请选择节点类型"
>
<el-option
v-for="item in approvalType"
:key="item.item_value"
:label="item.item_label"
:value="item.item_value"
/>
</el-select>
</el-form-item>
<el-form-item label="审批条件">
<el-input
v-model="selectedNode.condition"
placeholder="请输入审批条件)"
type="textarea"
:rows="2"
/>
</el-form-item>
<el-form-item label="是否必须">
<el-switch
v-model="selectedNode.required"
active-text="必须"
inactive-text="可选"
/>
</el-form-item>
</el-form>
</div>
<div class="config-section">
<h4>审批人</h4>
<el-checkbox-group v-model="selectedNode.approverUsers">
<!-- <el-checkbox label="上级领导">上级领导</el-checkbox> -->
</el-checkbox-group>
<div class="approver-groups">
<div
v-for="(group, groupIndex) in selectedNode.approverGroups"
:key="groupIndex"
class="approver-group"
>
<div class="group-header">
<span class="group-title">审批组 {{ groupIndex + 1 }}</span>
<el-button
v-if="selectedNode.approverGroups.length > 1"
type="danger"
size="small"
icon="Delete"
@click="removeApproverGroup(groupIndex)"
circle
/>
</div>
<div class="group-content">
<el-form-item label="类型" size="small">
<el-select
v-model="group.type"
placeholder="请选择审批类型"
@change="onGroupTypeChange(group)"
>
<el-option
v-for="item in approvalRole"
:key="item.item_value"
:label="item.item_label"
:value="item.item_value"
/>
</el-select>
</el-form-item>
<el-form-item label="选项" size="small">
<el-select
v-model="group.users"
multiple
placeholder="请选择"
:loading="group.loading"
ollapse-tags
filterable
:disabled="group.type === 'initiator_leader'"
>
<el-option
v-for="opt in group.options"
:key="opt.id"
:label="opt.name"
:value="opt.id"
/>
</el-select>
</el-form-item>
</div>
</div>
<el-button
type="primary"
size="small"
icon="Plus"
@click="addApproverGroup"
class="add-group-btn"
>
添加审批组
</el-button>
</div>
</div>
<div class="config-section">
<div class="cc-header">
<h4>是否抄送</h4>
<el-switch v-model="selectedNode.isCC" />
</div>
<div v-if="selectedNode.isCC" class="cc-config">
<h4>抄送人</h4>
<el-checkbox-group v-model="selectedNode.ccUsers">
<!-- <el-checkbox label="上级领导">上级领导</el-checkbox> -->
</el-checkbox-group>
<div class="cc-groups">
<div
v-for="(group, groupIndex) in selectedNode.ccGroups"
:key="groupIndex"
class="cc-group"
>
<div class="group-header">
<span class="group-title">抄送组 {{ groupIndex + 1 }}</span>
<el-button
v-if="selectedNode.ccGroups.length > 1"
type="danger"
size="small"
icon="Delete"
@click="removeCCGroup(groupIndex)"
>
</el-button>
</div>
<div class="group-content">
<el-form-item label="类型" size="small">
<el-select
v-model="group.type"
placeholder="请选择类型"
style="width: 100%"
@change="onGroupTypeChange(group)"
>
<el-option
v-for="item in approvalRole"
:key="item.item_value"
:label="item.item_label"
:value="item.item_value"
/>
</el-select>
</el-form-item>
<el-form-item label="选项" size="small">
<el-select
v-model="group.users"
multiple
placeholder="请选择"
:loading="group.loading"
:disabled="group.type === 'initiator_leader'"
>
<el-option
v-for="opt in group.options"
:key="opt.id"
:label="opt.name"
:value="opt.id"
/>
</el-select>
</el-form-item>
</div>
</div>
<el-button
type="primary"
size="small"
icon="Plus"
@click="addCCGroup"
class="add-group-btn"
>
添加抄送组
</el-button>
</div>
</div>
</div>
</div>
<!-- <div v-else class="no-selection">
<p>请选择一个节点进行配置</p>
</div> -->
</div>
</div>
<!-- 底部操作按钮 -->
<div class="bottom-actions">
<el-button @click="cancelFlow">取消</el-button>
<el-button type="primary" @click="saveFlow">保存</el-button>
</div>
<!-- 节点编辑弹框 -->
<el-dialog
v-model="editDialogVisible"
:title="editingNode.id === null ? '添加新节点' : '编辑节点名称'"
width="400px"
:close-on-click-modal="false"
>
<el-form :model="editingNode" label-width="80px">
<el-form-item label="节点名称">
<el-input v-model="editingNode.label" placeholder="请输入节点名称" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="cancelEdit">取消</el-button>
<el-button type="primary" @click="saveNode">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { commonService } from '@/api/commonApi'
import { manageService } from '@/api/adminApi'
defineOptions({ name: 'AdminApprovalEdit' })
//审批类型 AWF_STEP_TYPE
const approvalType = ref([])
//角色类型
const approvalRole = ref([])
const loadDictOptions = async () => {
const response = await commonService.getDictSelect({
scope: 'awf_step_type,awf_approvers_type',
scope_type: '1'
})
approvalType.value = response.awf_step_type
approvalRole.value = response.awf_approvers_type
}
const route = useRoute()
const router = useRouter()
onMounted(async () => {
await loadDictOptions()
await loadDetail()
})
// 审批流程数据
const form = reactive({
id: '',
name: '',
type: '',
timeout_hours: '',
timeout_action: '',
is_active: 1,
nodes: [
{
id: 1,
label: '申请人',
title: '申请人',
subtitle: '本人',
type: '',
condition: '',
required: true,
approverGroups: [
{
type: '',
users: [],
loading: false,
options: []
}
],
approvers: [],
businessApprovers: [],
approverUsers: [],
ccGroups: [
{
type: '',
users: [],
loading: false,
options: []
}
],
ccUsers: [],
isCC: false
},
{
id: 2,
label: '审批人',
title: '审批人',
subtitle: '上级',
type: '',
condition: '',
required: true,
approverGroups: [
{
type: '',
users: [],
loading: false,
options: []
}
],
approvers: [],
businessApprovers: [],
approverUsers: [],
ccGroups: [
{
type: '',
users: [],
loading: false,
options: []
}
],
ccUsers: [],
isCC: false
}
]
})
// 编辑弹框状态
const editDialogVisible = ref(false)
const editingNode = reactive({
id: null,
label: '',
afterNodeId: null // 用于记录在哪个节点后添加新节点
})
// 选中的节点
const selectedNodeId = ref(null)
const selectedNode = ref(null)
// 业务部用户数据(如需部门静态项,可在此填充)
// const businessUsers = ref([])
// 选项缓存,避免重复请求
const optionsCache = reactive({ role: [], user: [], department: [] })
// 懒加载并缓存不同类型的选项
const ensureOptionsLoaded = async (group) => {
const type = group.type
if (!type || type === 'initiator_leader') {
group.options = []
return
}
group.loading = true
try {
if (type === 'role') {
if (!optionsCache.role || optionsCache.role.length === 0) {
const response = await commonService.getRoleSelect()
optionsCache.role = response || []
}
console.log('optionsCache.role', optionsCache.role)
group.options = optionsCache.role
} else if (type === 'user') {
if (!optionsCache.user || optionsCache.user.length === 0) {
const response = await commonService.getUserSelect()
optionsCache.user = response || []
}
group.options = optionsCache.user
} else {
group.options = []
}
} finally {
group.loading = false
}
}
// 节点ID计数器
let nodeIdCounter = 3
// 选择节点
const selectNode = (node) => {
// 第一个节点(申请人)不可选择
const nodeIndex = form.nodes.findIndex((n) => n.id === node.id)
if (nodeIndex === 0) {
return
}
selectedNodeId.value = node.id
selectedNode.value = node
}
// 更新节点标签(当名称改变时)
const updateNodeLabel = () => {
if (selectedNode.value) {
selectedNode.value.label = selectedNode.value.title
}
}
// 节点类型改变
const onNodeTypeChange = (type) => {
if (selectedNode.value) {
// 根据类型设置默认值
if (type === 'applicant') {
selectedNode.value.subtitle = '本人'
} else if (type === 'approver') {
selectedNode.value.subtitle = '请选择'
} else if (type === 'cc') {
selectedNode.value.subtitle = '请选择'
selectedNode.value.isCC = true
}
}
}
// 添加审批组
const addApproverGroup = () => {
if (selectedNode.value) {
selectedNode.value.approverGroups.push({
type: '',
users: [],
loading: false,
options: optionsCache.department
})
}
}
// 删除审批组
const removeApproverGroup = (groupIndex) => {
if (selectedNode.value && selectedNode.value.approverGroups.length > 1) {
selectedNode.value.approverGroups.splice(groupIndex, 1)
}
}
// 添加抄送组
const addCCGroup = () => {
if (selectedNode.value) {
selectedNode.value.ccGroups.push({
type: '',
users: [],
loading: false,
options: optionsCache.department
})
}
}
// 删除抄送组
const removeCCGroup = (groupIndex) => {
if (selectedNode.value && selectedNode.value.ccGroups.length > 1) {
selectedNode.value.ccGroups.splice(groupIndex, 1)
}
}
// (已由 ensureOptionsLoaded 替代)
// 类型变化时,清空选项(如发起人上级则禁用)
const onGroupTypeChange = async (group) => {
group.users = []
group.options = []
await ensureOptionsLoaded(group)
}
// 加载详情并回显
const loadDetail = async () => {
const id = route.params.id || route.query.id
let data = {}
if (!id) return
await manageService.stockapprovaldetail({ id }).then((res) => {
data = res
})
console.log('data', data)
if (!data) return
form.id = data.id
form.name = data.name || ''
form.type = data.type || ''
form.timeout_hours = data.timeout_hours ?? ''
form.timeout_action = data.timeout_action || ''
form.is_active = data.is_active ? 1 : 0
data.steps = JSON.parse(data.steps)
console.log('data', data.steps)
// 重建节点:第一个默认申请人 + 每个步骤一个节点
const nodes = [form.nodes[0]]
if (Array.isArray(data.steps)) {
for (const step of data.steps) {
const node = {
id: ++nodeIdCounter,
label: step.name || '审批节点',
title: step.name || '审批节点',
subtitle: '上级',
type: step.type || 'any',
condition: step.condition || '',
required: !!step.required,
approverGroups: [],
approverUsers: [],
ccGroups: [],
ccUsers: [],
isCC: Array.isArray(step.cc) && step.cc.length > 0
}
// 审批组
if (Array.isArray(step.approvers)) {
for (const ap of step.approvers) {
const group = { type: ap.type || '', users: [], loading: false, options: [] }
await ensureOptionsLoaded(group)
if (ap.type === 'initiator_leader') {
group.users = []
} else if (ap.value) {
group.users = String(ap.value)
.split(',')
.map((v) => Number((v || '').trim()))
.filter((n) => !Number.isNaN(n))
}
node.approverGroups.push(group)
}
} else {
// 至少保留一个空组方便编辑
const group = { type: '', users: [], loading: false, options: [] }
await ensureOptionsLoaded(group)
node.approverGroups.push(group)
}
// 抄送组
if (Array.isArray(step.cc)) {
for (const c of step.cc) {
const group = { type: c.type || '', users: [], loading: false, options: [] }
await ensureOptionsLoaded(group)
if (c.type === 'initiator_leader') {
group.users = []
} else if (c.value) {
group.users = String(c.value)
.split(',')
.map((v) => Number((v || '').trim()))
.filter((n) => !Number.isNaN(n))
}
node.ccGroups.push(group)
}
} else {
const group = { type: '', users: [], loading: false, options: [] }
await ensureOptionsLoaded(group)
node.ccGroups.push(group)
}
nodes.push(node)
}
}
form.nodes = nodes
}
// 保存节点
const saveNode = () => {
if (editingNode.id === null) {
// 添加新节点
if (editingNode.afterNodeId) {
const afterIndex = form.nodes.findIndex((n) => n.id === editingNode.afterNodeId)
if (afterIndex !== -1) {
const newNode = {
id: ++nodeIdCounter,
label: editingNode.label,
title: editingNode.label,
subtitle: '请选择',
type: '',
condition: '',
required: true,
approverGroups: [
{
type: '',
users: [],
loading: false
}
],
approvers: [],
businessApprovers: [],
ccUsers: [],
businessCCUsers: [],
isCC: false
}
form.nodes.splice(afterIndex + 1, 0, newNode)
}
}
} else {
// 编辑现有节点
const nodeIndex = form.nodes.findIndex((n) => n.id === editingNode.id)
if (nodeIndex !== -1) {
form.nodes[nodeIndex].label = editingNode.label
form.nodes[nodeIndex].title = editingNode.label
}
}
// 重置编辑状态
editingNode.id = null
editingNode.label = ''
editingNode.afterNodeId = null
editDialogVisible.value = false
}
// 在指定节点后添加新节点
const addNodeAfter = (afterNodeId) => {
// 先打开编辑弹框,让用户输入节点名称
editingNode.id = null // 设置为null表示这是新节点
editingNode.label = ''
editingNode.afterNodeId = afterNodeId
editDialogVisible.value = true
}
// 取消编辑
const cancelEdit = () => {
// 重置编辑状态
editingNode.id = null
editingNode.label = ''
editingNode.afterNodeId = null
editDialogVisible.value = false
}
// 删除节点
const removeNode = (nodeId) => {
const nodeIndex = form.nodes.findIndex((n) => n.id === nodeId)
if (nodeIndex !== -1 && nodeIndex > 0) {
form.nodes.splice(nodeIndex, 1)
}
}
// 保存流程 -> 组装为后端需要的结构
const saveFlow = async () => {
const timeout = Number(form.timeout_hours || 0)
if (timeout > 0 && !form.timeout_action) {
ElMessage.error('当超时时间大于0时,必须选择超时动作')
return
}
const payload = {
id: form.id,
name: form.name,
type: form.type,
timeout_hours: timeout,
timeout_action: form.timeout_action,
is_active: !!form.is_active,
steps: []
}
const steps = []
form.nodes.forEach((node, idx) => {
// 跳过第一个默认的申请人节点
if (idx === 0) return
const approvers = []
// 组审批
if (Array.isArray(node.approverGroups)) {
node.approverGroups.forEach((group) => {
if (group && group.type === 'initiator_leader') {
approvers.push({ type: 'initiator_leader' })
} else if (group && group.type && Array.isArray(group.users) && group.users.length > 0) {
approvers.push({ type: group.type, value: group.users.join(',') })
}
})
}
// // 单选“上级领导”
// if (Array.isArray(node.approverUsers) && node.approverUsers.includes('上级领导')) {
// approvers.push({ type: 'initiator_leader' })
// }
const cc = []
if (node.isCC && Array.isArray(node.ccGroups)) {
node.ccGroups.forEach((group) => {
if (group && group.type === 'initiator_leader') {
cc.push({ type: 'initiator_leader' })
} else if (group && group.type && Array.isArray(group.users) && group.users.length > 0) {
cc.push({ type: group.type, value: group.users.join(',') })
}
})
}
// if (node.isCC && Array.isArray(node.ccUsers) && node.ccUsers.includes('上级领导')) {
// cc.push({ type: 'initiator_leader' })
// }
steps.push({
name: node.title || node.label || '',
type: node.type || 'any',
approvers,
cc,
condition: node.condition || '',
required: !!node.required
})
})
payload.steps = steps
const { message } = await manageService.stockapprovalupdate(payload)
ElMessage.success(message)
console.log('保存的流程数据:', payload)
setTimeout(() => {
router.back()
}, 300)
}
// 取消流程
const cancelFlow = () => {
console.log('取消流程编辑')
}
</script>
<style scoped>
.approval-flow-container {
display: flex;
flex-direction: column;
height: 88vh;
/* padding: 20px; */
/* gap: 20px; */
}
.main-content {
display: flex;
flex: 1;
gap: 20px;
}
.left-panel,
.middle-panel,
.right-panel {
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
height: 75vh;
}
.left-panel {
width: 300px;
flex-shrink: 0;
}
.middle-panel {
flex: 1;
min-width: 400px;
display: flex;
flex-direction: column;
}
.right-panel {
width: 350px;
flex-shrink: 0;
display: flex;
flex-direction: column;
}
.panel-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.panel-header .panel-title {
margin-bottom: 0;
border-bottom: none;
}
.unit {
margin-left: 8px;
color: #909399;
}
.flow-diagram {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 0;
flex: 1;
overflow-y: auto;
padding: 20px 10px;
max-height: calc(100vh - 200px);
}
.node-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 15px;
position: relative;
margin: 10px 0;
}
.node-btn {
width: 50px;
height: 50px !important;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
transition: all 0.3s ease;
}
.node-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
.node-btn.selected {
background-color: #67c23a;
border-color: #67c23a;
}
.node-btn.selected:hover {
background-color: #85ce61;
border-color: #85ce61;
}
.node-btn.disabled {
opacity: 0.6;
cursor: not-allowed;
background-color: #6ca3f5;
border-color: #6ca3f5;
color: #fff;
box-shadow: none;
}
.node-btn.disabled:hover {
background-color: #f5f7fa;
border-color: #dcdfe6;
color: #909399;
transform: none;
box-shadow: none;
}
.cc-node {
background-color: #67c23a;
border-color: #67c23a;
}
.cc-node:hover {
background-color: #85ce61;
border-color: #85ce61;
}
.node-info {
display: flex;
flex-direction: column;
gap: 4px;
/* min-width: 120px; */
text-align: left;
}
.node-title {
font-size: 16px;
font-weight: 600;
color: #303133;
line-height: 1.2;
}
.node-subtitle {
font-size: 14px;
color: #909399;
line-height: 1.2;
}
.danger-color {
position: absolute;
right: -30px;
top: 50%;
transform: translateY(-50%);
color: #f56c6c;
}
.connection-line {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
margin: 5px 0;
}
.dotted-line {
width: 2px;
height: 80px;
background: repeating-linear-gradient(
to bottom,
#409eff 0px,
#409eff 4px,
transparent 4px,
transparent 8px
);
margin: 10px 0;
}
.add-connection-btn {
width: 24px;
height: 24px;
padding: 0;
background-color: #409eff;
border-color: #409eff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
.add-connection-btn:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
.approver-config {
margin-top: 20px;
flex: 1;
overflow-y: auto;
padding-right: 10px;
}
/* 滚动条样式 */
.flow-diagram::-webkit-scrollbar,
.approver-config::-webkit-scrollbar {
width: 6px;
}
.flow-diagram::-webkit-scrollbar-track,
.approver-config::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.flow-diagram::-webkit-scrollbar-thumb,
.approver-config::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.flow-diagram::-webkit-scrollbar-thumb:hover,
.approver-config::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 确保滚动区域有足够的最小高度 */
.flow-diagram {
min-height: 300px;
}
.config-section {
margin-bottom: 30px;
}
.config-section h4 {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 15px;
}
.config-section:first-child {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #ebeef5;
}
.no-selection {
display: flex;
align-items: center;
justify-content: center;
height: 200px;
}
.no-selection-content {
text-align: center;
color: #909399;
}
.no-selection-content i {
font-size: 48px;
margin-bottom: 16px;
display: block;
}
.no-selection-content p {
margin: 0;
font-size: 14px;
}
.approver-list {
margin-bottom: 20px;
}
.config-section .el-checkbox-group {
margin-bottom: 20px;
}
.approver-groups {
margin-bottom: 20px;
}
.approver-group {
border: 1px solid #ebeef5;
border-radius: 6px;
margin-bottom: 16px;
padding: 16px;
background: #fafafa;
}
.group-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.group-title {
font-weight: 600;
color: #303133;
font-size: 14px;
}
.group-content {
display: flex;
gap: 16px;
align-items: flex-end;
}
.group-content .el-form-item {
flex: 1;
margin-bottom: 0;
}
.add-group-btn {
width: 100%;
margin-top: 8px;
}
.cc-config .el-checkbox-group {
margin-bottom: 20px;
}
.cc-groups {
margin-bottom: 20px;
}
.cc-group {
border: 1px solid #ebeef5;
border-radius: 6px;
margin-bottom: 16px;
padding: 16px;
background: #fafafa;
}
.department-group {
margin-top: 10px;
}
.cc-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.cc-header h4 {
margin: 0;
font-weight: 600;
color: #303133;
}
.cc-config {
margin-top: 15px;
}
.no-selection {
text-align: center;
color: #909399;
margin-top: 50px;
}
.no-selection p {
margin: 0;
}
.bottom-actions {
display: flex;
justify-content: center;
gap: 20px;
padding: 20px 0;
}
/* 弹框样式优化 */
:deep(.el-dialog__body) {
padding: 20px;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
:deep(.el-form-item__label) {
font-weight: 600;
color: #303133;
}
:deep(.el-input__inner) {
border-radius: 6px;
}
:deep(.el-select) {
width: 100%;
}
:deep(.el-select .el-input__inner) {
border-radius: 6px;
}
/* 移动端适配 */
@media (max-width: 768px) {
.approval-flow-container {
height: auto;
min-height: 100vh;
padding: 10px;
}
.main-content {
flex-direction: column;
gap: 15px;
}
.left-panel,
.middle-panel,
.right-panel {
width: 100%;
height: auto;
min-height: auto;
padding: 15px;
margin-bottom: 15px;
}
.left-panel {
order: 1;
}
.middle-panel {
order: 2;
min-width: auto;
}
.right-panel {
order: 3;
}
.flow-diagram {
max-height: 400px;
padding: 15px 5px;
}
.node-container {
flex-direction: column;
gap: 10px;
margin: 15px 0;
}
.node-btn {
width: 60px;
height: 60px;
font-size: 18px;
}
.node-info {
min-width: auto;
text-align: center;
}
.node-title {
font-size: 14px;
}
.node-subtitle {
font-size: 12px;
}
.connection-line {
margin: 0px 0;
}
.dotted-line {
height: 75px;
}
.add-node-btn {
width: 40px;
height: 40px;
font-size: 16px;
}
.approver-config {
max-height: 300px;
}
.approver-group,
.cc-group {
padding: 12px;
}
.group-content {
flex-direction: column;
gap: 12px;
}
.group-content .el-form-item {
flex: none;
}
.bottom-actions {
flex-direction: column;
gap: 15px;
padding: 15px 0;
}
.bottom-actions .el-button {
width: 100%;
}
.cc-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.cc-header h4 {
margin-bottom: 0;
}
.panel-title {
font-size: 14px;
margin-bottom: 15px;
}
.form-row {
flex-direction: column;
gap: 15px;
}
.form-row .el-form-item {
flex: none;
}
}
/* 超小屏幕适配 */
@media (max-width: 480px) {
.approval-flow-container {
padding: 5px;
}
.left-panel,
.middle-panel,
.right-panel {
padding: 10px;
}
.node-btn {
width: 50px;
height: 50px;
font-size: 16px;
}
.node-title {
font-size: 13px;
}
.node-subtitle {
font-size: 11px;
}
.flow-diagram {
padding: 10px 5px;
}
.approver-config {
max-height: 250px;
}
.approver-group,
.cc-group {
padding: 10px;
}
}
</style>