一、数据库及依赖安装

1、数据库文件导入

1.1 字典数据导入

导入sql目录下字典数据文件dict.sql

1.2 菜单数据导入

导入sql目录下菜单数据文件sys_auth_rule.sql

注意:sys_auth_rule.sql为脚本,需要在mysql命令中执行。

1.3 流程相关数据表导入

导入sql目录下流程相关数据表文件flow_table.sql

2、前端依赖安装

前端自定义表单功能使用了组件form-create,流程设计器使用的是logicflow 需要安装相关依赖:
通过修改package.json文件添加依赖,在dependencies块中添加(推荐使用此方式):

"dependencies": {
    "@form-create/component-wangeditor": "^3.2.3",
    "@form-create/designer": "^3.2.6",
    "@form-create/element-ui": "^3.2.6",
    "@logicflow/core": "^2.0.0",
    "@logicflow/extension": "^2.0.0",
    "vue-json-pretty": "^2.3.0",
}

添加后运行:npm install --registry=https://registry.npmmirror.com
或者通过命令行安装(如果使用了上面方式安装,跳过,不需重复安装):

#执行以下安装命令
npm install @logicflow/core@2.0.0 @logicflow/extension@2.0.0 @form-create/element-ui@3.2.6 @form-create/designer@3.2.6 @form-create/component-wangeditor@3.2.3 vue-json-pretty@2.3.0 --save --registry=https://registry.npmmirror.com

3、后端依赖安装

在后端项目路径下,运行go get github.com/looplab/fsm安装后端依赖,fsm是一个用go语言开发的有限状态机(Finite State Machine,FSM),是一种数学模型,用于描述系统在不同状态下的行为和转移条件。
状态机有三个组成部分:状态(State)、事件(Event)、动作(Action),事件(转移条件)触发状态的转移和动作的执行。动作的执行不是必须的,可以只转移状态,不指定任何动作。总体而言,状态机是一种用以表示有限个状态以及这些状态之间的转移和动作的执行等行为的数学模型。

二、覆盖项目文件

2.1 覆盖后端文件

将流程审批插件中go目录下的文件直接覆盖到后端项目根目录下即可。

2.2 覆盖前端文件

将流程审批插件中vue目录下的文件直接覆盖到前端项目根目录下即可。

覆盖完成后重启前后端项目,然后登陆到系统后台点击清除缓存:


缓存清理完成后即可看到流程审批相关菜单:

三、自定义表单发起审批及流程审批

流程审批支持两种审批模式,一种是直接使用自定义表单发起审批,比如一些常见简单的表单。另一种是将自己开发好的业务接入到流程审批(下一节讲解)。

3.1 新建表单


添加表单字段:

保存表单:

此时在表单列表可以看到已添加的表单,状态是未部署,部署后才能在表单中心查看

部署后,进入表单中心即可查看和发起审批:

3.2 新建审批流程

流程模型列表中新增审批流程:

新增成功后点击流程设计:

可以设计一个流程来测试,注意设计流程时需要包含一个开始节点或至少一个结束节点。


创建流程审批模型完成后,可对表单发起审批:

四、已存在业务功能接入流程审批

一些复杂的业务功能可能自定义表单无法满足,需要自己定制开发,开发完成后可以手动接入流程审批

下面演示如何从开发一个业务功能到接入审批流程的完整开发流程:

4.1 新建一个用于演示的数据表

