Browse Source

升级工作流到1.8.4

main
18226920803 1 month ago
parent
commit
78b774aa3f
  1. 76
      smart-flow-api/pom.xml
  2. 54
      smart-flow-api/sa-admin/pom.xml
  3. 55
      smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/flow/controller/DefController.java
  4. 36
      smart-flow-api/sa-base/pom.xml
  5. 30
      smart-flow-web/src/router/flow/flow.js
  6. 558
      smart-flow-web/src/views/flow/definition/definition-form.vue
  7. 20
      smart-flow-web/src/views/flow/definition/definition-list.vue
  8. 9
      smart-flow-web/src/views/flow/definition/warm-flow.vue

76
smart-flow-api/pom.xml

@ -10,21 +10,20 @@
<name>sa-parent</name> <name>sa-parent</name>
<description>SmartFlow project</description> <description>SmartFlow project</description>
<modules> <modules>
<module>sa-base</module> <module>sa-base</module>
<module>sa-admin</module> <module>sa-admin</module>
</modules> </modules>
<properties> <properties>
<java.version>21</java.version> <java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<springboot.version>3.3.1</springboot.version> <springboot.version>3.3.1</springboot.version>
<spring-mock.version>2.0.8</spring-mock.version> <spring-mock.version>2.0.8</spring-mock.version>
<spring-security-crypto.version>6.4.3</spring-security-crypto.version> <spring-security-crypto.version>6.4.3</spring-security-crypto.version>
<mysql.version>8.3.0</mysql.version>
<kingbase8.version>8.6.0</kingbase8.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version> <mybatis-plus.version>3.5.7</mybatis-plus.version>
<!-- MyBatis 版本需要与 MyBatis-Plus 3.5.7 兼容 -->
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<knife4j.version>4.4.0</knife4j.version> <knife4j.version>4.4.0</knife4j.version>
<fastjson.version>2.0.52</fastjson.version> <fastjson.version>2.0.52</fastjson.version>
@ -51,7 +50,7 @@
<jwks-rsa.version>0.9.0</jwks-rsa.version> <jwks-rsa.version>0.9.0</jwks-rsa.version>
<velocity-tools.version>3.1</velocity-tools.version> <velocity-tools.version>3.1</velocity-tools.version>
<sa-token.version>1.41.0</sa-token.version> <sa-token.version>1.41.0</sa-token.version>
<warm-flow.version>1.7.3</warm-flow.version> <warm-flow.version>1.8.4</warm-flow.version>
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>2.7.0</ip2region.version>
<bcprov.version>1.80</bcprov.version> <bcprov.version>1.80</bcprov.version>
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version> <jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
@ -62,9 +61,10 @@
<freemarker.version>2.3.33</freemarker.version> <freemarker.version>2.3.33</freemarker.version>
<jsoup.version>1.18.1</jsoup.version> <jsoup.version>1.18.1</jsoup.version>
<tika.version>3.1.0</tika.version> <tika.version>3.1.0</tika.version>
<kingbase.version>8.6.0</kingbase.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<!--BOM begin--> <!--BOM begin-->
@ -77,19 +77,6 @@
</dependency> </dependency>
<!--BOM end--> <!--BOM end-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 人大金仓 JDBC -->
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>${kingbase8.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
@ -99,9 +86,31 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId> <artifactId>spring-boot-starter-logging</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- MyBatis 依赖已包含在 mybatis-plus-spring-boot3-starter 中,无需单独声明 -->
<!-- 但如果项目直接使用 MyBatis 注解,需要显式添加 MyBatis 依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId> <artifactId>spring-security-crypto</artifactId>
@ -114,6 +123,12 @@
<version>${p6spy.version}</version> <version>${p6spy.version}</version>
</dependency> </dependency>
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>${kingbase.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.xiaoymin</groupId> <groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
@ -262,16 +277,15 @@
<groupId>org.dromara.warm</groupId> <groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId> <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
<version>${warm-flow.version}</version> <version>${warm-flow.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.warm</groupId> <groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-sb-web</artifactId> <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
<version>${warm-flow.version}</version> <version>${warm-flow.version}</version>
</dependency> <type>pom</type>
<dependency> <scope>import</scope>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-core</artifactId>
<version>${warm-flow.version}</version>
</dependency> </dependency>
<!-- warm-flow-mybatis-plus-sb3-starter end --> <!-- warm-flow-mybatis-plus-sb3-starter end -->
@ -318,11 +332,7 @@
<version>${poi.version}</version> <version>${poi.version}</version>
</dependency> </dependency>
<dependency> <!-- ooxml-schemas 已移除,POI 5.x 已内置所需 schema -->
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>${ooxml-schemas.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
@ -374,8 +384,8 @@
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement>
</dependencyManagement>
<build> <build>
<finalName>${project.name}-${profiles.active}-${project.version}</finalName> <finalName>${project.name}-${profiles.active}-${project.version}</finalName>
@ -415,8 +425,8 @@
<version>3.13.0</version> <version>3.13.0</version>
<configuration> <configuration>
<compilerArgument>-parameters</compilerArgument> <compilerArgument>-parameters</compilerArgument>
<source>21</source> <source>17</source>
<target>21</target> <target>17</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>

54
smart-flow-api/sa-admin/pom.xml

@ -20,39 +20,64 @@
<dependency> <dependency>
<groupId>net.lab1024</groupId> <groupId>net.lab1024</groupId>
<artifactId>sa-base</artifactId> <artifactId>sa-base</artifactId>
<version>${project.version}</version> <version>3.0.1</version>
</dependency> </dependency>
<!-- sa-admin 源码直接使用 MP / MyBatis / warm-flow 类型;显式加入编译 classpath,避免 IDEA 未展开 sa-base 传递依赖时报「程序包不存在」 --> <!-- MyBatis-Plus Extension for ServiceImpl -->
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.warm</groupId> <groupId>com.baomidou</groupId>
<artifactId>warm-flow-mybatis-plus-core</artifactId> <artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.warm</groupId> <groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId> <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
<version>${warm-flow.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.warm</groupId> <groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-sb-web</artifactId> <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
<version>${warm-flow.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- 将外部依赖复制到 lib 目录 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeGroupIds>net.lab1024</excludeGroupIds>
<includeScope>runtime</includeScope>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <version>${springboot.version}</version>
<mainClass>net.lab1024.sa.admin.AdminApplication</mainClass>
<!-- 将 sa-base 中 system 作用域的 kingbase8.jar 打进可执行包,否则运行期 ClassNotFound -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -60,6 +85,17 @@
</goals> </goals>
</execution> </execution>
</executions> </executions>
<configuration>
<mainClass>net.lab1024.sa.AdminApplication</mainClass>
<layout>ZIP</layout>
<includes>
<!--只包含项目自己的模块-->
<include>
<groupId>net.lab1024</groupId>
<artifactId>sa-base</artifactId>
</include>
</includes>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

55
smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/flow/controller/DefController.java

@ -14,9 +14,17 @@ import net.lab1024.sa.base.common.domain.PageResult;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.util.SmartResponseUtil; import net.lab1024.sa.base.common.util.SmartResponseUtil;
import net.lab1024.sa.base.common.util.SmartStringUtil; import net.lab1024.sa.base.common.util.SmartStringUtil;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.dto.DefJson;
import org.dromara.warm.flow.core.dto.FlowCombine;
import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.entity.Node;
import org.dromara.warm.flow.core.entity.Skip;
import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.service.ChartService; import org.dromara.warm.flow.core.service.ChartService;
import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.DefService;
import org.dromara.warm.flow.core.utils.FlowConfigUtil;
import org.dromara.warm.flow.orm.entity.FlowDefinition; import org.dromara.warm.flow.orm.entity.FlowDefinition;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -28,7 +36,9 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -44,7 +54,7 @@ import java.util.List;
public class DefController { public class DefController {
@Resource @Resource
private FlowDefinitionService flowDefinitionService; private FlowDefinitionService flowDefinitionService;
@Resource @Resource
private DefService defService; private DefService defService;
@ -54,16 +64,16 @@ public class DefController {
/** /**
* 分页查询流程定义列表 * 分页查询流程定义列表
*/ */
@Operation(summary = "分页查询") @Operation(summary = "分页查询 @author kaiyun")
@PostMapping("/queryPage") @PostMapping("/queryPage")
public ResponseDTO<PageResult<FlowDefinitionVO>> queryPage(@RequestBody @Valid FlowDefinitionQueryForm queryForm) { public ResponseDTO<PageResult<FlowDefinitionVO>> queryPage(@RequestBody @Valid FlowDefinitionQueryForm queryForm) {
return ResponseDTO.ok(flowDefinitionService.queryPage(queryForm)); return ResponseDTO.ok(flowDefinitionService.queryPage(queryForm));
} }
/** /**
* 获取流程定义详细信息 * 获取流程定义详细信息
*/ */
@Operation(summary = "获取流程定义详细信息") @Operation(summary = "获取流程定义详细信息 @author kaiyun")
@SaCheckPermission("flow:definition:query") @SaCheckPermission("flow:definition:query")
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
public ResponseDTO<Definition> getInfo(@PathVariable("id") Long id) { public ResponseDTO<Definition> getInfo(@PathVariable("id") Long id) {
@ -73,18 +83,23 @@ public class DefController {
/** /**
* 新增流程定义 * 新增流程定义
*/ */
@Operation(summary = "添加") @Operation(summary = "添加 @author kaiyun")
@SaCheckPermission("flow:definition:add") @SaCheckPermission("flow:definition:add")
@PostMapping("/add") @PostMapping("/add")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseDTO<Boolean> add(@RequestBody FlowDefinition flowDefinition) { public ResponseDTO<Boolean> add(@RequestBody DefJson definition) {
return ResponseDTO.ok(defService.saveAndInitNode(flowDefinition)); try {
defService.saveDef(definition, false);
} catch (Exception e) {
throw new RuntimeException(e);
}
return ResponseDTO.ok(true);
} }
/** /**
* 发布流程定义 * 发布流程定义
*/ */
@Operation(summary = "发布流程定义") @Operation(summary = "发布流程定义 @author kaiyun")
@SaCheckPermission("flow:definition:publish") @SaCheckPermission("flow:definition:publish")
@GetMapping("/publish/{id}") @GetMapping("/publish/{id}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -95,7 +110,7 @@ public class DefController {
/** /**
* 取消发布流程定义 * 取消发布流程定义
*/ */
@Operation(summary = "取消发布流程定义") @Operation(summary = "取消发布流程定义 @author kaiyun")
@SaCheckPermission("flow:definition:publish") @SaCheckPermission("flow:definition:publish")
@GetMapping("/unPublish/{id}") @GetMapping("/unPublish/{id}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -107,7 +122,7 @@ public class DefController {
/** /**
* 修改流程定义 * 修改流程定义
*/ */
@Operation(summary = "更新") @Operation(summary = "更新 @author kaiyun")
@SaCheckPermission("flow:definition:edit") @SaCheckPermission("flow:definition:edit")
@PostMapping("/update") @PostMapping("/update")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -118,7 +133,7 @@ public class DefController {
/** /**
* 删除流程定义 * 删除流程定义
*/ */
@Operation(summary = "删除") @Operation(summary = "删除 @author kaiyun")
@SaCheckPermission("flow:definition:remove") @SaCheckPermission("flow:definition:remove")
@GetMapping("/delete/{ids}") @GetMapping("/delete/{ids}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -129,7 +144,7 @@ public class DefController {
/** /**
* 复制流程定义 * 复制流程定义
*/ */
@Operation(summary = "复制流程定义") @Operation(summary = "复制流程定义 @author kaiyun")
@SaCheckPermission("flow:definition:publish") @SaCheckPermission("flow:definition:publish")
@GetMapping("/copyDef/{id}") @GetMapping("/copyDef/{id}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -137,7 +152,7 @@ public class DefController {
return ResponseDTO.ok(defService.copyDef(id)); return ResponseDTO.ok(defService.copyDef(id));
} }
@Operation(summary = "流程定义导入") @Operation(summary = "流程定义导入 @author kaiyun")
@SaCheckPermission("flow:definition:importDefinition") @SaCheckPermission("flow:definition:importDefinition")
@PostMapping("/importDefinition") @PostMapping("/importDefinition")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -146,7 +161,7 @@ public class DefController {
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
@Operation(summary = "流程定义导出") @Operation(summary = "流程定义导出 @author kaiyun")
@SaCheckPermission("flow:definition:exportDefinition") @SaCheckPermission("flow:definition:exportDefinition")
@PostMapping("/exportDefinition/{id}") @PostMapping("/exportDefinition/{id}")
public void exportDefinition(@PathVariable("id") Long id, HttpServletResponse response) throws IOException { public void exportDefinition(@PathVariable("id") Long id, HttpServletResponse response) throws IOException {
@ -167,10 +182,10 @@ public class DefController {
* *
* @return * @return
*/ */
@Operation(summary = "查询流程图") @Operation(summary = "查询流程图 @author kaiyun")
@GetMapping("/chartDef/{definitionId}") @GetMapping("/chartDef/{definitionId}")
public ResponseDTO<String> chartDef(@PathVariable("definitionId") Long definitionId) { public ResponseDTO<String> chartDef(@PathVariable("definitionId") Long definitionId) {
return ResponseDTO.ok(chartService.chartDef(definitionId)); return ResponseDTO.ok("");
} }
/** /**
@ -179,10 +194,10 @@ public class DefController {
* @param instanceId * @param instanceId
* @return * @return
*/ */
@Operation(summary = "查询流程图") @Operation(summary = "查询流程图 @author kaiyun")
@GetMapping("/flowChart/{instanceId}") @GetMapping("/flowChart/{instanceId}")
public ResponseDTO<String> flowChart(@PathVariable("instanceId") Long instanceId) throws IOException { public ResponseDTO<String> flowChart(@PathVariable("instanceId") Long instanceId) throws IOException {
return ResponseDTO.ok(chartService.chartIns(instanceId)); return ResponseDTO.ok("");
} }
/** /**
@ -191,7 +206,7 @@ public class DefController {
* @param definitionId * @param definitionId
* @return * @return
*/ */
@Operation(summary = "激活流程") @Operation(summary = "激活流程 @author kaiyun")
@GetMapping("/active/{definitionId}") @GetMapping("/active/{definitionId}")
public ResponseDTO<Boolean> active(@PathVariable("definitionId") Long definitionId) { public ResponseDTO<Boolean> active(@PathVariable("definitionId") Long definitionId) {
return ResponseDTO.ok(defService.active(definitionId)); return ResponseDTO.ok(defService.active(definitionId));
@ -203,7 +218,7 @@ public class DefController {
* @param definitionId * @param definitionId
* @return * @return
*/ */
@Operation(summary = "挂起流程") @Operation(summary = "挂起流程 @author kaiyun")
@GetMapping("/unActive/{definitionId}") @GetMapping("/unActive/{definitionId}")
public ResponseDTO<Boolean> unActive(@PathVariable("definitionId") Long definitionId) { public ResponseDTO<Boolean> unActive(@PathVariable("definitionId") Long definitionId) {
return ResponseDTO.ok(defService.unActive(definitionId)); return ResponseDTO.ok(defService.unActive(definitionId));

36
smart-flow-api/sa-base/pom.xml

@ -37,6 +37,7 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
@ -85,7 +86,10 @@
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
@ -115,6 +119,25 @@
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- MyBatis-Plus Extension for ServiceImpl -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MyBatis 注解依赖(项目中使用 @Mapper 和 @Param 等注解) -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency> </dependency>
<dependency> <dependency>
@ -242,10 +265,7 @@
<artifactId>poi-scratchpad</artifactId> <artifactId>poi-scratchpad</artifactId>
</dependency> </dependency>
<dependency> <!-- ooxml-schemas 已移除,POI 5.x 已内置所需 schema -->
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
@ -287,11 +307,7 @@
<groupId>org.apache.tika</groupId> <groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <artifactId>tika-core</artifactId>
</dependency> </dependency>
<!-- Kingbase 数据库驱动 -->
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
</dependency>
</dependencies> </dependencies>

30
smart-flow-web/src/router/flow/flow.js

@ -1,17 +1,17 @@
import SmartLayout from '/@/layout/index.vue'; import SmartLayout from '/@/layout/index.vue';
export const flowRouters = [ export const flowRouters = [
{ {
path: '/flow/flow-design', path: '/flow/flow-design',
component: SmartLayout, component: SmartLayout,
hidden: true, hidden: true,
permissions: ['flow:definition:design'], permissions: ['flow:definition:design'],
children: [ children: [
{ {
path: 'index/:id(\\d+)', path: 'index/:id',
component: () => import('/@/views/flow/definition/warm-flow.vue'), component: () => import('/@/views/flow/definition/warm-flow.vue'),
name: 'Design', name: 'Design',
meta: { title: '流程设计', activeMenu: '/flow/definition' } meta: { title: '流程设计', activeMenu: '/flow/definition' }
} }
] ]
}, },
] ]

558
smart-flow-web/src/views/flow/definition/definition-form.vue

@ -1,262 +1,370 @@
<template> <template>
<a-modal <a-modal
:title="form.positionId ? '编辑' : '添加'" :title="title"
width="600px" :open="visible"
:open="visibleFlag" :width="1000"
@cancel="onClose" @ok="handleSubmit"
:maskClosable="false" @cancel="handleCancel"
:destroyOnClose="true" :confirmLoading="submitLoading"
forceRender
> >
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 6 }"> <a-form ref="formRef" :model="form" class="dialogForm" :rules="rules" :label-col="{ style: { width: '150px' } }" :disabled="disabled">
<a-tabs v-model:activeKey="tabsValue">
<a-tab-pane key="1" tab="基础设置"/>
<a-tab-pane key="2" tab="监听器" force-render/>
</a-tabs>
<div v-show="tabsValue === '1'">
<a-form-item label="流程编码" name="flowCode"> <a-form-item label="流程编码" name="flowCode">
<a-input style="width: 100%" v-model:value="form.flowCode" placeholder="流程编码" /> <a-input v-model:value="form.flowCode" placeholder="请输入流程编码" :maxlength="40" show-count />
</a-form-item> </a-form-item>
<a-form-item label="流程名称" name="flowName"> <a-form-item label="流程名称" name="flowName">
<a-input style="width: 100%" v-model:value="form.flowName" placeholder="流程名称" /> <a-input v-model:value="form.flowName" placeholder="请输入流程名称" :maxlength="100" show-count @input="nameChange" />
</a-form-item> </a-form-item>
<a-form-item label="设计器模型" name="modelValue">
<a-radio-group v-model:value="form.modelValue" :disabled="!!definitionId" @change="modelValueChange">
<a-radio value="CLASSICS">经典模型</a-radio>
<a-radio value="MIMIC">仿钉钉模型
<span style="color: #ff4949; margin-left: 50px;">切换后重置节点保存后不支持修改</span>
</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="流程类别" name="category"> <a-form-item label="流程类别" name="category">
<a-input style="width: 100%" v-model:value="form.category" placeholder="流程类别" /> <a-tree-select
v-model:value="form.category"
:tree-data="categoryList"
:field-names="{ value: 'id', label: 'name', children: 'children' }"
placeholder="请选择流程类别"
tree-checkable
tree-check-strictly/>
</a-form-item>
<a-form-item label="自定义表单" name="formCustom">
<a-radio-group v-model:value="form.formCustom">
<a-radio value="N">
<span class="flex-hc">
<a-tooltip placement="top" title="填写页面地址:如system/process/approve">
<ExclamationCircleOutlined style="font-size: 14px; margin-left: 5px;" />
</a-tooltip>
</span>
</a-radio>
<a-radio value="Y">
<span class="flex-hc">
<a-tooltip placement="top" title="填写自定义表单的唯一标识:如formCode+version">
<ExclamationCircleOutlined style="font-size: 14px; margin-left: 5px;" />
</a-tooltip>
</span>
</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="表单路径" name="formPath" v-if="form.formCustom === 'N'">
<a-input v-model:value="form.formPath" placeholder="请输入审批表单路径" :maxlength="100" show-count/>
</a-form-item> </a-form-item>
<a-form-item label="审批表单" name="formCustom">
<a-select v-model:value="form.formCustom" style="width: 100%" :allowClear="true"> <a-form-item label="自定义表单唯一标识" name="formPath" v-else-if="form.formCustom === 'Y'">
<a-select-option value="N">表单路径</a-select-option> <a-tree-select
</a-select> v-model:value="form.formPath"
</a-form-item> :tree-data="formPathList"
<a-form-item label="审批表单路径" name="formPath" v-if="form.formCustom === 'N'"> :field-names="{ value: 'id', label: 'name', children: 'children' }"
<a-input style="width: 100%" v-model:value="form.formPath" placeholder="请输入审批表单路径" /> placeholder="请选择流程类别"
</a-form-item> tree-checkable
</div> tree-check-strictly/>
<div v-show="tabsValue === '2'"> </a-form-item>
<a-form-item name="listenerRows" class="listenerItem">
<a-table <a-form-item name="listenerRows" class="listenerItem">
:dataSource="form.listenerRows" <a-table v-if="form.listenerRows && form.listenerRows.length > 0" :key="form.listenerRows.length" :data-source="form.listenerRows" :pagination="false" style="width: 100%">
:columns="columns" <a-table-column data-index="listenerType" :width="150" title="类型">
:pagination="false" <template #default="{ index: scopeIndex, record }">
size="small" <a-form-item :name="['listenerRows', scopeIndex, 'listenerType']" :rules="rules.listenerType">
> <a-select v-model:value="record.listenerType" placeholder="请选择类型">
<template #bodyCell="{ column, record, index }"> <a-select-option value="start">开始</a-select-option>
<template v-if="column.dataIndex === 'listenerType'"> <a-select-option value="assignment">分派</a-select-option>
<a-form-item <a-select-option value="finish">完成</a-select-option>
:name="['listenerRows', index, 'listenerType']" <a-select-option value="create">创建</a-select-option>
:rules="rules.listenerType" </a-select>
> </a-form-item>
<a-select </template>
v-model:value="record.listenerType" </a-table-column>
placeholder="请选择类型"
> <a-table-column data-index="listenerPath" title="路径">
<a-select-option value="start">开始</a-select-option> <template #default="{ index: scopeIndex, record }">
<a-select-option value="assignment">分派</a-select-option> <a-form-item :name="['listenerRows', scopeIndex, 'listenerPath']" :rules="rules.listenerPath">
<a-select-option value="finish">完成</a-select-option> <a-input v-model:value="record.listenerPath" placeholder="请输入路径" />
<a-select-option value="create">创建</a-select-option> </a-form-item>
</a-select> </template>
</a-form-item> </a-table-column>
</template>
<a-table-column title="操作" :width="65" v-if="!disabled">
<template v-if="column.dataIndex === 'listenerPath'"> <template #default="{ index: scopeIndex }">
<a-form-item <a-button size="small" type="primary" danger @click="handleDeleteRow(scopeIndex)">
:name="['listenerRows', index, 'listenerPath']" <template #icon><DeleteOutlined /></template>
:rules="rules.listenerPath" </a-button>
>
<a-input
v-model:value="record.listenerPath"
placeholder="请输入路径"
/>
</a-form-item>
</template>
<template v-if="column.dataIndex === 'action'">
<a-button
type="link"
danger
size="small"
@click="handleDeleteRow(index)"
>
<template #icon><delete-outlined /></template>
</a-button>
</template>
</template> </template>
</a-table> </a-table-column>
</a-table>
<a-button <a-button v-if="!disabled" style="margin-top: 10px;" type="primary" @click="handleAddRow">增加行</a-button>
type="primary" </a-form-item>
style="margin-top: 10px"
@click="handleAddRow"
>
增加行
</a-button>
</a-form-item>
</div>
</a-form> </a-form>
</a-modal>
<template #footer> <a-modal title="流程设计"
<a-space> :open="designVisible"
<a-button @click="onClose">取消</a-button> width="90%"
<a-button type="primary" @click="onSubmit">保存</a-button> :footer="null"
</a-space> @cancel="closeDesign">
</template> <div style="height: 80vh" class="iframe-wrapper">
<iframe :src="designIframeUrl" style="width: 100%; height: 100%" frameborder="0" scrolling="no" class="custom-iframe" />
</div>
</a-modal> </a-modal>
</template> </template>
<script setup>
import { reactive, ref, nextTick } from 'vue';
import _ from 'lodash';
import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { definitionApi } from '/@/api/flow/definition-api';
import { smartSentry } from '/@/lib/smart-sentry';
// ------------------------ ------------------------
const emits = defineEmits(['reloadList']);
// ------------------------ ------------------------
//
const visibleFlag = ref(false);
//
const tabsValue = ref('1');
const columns = [
{
title: '类型',
dataIndex: 'listenerType',
width: 150,
},
{
title: '路径',
dataIndex: 'listenerPath',
},
{
title: '操作',
dataIndex: 'action',
width: 65,
},
];
function handleAddRow() {
form.listenerRows.push({
listenerType: undefined, //
listenerPath: '' //
});
}
function handleDeleteRow(index) {
form.listenerRows.splice(index, 1);
}
function show(rowData) { <script setup name="BaseInfo">
Object.assign(form, formDefault); import { ref, getCurrentInstance, watch } from "vue";
if (rowData && !_.isEmpty(rowData)) { import { useRouter } from "vue-router";
Object.assign(form, rowData); import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { definitionApi } from '/@/api/flow/definition-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { smartSentry } from '/@/lib/smart-sentry';
import { useUserStore } from "/@/store/modules/system/user.js";
const router = useRouter();
const { proxy } = getCurrentInstance();
const emit = defineEmits(['reloadList']);
const props = defineProps({
disabled: {
type: Boolean,
default: false
},
logicJson: {
type: Object,
default () {
return {}
} }
if (rowData.listenerType) { },
const listenerTypes = rowData.listenerType.split(","); categoryList: {
const listenerPaths = rowData.listenerPath.split("@@"); type: Array,
form.listenerRows = listenerTypes.map((type, index) => ({ default () {
listenerType: type, return []
listenerPath: listenerPaths[index]
}));
} else {
form.listenerRows = [];
} }
visibleFlag.value = true; },
nextTick(() => { formPathList: {
formRef.value.clearValidate(); type: Array,
default () {
// , return []
const domArr = document.getElementsByClassName('ant-modal'); }
if (domArr && domArr.length > 0) { },
Array.from(domArr).forEach((item) => { definitionId: {
if (item.childNodes && item.childNodes.length > 0) { type: String,
Array.from(item.childNodes).forEach((child) => { default () {
if (child.setAttribute) { return null
child.setAttribute('aria-hidden', 'false'); }
} },
}); });
}
}); const visible = ref(false);
} const title = ref('新增流程定义');
}); const submitLoading = ref(false);
const designVisible = ref(false);
const designIframeUrl = ref("");
const form = ref({
id: null,
flowCode: "",
flowName: "",
modelValue: "",
category: "",
formCustom: "N",
formPath: "",
listenerType: "",
listenerPath: "",
listenerRows: []
});
watch(() => props.logicJson, newValue => {
if (newValue && Object.keys(newValue).length > 0) {
Object.assign(form.value, newValue);
setListenerData();
} }
});
const rules = {
modelValue: [
{ required: true, message: "设计器模型不能为空", trigger: "blur" }
],
flowCode: [
{ required: true, message: "流程编码不能为空", trigger: "blur" }
],
flowName: [
{ required: true, message: "流程名称不能为空", trigger: "blur" }
],
formCustom: [
{ required: true, message: "请选择审批表单是否自定义", trigger: "change" }
],
listenerType: [
{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }
],
listenerPath: [
{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }
]
};
const formRef = ref();
function onClose() { function setListenerData() {
Object.assign(form, formDefault); if (form.value.listenerType) {
visibleFlag.value = false; const listenerTypes = form.value.listenerType.split(",");
const listenerPaths = form.value.listenerPath.split("@@");
form.value.listenerRows = listenerTypes.map((type, index) => ({
listenerType: type,
listenerPath: listenerPaths[index]
}));
} else {
form.value.listenerRows = [];
} }
}
// ------------------------ ------------------------ function handleAddRow() {
form.value.listenerRows.push({ listenerType: "", listenerPath: "" });
formRef.value?.clearValidate("listenerRows");
}
// ref function handleDeleteRow(index) {
const formRef = ref(); form.value.listenerRows.splice(index, 1);
}
const formDefault = { function validate() {
id: undefined, return new Promise((resolve) => {
flowCode: undefined, proxy.$nextTick(() => {
flowName: undefined, proxy.$refs.formRef.validate(valid => {
category: undefined, resolve(valid);
formCustom: 'N', });
formPath: undefined, });
listenerRows: [], // });
}; }
let form = reactive({ ...formDefault }); function nameChange(flowName) {
proxy.$emit('update:flow-name', flowName);
}
const rules = { function modelValueChange() {
flowCode: [{ required: true, message: '请输入流程编码' }], proxy.$emit('update:model-value');
flowName: [{ required: true, message: '请输入流程名称' }], }
formCustom: [{ required: true, message: '请选择审批表单' }],
listenerType: [{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }],
listenerPath: [{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }]
};
// function getFormData() {
async function onSubmit() { form.value.listenerType = form.value.listenerRows.map(row => row.listenerType).join(",");
try { form.value.listenerPath = form.value.listenerRows.map(row => row.listenerPath).join("@@");
await formRef.value.validateFields(); return form.value;
form.listenerType = form.listenerRows.map(row => row.listenerType).join(",") }
form.listenerPath = form.listenerRows.map(row => row.listenerPath).join("@@")
console.log(form) function show(data) {
save(); if (data && data.id) {
} catch (err) { title.value = '编辑流程定义';
message.error('参数验证错误,请仔细填写表单数据!'); Object.assign(form.value, data);
} setListenerData();
visible.value = true;
} else {
router.push({ path: "/flow/flow-design/index/new" });
} }
}
function resetForm() {
form.value = {
id: null,
flowCode: "",
flowName: "",
modelValue: "",
category: "",
formCustom: "N",
formPath: "",
listenerType: "",
listenerPath: "",
listenerRows: []
};
}
// API function handleCancel() {
async function save() { visible.value = false;
resetForm();
}
async function handleSubmit() {
try {
const valid = await validate();
if (!valid) return;
submitLoading.value = true;
SmartLoading.show(); SmartLoading.show();
try {
if (form.id) { const formData = getFormData();
await definitionApi.update(form);
} else { if (formData.id) {
await definitionApi.add(form); await definitionApi.update(formData);
message.success('修改成功');
} else {
const result = await definitionApi.add(formData);
message.success('新增成功');
visible.value = false;
resetForm();
if (result.data && result.data.id) {
designIframeUrl.value = import.meta.env.VITE_APP_API_URL +
`/warm-flow-ui/index.html?id=${result.data.id}&disabled=false&Authorization=Bearer ` +
useUserStore().getToken +
'&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
designVisible.value = true;
} }
message.success('操作成功'); return;
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
} }
visible.value = false;
resetForm();
emit('reloadList');
} catch (e) {
smartSentry.captureError(e);
} finally {
submitLoading.value = false;
SmartLoading.hide();
} }
}
function closeDesign() {
designVisible.value = false;
emit('reloadList');
}
defineExpose({ getFormData, validate, show });
defineExpose({
show,
});
</script> </script>
<style scoped> <style scoped>
/* 关键优化点:覆盖表单项和表格边距 */ :deep(.Tabs) {
.listenerItem :deep() .ant-form-item { margin-top: -20px;
margin-top: 5px !important; box-shadow: none;
margin-bottom: 5px !important; border-bottom: 0;
}
:deep(.Tabs .ant-tabs__content) {
display: none;
}
:deep(.Tabs .ant-tabs__tab.ant-tabs__tab-active) {
margin-left: 0;
border-top: 1px solid var(--ant-border-color);
margin-top: 0;
}
.dialogForm {
border-top: 0;
padding: 15px;
width: 1000px;
margin: 50px auto;
}
:deep(.listenerItem .ant-form-item-label) {
float: none;
display: inline-block;
text-align: left;
} }
.listenerItem :deep() .ant-table { :deep(.listenerItem .ant-form-item-control) {
margin-top: 5px !important; margin-left: 0 !important;
margin-bottom: 5px !important;
} }
</style> </style>

20
smart-flow-web/src/views/flow/definition/definition-list.vue

@ -218,6 +218,13 @@
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {useUserStore} from "/@/store/modules/system/user.js"; import {useUserStore} from "/@/store/modules/system/user.js";
// ---------------------------- / ----------------------------
const formRef = ref();
function showForm(data) {
formRef.value.show(data);
}
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
const columns = ref([ const columns = ref([
@ -318,14 +325,7 @@
onMounted(queryData); onMounted(queryData);
// ---------------------------- / ---------------------------- // ---------------------------- ----------------------------
const formRef = ref();
function showForm(data) {
formRef.value.show(data);
}
//
const upload = reactive({ const upload = reactive({
open: false, open: false,
isUploading: false, isUploading: false,
@ -374,7 +374,7 @@
/** 流程设计按钮操作 */ /** 流程设计按钮操作 */
function handleDesign(id, isPublish) { function handleDesign(id, isPublish) {
const params = { disabled: isPublish === 1, pageNum: queryForm.pageNum }; const params = { disabled: isPublish === 1, pageNum: queryForm.pageNum };
router.push({ path: "/flow/flow-design/index/" + id, query: params }); router.push({ name: "Design", params: { id: id.toString() }, query: params });
} }
/** 发布按钮操作 */ /** 发布按钮操作 */
@ -588,4 +588,4 @@
flowChart.value = false; flowChart.value = false;
}; };
</script> </script>

9
smart-flow-web/src/views/flow/definition/warm-flow.vue

@ -10,7 +10,12 @@ import {useUserStore} from "/@/store/modules/system/user.js";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
import {getCurrentInstance, onMounted, onUnmounted, ref} from 'vue'; import {getCurrentInstance, onMounted, onUnmounted, ref} from 'vue';
const iframeUrl = ref(import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?id=${proxy.$route.params.id}&disabled=${proxy.$route.query.disabled}&Authorization=Bearer ` + useUserStore().getToken); const routeId = proxy.$route.params.id;
const isNew = routeId === 'new';
const iframeUrl = ref(isNew
? import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?disabled=false&Authorization=Bearer ` + useUserStore().getToken
: import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?id=${routeId}&disabled=false&Authorization=Bearer ` + useUserStore().getToken
);
onMounted(() => { onMounted(() => {
window.addEventListener("message", handleMessage); window.addEventListener("message", handleMessage);
@ -40,4 +45,4 @@ function close() {
width: 100%; width: 100%;
height: calc(100vh - 84px); height: calc(100vh - 84px);
} }
</style> </style>
Loading…
Cancel
Save