Merge branch 'ly' into dev

main
刘妍 2024-03-19 17:03:47 +08:00
commit eaf2ce65f2
20 changed files with 850 additions and 78 deletions

16
src/api/sys/WFDelegate.ts Normal file
View File

@ -0,0 +1,16 @@
// WFDelegate 流程模版基本信息
import { defHttp } from '@/utils/http/axios';
import {DetailParams} from './model/wfSchemeInfoModel'
enum Api {
// 创建流程
LoadMyUserList = '/api/WFDelegate/LoadMyUserList',
}
/**
* @description: getLoadMyUserList
*/
export function getLoadMyUserList(params?: DetailParams) {
return defHttp.get({ url: Api.LoadMyUserList, params });
}

34
src/api/sys/WFProcess.ts Normal file
View File

@ -0,0 +1,34 @@
// WFProcess 流程模版基本信息
import { defHttp } from '@/utils/http/axios';
import {CreateParams} from './model/WFProcessModel'
enum Api {
// 创建流程
Create = '/api/WFProcess/Create',
SaveDraft='/api/WFProcess/SaveDraft'
}
/**
* @description:
*/
export function create( params?:CreateParams) {
return defHttp.post(
{
url: Api.Create,
params,
},
);
}
/**
* @description: 稿
*/
export function saveDraft( params?:CreateParams) {
return defHttp.post(
{
url: Api.SaveDraft,
params,
},
);
}

View File

@ -14,6 +14,8 @@ import {SchemeListParams,GetSchemeModel,AddParams,
// 流程模板详细信息
LoadHistoryPage= '/api/WFScheme/LoadPage',
UpdateScheme='/api/WFSchemeInfo/UpdateScheme',
// 获取自定义流程列表(流程发起页)
GetInfoList='/api/WFSchemeInfo/GetInfoList'
}
/**
@ -59,3 +61,10 @@ export function updateScheme(params:SchemeParams){
return defHttp.post({ url: Api.UpdateScheme + "?id=" + params.id +"&schemeId="+params.schemeId});
}
/**
* @description: getInfoList
*/
export function getInfoList(params) {
return defHttp.get({ url: Api.GetInfoList, params });
}

View File

@ -0,0 +1,21 @@
/**
* @description:
*/
export interface CreateParams {
processId: string,
schemeCode: string,
title: string,
userId: string,
toUserId: string,
nextUsers: {
additionalProp1: string,
additionalProp2: string,
additionalProp3: string
},
des: string,
code: string,
name: string,
stampImg: string,
stampPassWord: string,
nextId: string
}

View File