# 例如创建一个加班申请表
CREATE TABLE `demo_work_apply` (
  `id` bigint(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '-1回退修改0 保存中1流程中 2通过',
  `updated_at` datetime DEFAULT NULL COMMENT '更新日期',
  `created_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '用户id',
  `reason` varchar(100) NOT NULL DEFAULT '' COMMENT '加班原因',
  `start_date` datetime NULL COMMENT '加班起始日期',
  `end_date` datetime NULL COMMENT '加班截止日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='加班申请表';

注意:表格中应有一个字段来标记状态,比如statustag等,字段名称不限制可自定义
创建完表后,通过代码生成工具生成对应功能,代码生成请参考代码生成使用章节。






配置完成后,点击生成代码后,重启后端服务即可查看到对应功能:

我们添加几条数据:

4.2 接入流程审批功能

业务功能生成后,开始接入审批流程。

4.2.1 添加列表页发起审批按钮

进入后端代码页面,修改列表页返回数据添加发起审批按钮如下图:

修改相关代码:

# internal\app\demo\model\demo_work_apply.go 大约43行添加字段ActionBtn
# 并在该文件顶部引入依赖 "github.com/gogf/gf/v2/frame/g"

type DemoWorkApplyListRes struct {
    Id          uint64                   `json:"id" dc:"主键"`
    Title       string                   `json:"title" dc:"标题"`
    Status      int                      `json:"status" dc:"审核状态"`
    CreatedUser *systemModel.LinkUserRes `orm:"with:id=created_by" json:"createdUser"`
    CreatedBy   uint64                   `json:"createdBy" dc:"用户id"`
    Reason      string                   `json:"reason" dc:"加班原因"`
    StartDate   *gtime.Time              `json:"startDate" dc:"加班起始日期"`
    EndDate     *gtime.Time              `json:"endDate" dc:"加班截止日期"`
    ActionBtn   g.MapStrAny              `json:"actionBtn"`
}


# internal\app\demo\logic\demoWorkApply\demo_work_apply.go 大约20行引入依赖
import(
    ...
    flowConsts "github.com/tiger1103/gfast/v3/internal/app/flow/consts"
    flowService "github.com/tiger1103/gfast/v3/internal/app/flow/service"
    ...
)


# internal\app\demo\logic\demoWorkApply\demo_work_apply.go 大约80行
for k, v := range res {
    var btn g.MapStrAny
    btn, err = flowService.FlowModel().SetBtn(ctx, v.Id, flowConsts.FlowTypeBusiness, "demo_work_apply","title", "status", v.Status, systemService.Context().GetUserId(ctx))
    liberr.ErrIsNil(ctx, err)
    listRes.List[k] = &model.DemoWorkApplyListRes{
        Id:          v.Id,
        Title:       v.Title,
        Status:      v.Status,
        CreatedUser: v.CreatedUser,
        CreatedBy:   v.CreatedBy,
        Reason:      v.Reason,
        StartDate:   v.StartDate,
        EndDate:     v.EndDate,
        ActionBtn:   btn,
    }
}

4.2.2 修改前端列表页面

在列表页面添加按钮:

引入审批组件:

添加组件方法:

相关代码:

<!-- 在文件src\views\demo\demoWorkApply\list\index.vue大约168行添加发起审批按钮 -->
<el-button
    v-if="scope.row.actionBtn && scope.row.actionBtn.type!='disabled'"
    type="primary"
    link
    @click="handleStartFlow(scope.row)"
><el-icon><ele-Coordinate/></el-icon>{{scope.row.actionBtn.title}}</el-button>

<!-- 在文件src\views\demo\demoWorkApply\list\index.vue大约201行添加审批组件 -->
<checkFlow ref="ckFlowRef" @getList="demoWorkApplyList  "></checkFlow>
// 在文件src\views\demo\demoWorkApply\list\index.vue大约205行引入审批组件
import checkFlow from "/@/components/gFlow/checkFlow.vue"


// 在文件src\views\demo\demoWorkApply\list\index.vue大约350行添加审批处理方法
const ckFlowRef = ref()
const handleStartFlow =(row: DemoWorkApplyTableColumns|null)=>{
  ckFlowRef.value.handleStartFlow(row)
}

完成后刷新页面可看到页面多了发起审批按钮,点击即可发起审批:

到流程管理中新建一个用于加班申请的流程:
先到数据字典创建审批分类,找到工作流程类别字典类型添加一个类别

再到流程模型列表中新增流程:


回到加班申请页面发起审批:


进入审批页面:

可以查看当前审批的流程图节点信息和审批日志记录:

修改列表页状态标签颜色:


<!-- internal\app\demo\logic\demoWorkApply\demo_work_apply.go 大约143行 -->
<el-table-column label="审核状态" align="center" prop="status" min-width="150px">
   <template #default="scope">
      <el-tag :type="columnColor(scope.row)">{{ statusFormat(scope.row) }}</el-tag>
   </template>
</el-table-column>
// internal\app\demo\logic\demoWorkApply\demo_work_apply.go 最后添加一个方法
const columnColor=(row:FlowDemoTableColumns)=>{
  switch (row.status.toString()){
    case '-1':
      return "danger"
    case '0':
      return "info"
    case '1':
      return "warning"
    case '2':
      return "success"
  }
}

添加完成后:

4.2.3 详情页改造,支持切换查看详情、审批记录和流程图

详情页面,添加标签切换:

引入相关组件添加对应方法:




对应代码:

<!--在文件 src\views\demo\demoWorkApply\list\component\detail.vue顶部表单详情使用el-tabs包裹-->
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick" style="margin: 8px;">
        <el-tab-pane label="表单详情" name="0">
          <el-descriptions
              class="margin-top"
              :column="3"
              border
              style="margin: 8px;"
      >        
          <el-descriptions-item :span="1">            
              <template #label>
                <div class="cell-item">
                  主键
                </div>
              </template>
              {{ formData.id }}            
          </el-descriptions-item>        
          <el-descriptions-item :span="1">            
              <template #label>
                <div class="cell-item">
                  标题
                </div>
              </template>
              {{ formData.title }}            
          </el-descriptions-item>        
          <el-descriptions-item :span="1">              
                <template #label>
                  <div class="cell-item">
                    审核状态
                  </div>
                </template>
                {{ proxy.getOptionValue(formData.status, statusOptions,'value','label') }}            
          </el-descriptions-item>        
          <el-descriptions-item :span="1">            
              <template #label>
                <div class="cell-item">
                  用户id
                </div>
              </template>
              {{ formData.createdBy }}            
          </el-descriptions-item>        
          <el-descriptions-item :span="1">            
              <template #label>
                <div class="cell-item">
                  加班原因
                </div>
              </template>
              {{ formData.reason }}            
          </el-descriptions-item>        
          <el-descriptions-item :span="1">
            <template #label>
              <div class="cell-item">
                加班起始日期
              </div>
            </template>
            {{ proxy.parseTime(formData.startDate, '{y}-{m}-{d} {h}:{i}:{s}') }}
          </el-descriptions-item>        
          <el-descriptions-item :span="1">
            <template #label>
              <div class="cell-item">
                加班截止日期
              </div>
            </template>
            {{ proxy.parseTime(formData.endDate, '{y}-{m}-{d} {h}:{i}:{s}') }}
          </el-descriptions-item>        
      </el-descriptions>
        </el-tab-pane>
        <el-tab-pane label="审批记录" name="1">
            <FlowLog ref="flowLogRef" :form-id="formId" :form-table="formTable" :key="formId"/>
        </el-tab-pane>
        <el-tab-pane label="流程图" name="2">
          <ShowFlowDesign ref="showFlowCheckRef" />
        </el-tab-pane>
      </el-tabs>
// 在文件 src\views\demo\demoWorkApply\list\component\detail.vue大约100行引入相关组件依赖
import FlowLog from "/@/components/gFlow/flowLog.vue";
import ShowFlowDesign from "/@/components/gFlow/showDesign.vue";
import {getRunStep} from "/@/api/flow/flowModel";

// 在文件 src\views\demo\demoWorkApply\list\component\detail.vue大约111行添加相关常量及方法
const flowLogRef = ref()
const showFlowCheckRef = ref()
const formId = ref(0)
const formTable = ref('')
const activeName = ref('0')
const handleClick = (tab: TabsPaneContext) => {
  if(tab.index=="1"){
    flowLogRef.value.getLogList()
  }else if(tab.index=="2"){
    getRunStep({formTable:formTable.value,formId:formId.value}).then((res:any)=>{
        showFlowCheckRef.value.showDesign({flowId:res.data.data?.runFlow||'0',processId:res.data.data?.runFlowNode||''})
      })
    }
  }

//在文件 src\views\demo\demoWorkApply\list\component\detail.vue大约156行赋值
// 打开弹窗
const openDialog = (row?: DemoWorkApplyInfoData) => {
  resetForm();
  if(row) {
    formId.value = row.actionBtn.wfFid
    formTable.value = row.actionBtn.wfType
    getDemoWorkApply(row.id!).then((res:any)=>{
      const data = res.data;        
      data.createdBy = data.createdUser?.userNickname        
      state.formData = data;
    })
  }
  state.isShowDialog = true;
};

//在文件 src\views\demo\demoWorkApply\list\component\detail.vue大约188行restFrom中添加
const resetForm = ()=>{
    state.formData = {      
      ...
    }
    activeName.value = '0' //添加此行
  }

//在文件 src\views\demo\demoWorkApply\list\component\model.ts大约21行添加actionBtn
export interface DemoWorkApplyInfoData {    
    ...
    actionBtn?:any
}

完成后刷新页面,点击详情按钮即可查看:

至此流程接入完成。

视频接入讲解:https://www.bilibili.com/video/BV1ta2zY7EpT

作者:管理员  创建时间:2024-10-08 09:24
最后编辑:管理员  更新时间:2024-10-14 10:54