@ -2,22 +2,39 @@
<div :class="prefixCls" :style="{ width: `${props.width}px` }">
<a-tabs v-model:activeKey="configActiveName">
<a-tab-pane :tab="data.wfNodeName" key="1">
<!-- 开始节点 -->
<start-event-option ref="startRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:StartEvent' ? '' : 'hidden'"></start-event-option>
<!-- 审核节点 -->
<user-task-option ref="taskRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:Task' ? '' : 'hidden'">
</user-task-option>
<!-- 结束节点 -->
<end-event-option ref="endRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:class="data.currentWfNode.type=='bpmn:EndEvent' ? '' : 'hidden'"></end-event-option>
<!-- 子流程 -->
<subprocess-option ref="subprocessRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:SubProcess' ? '' : 'hidden'"></subprocess-option>
<!-- 排他网关 -->
<exclusive-gateway-option ref="exclusiveGatewayRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:ExclusiveGateway' ? '' : 'hidden'">
</exclusive-gateway-option>
<!-- 并行网关 -->
<parallel-gateway-option ref="parallelGatewayRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:ParallelGateway' ? '' : 'hidden'">
</parallel-gateway-option>
<!-- 包容网关 -->
<!-- "bpmn:InclusiveGateway" -->
<inclusive-gateway-option ref="inclusiveGatewayRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType"
:class="data.currentWfNode.type=='bpmn:InclusiveGateway' ? '' : 'hidden'">
</inclusive-gateway-option>
<!-- 线条 -->
<myline-option ref="mylineRef" :element="data.currentWfNode" :schemeCode="props.schemeCode"
:pageView="props.pageView" :pageType="props.pageType" :wfData="data.wfData" :flowRef="data.flowRef"
:class="data.currentWfNode.type=='bpmn:SequenceFlow' ? '' : 'hidden'">
@ -55,10 +72,12 @@
import userTaskOption from './userTask/index.vue'
//
import endEventOption from './endEvent/index.vue'
//
import gatewayAndOption from './config/gatewayAnd.vue'
//
import parallelGatewayOption from './parallelGateway/index.vue'
//
import exclusiveGatewayOption from './exclusiveGateway/index.vue'
//
import inclusiveGatewayOption from './inclusiveGateway/index.vue'
// Xor
import gatewayXorOption from './config/gatewayXor.vue'
//
@ -96,13 +115,15 @@
const taskRef = ref < any > ()
const endRef = ref < any > ()
const subprocessRef = ref < any > ()
const exclusiveGatewayRef = ref < any > ()
const exclusiveGatewayRef = ref < any > ()
const parallelGatewayRef = ref < any > ()
const inclusiveGatewayRef = ref < any > ()
const configActiveName = ref('2')
const data = reactive({
currentWfNode: undefined,
wfNodeName: "",
wfData:[],
flowRef:{}
wfData: [],
flowRef: {}
})
watch(
() => props.element,
@ -153,6 +174,20 @@
type: newVal.type
}
break;
case 'ParallelGateway':
data.wfNodeName = "并行网关"
data.currentWfNode = {
id: newVal.id,
type: newVal.type
}
break;
case 'InclusiveGateway':
data.wfNodeName = "包含网关"
data.currentWfNode = {
id: newVal.id,
type: newVal.type
}
break;
case 'SequenceFlow':
data.wfNodeName = "线条"
data.currentWfNode = {
@ -160,9 +195,9 @@
type: newVal.type
}
// 线id
data.flowRef={
from:newVal.businessObject.sourceRef.id,
to:newVal.businessObject.targetRef.id,
data.flowRef = {
from: newVal.businessObject.sourceRef.id,
to: newVal.businessObject.targetRef.id,
}
// 使
getWfData()
@ -279,8 +314,8 @@
}
}
});
data.wfData = wfData
data.wfData = wfData
}
async function validatePanel() {
let res = await shcemeinfoRef.value.validateForm();
@ -320,6 +355,16 @@
if (gatewayInclusiveFrom != undefined) {
wfData.push(gatewayInclusiveFrom)
}
//
let parallelGatewayFrom = await !(parallelGatewayRef.value) ? {} : parallelGatewayRef.value.getForm();
if (parallelGatewayFrom != undefined) {
wfData.push(parallelGatewayFrom)
}
//
let inclusiveGatewayFrom = await !(inclusiveGatewayRef.value) ? {} : inclusiveGatewayRef.value.getForm();
if (inclusiveGatewayFrom != undefined) {
wfData.push(inclusiveGatewayFrom)
}
// 3.
wfData.forEach(node => {
if (node.type == 'myline' && !node.isInit) {

View File

@ -1,42 +0,0 @@
<!-- 开始节点配置 -->
<template>
<el-form
class="l-form-config"
label-width="88px"
label-position="left"
size="mini"
>
<el-form-item label="节点标识">
<el-input v-model="node.id" readonly ></el-input>
</el-form-item>
<div style="padding:0 16px;">
<el-alert
title="并行网关说明"
type="info"
description="并行网关会等待所有分支汇入才往下执行,所有出口分支都会被执行"
show-icon
:closable="false"
>
</el-alert>
</div>
</el-form>
</template>
<script>
export default {
name:'gateway-and-option',
data () {
return {
}
},
computed: {
node(){
return this.wfdesign.currentWfNode;
}
},
inject: ["wfdesign"]
}
</script>
<style>
</style>

View File

@ -41,6 +41,7 @@
<script lang="ts" setup>
import conditionFormula from './src/conditionFormula.vue'
import conditionSql from './src/conditionSql.vue'
import { getDetail } from '@/api/sys/WFSchemeInfo'
import { reactive, ref, onMounted, nextTick, unref, h, watch, defineProps } from 'vue';
const labelCol = { span: 7 };
const wrapperCol = { span: 17 };
@ -72,7 +73,7 @@
}
}
)
const data = reactive({
tableBtns: [
{ prop: 'Edit', label: '编辑' },
@ -89,6 +90,8 @@
editRow: null,
rowIndex: 0,
componentDisabled: props.pageType == 'detail' ? true : false
})
function getNode() {
var content = JSON.parse(props.pageView)
@ -104,7 +107,6 @@
let wfData = scheme.wfData
wfData.forEach(element => {
if (element.id == node.value.id) {
console.log(node)
node.value = element
}
});
@ -139,8 +141,9 @@
async function handleFormulaOk() {
conditionFormulaRef.value.validateForm()
let obj = conditionFormulaRef.value.getForm()
console.log(obj.name)
if (obj.name != '') {
if (obj.name == '' || obj.dbCode == '' || obj.sql == '') {
data.formulaVisible = true;
}else{
data.formulaVisible = false;
}
@ -148,6 +151,13 @@
function handleSqlOk() {
console.log(conditionSqlRef.value.getForm())
// this.sqlVisible = false;
conditionSqlRef.value.validateForm()
let obj = conditionSqlRef.value.getForm()
if (obj.name == '' || obj.table == '' || obj.value == '' || obj.rfield == '' || obj.cfield == '' || obj.compareType == '') {
data.formulaVisible = true;
}else{
data.formulaVisible = false;
}
}
function getForm() {
return node.value

View File

@ -124,12 +124,11 @@
function validateForm() {
formRef.value
.validate()
.then((values) => {
return formData.value
.then(async (values) => {
return true
})
.catch((error) => {
console.log(error)
return {}
.catch(async (error) => {
return false
});
}
function setForm(data) {

View File

@ -43,23 +43,27 @@
]
},
})
async function getForm() {
let res = await formRef.value
//
function validateForm() {
formRef.value
.validate()
.then((values) => {
return formData.value
.then(async (values) => {
return true
})
.catch((error: ValidateErrorEntity<FormState>) => {
return {}
.catch(async (error) => {
return false
});
return res;
}
function getForm() {
return formData.value
}
defineExpose({
getForm
getForm,
validateForm
})
</script>
<style scoped>
.l-from-body{
.l-from-body {
padding: 10px 30px;
}
</style>
</style>

View File

@ -0,0 +1,178 @@
<template>
<div class="user-task">
<a-form ref="formRef" :rules="rules" :model="node" labelAlign="left" :label-col="labelCol"
:wrapper-col="wrapperCol" :disabled="data.componentDisabled">
<a-form-item label="节点标识">
<a-input v-model:value="node.id" placeholder="请输入" readonly />
</a-form-item>
<a-alert message="包容网关说明"
description="包容网关会等待所有分支汇入才往下执行出口分支能执行多条条件为true" type="info"
show-icon />
<a-space>
<a-radio-group>
<a-radio-button value="1" @click="handleFormulaClick"></a-radio-button>
<a-radio-button value="2" @click="handleSQlClick">sql</a-radio-button>
</a-radio-group>
<a-button danger :size="size" @click="handleClearClick"></a-button>
</a-space>
<a-table :columns="data.columns" :data-source="node.conditions" bordered :pagination="false">
<template #bodyCell="{ column, text, record }">
<template v-if="['name', 'type'].includes(column.dataIndex)">
<div>
{{ text }}
</div>
</template>
<template v-else-if="column.dataIndex === 'operation'">
<a-button>编辑</a-button>
<a-button danger @click='onDelete(record.name,false)'>删除</a-button>
</template>
</template>
</a-table>
</a-form>
<a-modal width="40%" v-model:open="data.formulaVisible" title="添加公式条件" @ok="handleFormulaOk">
<conditionFormula ref="conditionFormulaRef"></conditionFormula>
</a-modal>
<a-modal width="40%" v-model:open="data.sqlVisible" title="添加sql条件" @ok="handleSqlOk">
<conditionSql ref="conditionSqlRef"></conditionSql>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import conditionFormula from '../exclusiveGateway/src/conditionFormula.vue'
import conditionSql from '../exclusiveGateway/src/conditionSql.vue'
import { getDetail } from '@/api/sys/WFSchemeInfo'
import { reactive, ref, onMounted, nextTick, unref, h, watch, defineProps } from 'vue';
const labelCol = { span: 7 };
const wrapperCol = { span: 17 };
const conditionFormulaRef = ref < any > ()
const conditionSqlRef = ref < any > ()
const props = defineProps({
element: Object,
schemeCode: String,
pageType: String,
pageView: String,
})
const node = ref({
conditions: [],
id: ''
})
watch(
() => props.element,
(newVal, oldVal) => {
console.log(newVal)
if (newVal.type == "bpmn:ExclusiveGateway") {
node.value.id = newVal.id
node.value.type = newVal.type
if (props.pageType == 'detail') {
getNode()
}
if (props.schemeCode) {
getDetailInfo()
}
}
}
)
const data = reactive({
tableBtns: [
{ prop: 'Edit', label: '编辑' },
{ prop: 'Delete', label: '删除' }
],
columns: [
{ title: '类型', dataIndex: 'type' },
{ title: '名称', dataIndex: 'name' },
],
tableData: [],
formulaVisible: false,
sqlVisible: false,
editRow: null,
rowIndex: 0,
componentDisabled: props.pageType == 'detail' ? true : false
})
function getNode() {
var content = JSON.parse(props.pageView)
content.wfData.forEach(element => {
if (element.id == node.value.id) {
node.value = element
}
});
}
async function getDetailInfo() {
let data = await getDetail({ code: props.schemeCode })
let scheme = JSON.parse(data.scheme.content);
let wfData = scheme.wfData
wfData.forEach(element => {
if (element.id == node.value.id) {
node.value = element
}
});
}
onMounted(() => {
if (props.schemeCode) {
getDetailInfo()
}
if (props.pageType == 'detail') {
getNode()
}
})
function typeFormat(type) {
switch (type) {
case '1':
return '表达式'
case '2':
return 'sql语句'
}
}
function handleFormulaClick() {
data.formulaVisible = true;
}
function handleSQlClick() {
data.sqlVisible = true;
}
function handleClearClick() {
node.conditions = [];
}
async function handleFormulaOk() {
conditionFormulaRef.value.validateForm()
let obj = conditionFormulaRef.value.getForm()
if (obj.name == '' || obj.dbCode == '' || obj.sql == '') {
data.formulaVisible = true;
}else{
data.formulaVisible = false;
}
}
function handleSqlOk() {
console.log(conditionSqlRef.value.getForm())
// this.sqlVisible = false;
conditionSqlRef.value.validateForm()
let obj = conditionSqlRef.value.getForm()
if (obj.name == '' || obj.table == '' || obj.value == '' || obj.rfield == '' || obj.cfield == '' || obj.compareType == '') {
data.formulaVisible = true;
}else{
data.formulaVisible = false;
}
}
function getForm() {
return node.value
}
defineExpose({
getForm
})
</script>
<style scoped>
::v-deep .ant-alert-info {
margin-bottom: 20px;
}
::v-deep .ant-table-wrapper {
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,94 @@
<!-- 开始节点配置 -->
<template>
<div class="user-task">
<a-form ref="formRef" :rules="rules" :model="node" labelAlign="left" :label-col="labelCol"
:wrapper-col="wrapperCol" :disabled="data.componentDisabled">
<a-form-item label="节点标识">
<a-input v-model:value="node.id" placeholder="请输入" readonly />
</a-form-item>
<a-alert message="并行网关说明"
description="并行网关会等待所有分支汇入才往下执行,所有出口分支都会被执行" type="info"
show-icon />
</a-form>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted, nextTick, unref, h, watch, defineProps } from 'vue';
import { getDetail } from '@/api/sys/WFSchemeInfo'
const labelCol = { span: 7 };
const wrapperCol = { span: 17 };
const props = defineProps({
element: Object,
schemeCode: String,
pageType: String,
pageView: String,
})
const node = ref({
id: ''
})
watch(
() => props.element,
(newVal, oldVal) => {
console.log(newVal)
if (newVal.type == "bpmn:ParallelGateway") {
node.value.id = newVal.id
node.value.type = newVal.type
if (props.pageType == 'detail') {
getNode()
}
if (props.schemeCode) {
getDetailInfo()
}
}
}
)
const data = reactive({
componentDisabled: props.pageType == 'detail' ? true : false
})
function getNode() {
var content = JSON.parse(props.pageView)
content.wfData.forEach(element => {
if (element.id == node.value.id) {
node.value = element
}
});
}
async function getDetailInfo() {
let data = await getDetail({ code: props.schemeCode })
let scheme = JSON.parse(data.scheme.content);
let wfData = scheme.wfData
wfData.forEach(element => {
if (element.id == node.value.id) {
node.value = element
}
});
}
onMounted(() => {
if (props.schemeCode) {
getDetailInfo()
}
if (props.pageType == 'detail') {
getNode()
}
})
function getForm() {
return node.value
}
defineExpose({
getForm
})
</script>
<style scoped>
::v-deep .ant-alert-info {
margin-bottom: 20px;
}
::v-deep .ant-table-wrapper {
margin-top: 20px;
}
</style>

View File

@ -218,6 +218,14 @@
node.value.authFields = node.value.authFields.filter(item => item.field !== key);
}
function getForm() {
if(node.value.messageType != ""){
if(node.value.messageType.length > 1){
node.value.messageType = node.value.messageType.join(',')
}else{
node.value.messageType = node.value.messageType[0]
}
}
return node.value
}
defineExpose({

View File

@ -334,7 +334,6 @@
let wfData = scheme.wfData
wfData.forEach(element => {
if (element.id == node.value.id) {
console.log(node)
node.value = element
}
});
@ -420,6 +419,11 @@
}
function getForm() {
if (node.value.id != '') {
if (node.value.overtimeMessageType.length > 1) {
node.value.overtimeMessageType = node.value.overtimeMessageType.join(',')
} else {
node.value.overtimeMessageType = node.value.overtimeMessageType[0]
}
return node.value
}
}

View File

@ -0,0 +1,102 @@
<template>
<div class="my-process-designer">
<div class="my-process-designer__header">
<slot name="control-header"></slot>
<template v-if="!$slots['control-header']">
<div slot="content">
<a-space>
<a-tooltip placement="bottom" class="ml-2" title="缩小视图">
<a-button :disabled="data.defaultZoom <= 0.3" :icon="h(ZoomOutOutlined)" @click="processZoomOut()"></a-button>
</a-tooltip>
<a-button>{{ Math.floor(data.defaultZoom * 10 * 10) + "%" }}</a-button>
<a-tooltip placement="bottom" title="放大视图">
<a-button :disabled="data.defaultZoom >= 3.9" :icon="h(ZoomInOutlined)" @click="processZoomIn()"></a-button>
</a-tooltip>
</a-space>
</div>
</template>
<div>
</div>
</div>
<div class="my-process-designer__container">
<div class="my-process-designer__canvas" ref="bpmn-canvas" id="view"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import '@/components/ProcessDesigner/package/theme/index.scss';
import { h, ref, provide, reactive, onMounted, defineProps, computed, defineEmits, onBeforeMount, watch } from 'vue';
import { SaveOutlined, ZoomOutOutlined, ZoomInOutlined, RotateLeftOutlined, RotateRightOutlined, ClearOutlined } from '@ant-design/icons-vue';
import BpmnViewer from 'bpmn-js/lib/Viewer'
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
const data = reactive({
bpmnModeler: null,
defaultZoom: 1,
})
const props = defineProps({
xml: String
})
watch(
() => props.xml,
(newVal, oldVal) => {
createDiagram(newVal)
}
)
onMounted(() => {
initBpmnModeler()
createDiagram(props.xml)
})
function initBpmnModeler() {
if (data.bpmnModerler) return
const containerEl = document.getElementById('view')
data.bpmnModerler && data.bpmnModerler.destroy() //
data.bpmnModerler = new BpmnViewer({
container: containerEl,
additionalModules: [
//
MoveCanvasModule
],
})
}
async function createDiagram(xml) {
const viewer = data.bpmnModerler
try {
const result = await data.bpmnModerler.importXML(xml)
const { warnings } = result
if (warnings && warnings.length) {
warnings.forEach(warn => console.warn(warn))
}
const canvas = viewer.get('canvas')
canvas.zoom('fit-viewport')
} catch (err) {
console.log(err.message, err.warnings)
}
}
function processZoomIn(zoomStep = 0.1) {
let newZoom = Math.floor(data.defaultZoom * 100 + zoomStep * 100) / 100;
if (newZoom > 4) {
throw new Error("[Process Designer Warn ]: The zoom ratio cannot be greater than 4");
}
data.defaultZoom = newZoom;
data.bpmnModerler.get("canvas").zoom(data.defaultZoom);
}
function processZoomOut(zoomStep = 0.1) {
let newZoom = Math.floor(data.defaultZoom * 100 - zoomStep * 100) / 100;
if (newZoom < 0.2) {
throw new Error("[Process Designer Warn ]: The zoom ratio cannot be less than 0.2");
}
data.defaultZoom = newZoom;
data.bpmnModerler.get("canvas").zoom(data.defaultZoom);
}
</script>
<style scoped>
.my-process-designer{
width: 100%;
}
::v-deep .bjs-container a {
visibility: hidden;
}
</style>

View File

@ -178,6 +178,7 @@
"vxeTable": "VxeTable"
},
"workflow":{
"scheme_preview":"流程模板设计"
"scheme_preview":"流程模板设计",
"create_preview":"流程发起"
}
}

View File

@ -25,7 +25,7 @@ const dashboard: AppRouteModule = {
},
{
path: 'scheme_preview/:id',
name: 'AccountDetail',
name: 'SchemePreview',
meta: {
hideMenu: true,
title: t('routes.demo.workflow.scheme_preview'),
@ -35,6 +35,18 @@ const dashboard: AppRouteModule = {
},
component: () => import('@/views/demo/workflow/scheme/preview.vue'),
},
{
path: 'create_preview/:id',
name: 'CreatePreview',
meta: {
hideMenu: true,
title: t('routes.demo.workflow.create_preview'),
ignoreKeepAlive: true,
showMenu: false,
currentActiveMenu: '/workflow/create',
},
component: () => import('@/views/demo/workflow/create/preview.vue'),
},
],
};

View File

@ -2,7 +2,21 @@ const hexList: string[] = [];
for (let i = 0; i <= 15; i++) {
hexList[i] = i.toString(16);
}
export function buildGUID(): string {
let guid = '';
for (let i = 1; i <= 36; i++) {
if (i === 9 || i === 14 || i === 19 || i === 24) {
guid += '-';
} else if (i === 15) {
guid += 4;
} else if (i === 20) {
guid += hexList[(Math.random() * 4) | 8];
} else {
guid += hexList[(Math.random() * 16) | 0];
}
}
return guid;
}
export function buildUUID(): string {
let uuid = '';
for (let i = 1; i <= 36; i++) {

View File

@ -1,3 +1,105 @@
<template>
流程发起
</template>
<PageWrapper :class="prefixCls">
<div :class="`${prefixCls}__content`" v-for="item in cardList">
<div :class="`${prefixCls}__list-title`">{{item.category}}{{item.data.length}}</div>
<List>
<Row :gutter="16">
<template v-for="itemchild in item.data" :key="item.id">
<Col :span="6">
<List.Item>
<Card :hoverable="true" :class="`${prefixCls}__card`" @click="launch(itemchild.code)">
<div :class="`${prefixCls}__card-title`">
<Icon class="icon" v-if="itemchild.icon" :icon="itemchild.icon" :color="itemchild.color" />
{{ itemchild.name }}
</div>
</Card>
</List.Item>
</Col>
</template>
</Row>
</List>
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import Icon from '@/components/Icon/Icon.vue';
import { PageWrapper } from '@/components/Page';
import { Card, Row, Col, List } from 'ant-design-vue';
import { getInfoList } from '@/api/sys/WFSchemeInfo.ts'
import { useGo } from '@/hooks/web/usePage';
const go = useGo();
const prefixCls = 'list-card';
let cardList = ref()
function launch(code){
console.log(code)
go('/dashboard/create_preview/add?code='+code);
}
async function getList() {
let data = await getInfoList()
let newArr = {}
data.map(item => {
newArr[item.category] = newArr[item.category] || []; //category(namesex)[]
newArr[item.category].push(item); //itemcategory
})
let list = [];
Object.keys(newArr).forEach(v => {
let obj = {};
obj.category = v;
obj.data = newArr[v]
list.push(obj);
})
cardList.value = list
}
onMounted(() => {
getList()
})
</script>
<style lang="less" scoped>
.list-card {
background-color: @component-background;
&__list-title {
cursor: default;
height: 32px;
line-height: 17px;
padding-top: 16px;
color: @text-color-base;
font-size: 16px;
font-weight: 500;
}
&__card {
width: 100%;
margin-bottom: -8px;
.ant-card-body {
padding: 16px;
}
&-title {
margin-bottom: 5px;
color: @text-color-base;
font-size: 16px;
font-weight: 500;
.icon {
margin-top: -5px;
margin-right: 10px;
font-size: 38px !important;
}
}
&-detail {
padding-top: 10px;
padding-left: 30px;
color: @text-color-secondary;
font-size: 14px;
}
}
::v-deep .ant-list-item {
padding: 12px 2px;
}
}
</style>

View File

@ -0,0 +1,161 @@
<template>
<PageWrapper :class="prefixCls">
<div class="btn-box">
<a-button type="primary" :icon="h(SendOutlined)" @click="handleCreateFlow" class="ml-2">发起流程 </a-button>
<a-button type="primary" :icon="h(SaveOutlined)" @click="handleSaveDraft" class="ml-2">保存草稿 </a-button>
<a-button type="primary" :icon="h(CloseCircleOutlined)" @click="closePreview" class="ml-2" danger>关闭 </a-button>
</div>
<a-tabs v-model:activeKey="activeName">
<a-tab-pane key="form" tab="表单信息">
</a-tab-pane>
<a-tab-pane key="flow" tab="流程信息" force-render>
<div class="process-design" :style="'display: flex; height:' + designerData.height">
<process-viewer :key="`designer-${code}`" :xml="flowContent" />
<div class="form-box"
v-if="designerData.isCustmerTitle || (designerData.delegateUsers && designerData.delegateUsers.length >0)">
<a-form ref="formRef" :model="formData" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item ref="title" label="流程标题" name="title" v-if="designerData.isCustmerTitle">
<a-input v-model:value="formData.title" />
</a-form-item>
<a-form-item label="流程发起人" name="userId"
v-if="designerData.delegateUsers && designerData.delegateUsers.length >0">
<a-select v-model:value="formData.userId" placeholder="请选择" :options="delegateUsers"></a-select>
</a-form-item>
</a-form>
</div>
</div>
</a-tab-pane>
</a-tabs>
</PageWrapper>
</template>
<script lang="ts" setup>
import { h, ref, provide, reactive, onMounted, defineProps, computed, defineEmits, onBeforeMount } from 'vue';
import ProcessViewer from '@/components/ProcessViewer/index.vue';
import { PageWrapper } from '@/components/Page';
import { SendOutlined, SaveOutlined, CloseCircleOutlined, ZoomInOutlined, RotateLeftOutlined, RotateRightOutlined, ClearOutlined } from '@ant-design/icons-vue';
import { getDetail } from '@/api/sys/WFSchemeInfo'
import { create ,saveDraft} from '@/api/sys/WFProcess'
import { getLoadMyUserList } from '@/api/sys/WFDelegate'
import { useRoute } from 'vue-router'
import { useMultipleTabStore } from '@/store/modules/multipleTab';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/store/modules/user';
import { buildGUID } from '@/utils/uuid';
const userStore = useUserStore();
console.log()
const userInfo = userStore.getUserInfo
const prefixCls = 'preview-box'
const tabStore = useMultipleTabStore();
const router = useRouter();
const content = ref('')
const flowContent = ref('')
const route = useRoute()
const code = route.query.code
const designerOpen = ref(false)
const formRef = ref();
const labelCol = { span: 7 };
const wrapperCol = { span: 13 };
const designerData = reactive({
loading: false,
xmlString: '',
controlForm: {
prefix: 'flowable',
},
height: document.documentElement.clientHeight - 230.5 + "px;",
midVisible: false,
isCustmerTitle: false,
nodeUsers: [],
selectUsersVisible: false,
isDraft: false,
delegateUsers: []
})
const activeName = ref('flow')
const modelDesigner = ref < any > ()
const formData = reactive({
userId: '',
title: ''
})
const rules = reactive({
title: [
{ required: true, message: '请选择流程标题', trigger: 'blur' }
],
userId: [
{ required: true, message: '请选择流程发起人', trigger: 'blur' }
],
})
async function getDetailInfo() {
let data = await getDetail({ code: code })
flowContent.value = data.scheme.flowContent
formData.userId = userInfo.account
let content = JSON.parse(data.scheme.content)
let wfData = content.wfData
const currentNode = wfData.find(t => t.type == "bpmn:StartEvent")
console.log(currentNode)
designerData.isCustmerTitle = currentNode.isCustmerTitle
}
async function getDelegateUsers() {
const data = await getLoadMyUserList({
code: code
})
console.log(data)
designerData.delegateUsers = data
}
async function handleSaveDraft() {
}
async function handleCreateFlow() {
var querys = {
schemeCode: designerData.isDraft ? '' : code,
userId: formData.userId,
title: formData.title,
processId: buildGUID()
}
if (!designerData.isDraft) {
await saveDraft(querys)
querys.schemeCode = ''
designerData.isDraft = true
}
console.log(querys)
const data = await create(querys)
closePreview()
}
function closePreview() {
if (!code) {
tabStore.closeTabByKey('/dashboard/create_preview/add', router);
} else {
// /dashboard/create_preview/add?code=1
tabStore.closeTabByKey('/dashboard/create_preview/add?code=' + code, router);
}
}
onBeforeMount(() => {
getDetailInfo()
getDelegateUsers()
})
</script>
<style lang="less" scoped>
.preview-box {
background-color: @component-background;
.btn-box {
padding: 10px;
justify-content: flex-end;
display: flex;
}
}
.form-box {
width: 480px;
}
</style>

View File

@ -18,7 +18,7 @@
</BasicDrawer>
<a-modal width="90%" height="80%" v-model:open="postOpen" title="流程模板预览" @ok="postHandleOk">
<process-designer :key="designerOpen" style="border:1px solid rgba(0, 0, 0, 0.1);" ref="modelDesigner"
v-loading="designerData.loading" :schemeCode="schemeCode" :pageFlow="flowContent" :pageView="content" :pageType="'detail'"
v-loading="designerData.loading" :pageFlow="flowContent" :pageView="content" :pageType="'detail'"
@save="onSaveDesigner" />
</a-modal>
</div>