Compare commits

..

4 Commits

25 changed files with 1029 additions and 140 deletions

View File

@ -1,14 +1,16 @@
import { defHttp } from '@/utils/http/axios';
import { parseMinWidth } from 'element-plus/es/components/table/src/util';
import { DemoOptionsItem, selectParams, AccountListGetResultModel, AccountParams,NoOptionsParam } from './model/index';
enum Api {
FORMSTYPES_LIST = '/api/SysDataItemDetail/Load',
FORMS_LIST = '/api/FormScheme/LoadFormPage',
DATABASE_LIST = '/api/CodeTable/LoadCodeTablePage',
OUTKEY_LIST = '/api/CodeTable/GetForms',
GETBASE_LIST = '/api/FormScheme/GetForm',
ADDFORM_DATA = '/api/FormScheme/AddForm',
EDITFORM_DATA = '/api/FormScheme/UpdateForm?id='
FORMSTYPES_LIST = '/api/SysDataItemDetail/Load', //字典
FORMS_LIST = '/api/FormScheme/LoadFormPage', //查询表单列表
DATABASE_LIST = '/api/CodeTable/LoadCodeTablePage', //数据对象选择表
OUTKEY_LIST = '/api/CodeTable/GetForms', //获取数据表
GETBASE_LIST = '/api/FormScheme/GetForm', //获取列表详情
ADDFORM_DATA = '/api/FormScheme/AddForm', //新增表单设计
EDITFORM_DATA = '/api/FormScheme/UpdateForm?id=', //编辑表单设计
ADDSql_DATA = '/api/FormScheme/GetDataColName?dbCode=', // 添加编辑SQL
}
/**
@ -35,4 +37,8 @@ export const addFormDesignData = (params: NoOptionsParam) =>
export const editFormDesignData = (params: NoOptionsParam) =>
defHttp.post<AccountListGetResultModel>({ url: Api.EDITFORM_DATA+params.info.id, params });
export const addFormSqlData = (params: NoOptionsParam) =>
defHttp.post<AccountListGetResultModel>({ url: Api.ADDSql_DATA+params.dbCode+"&sql="+params.sql, params });

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

@ -135,18 +135,9 @@
console.log('adddata',data)
if(data.table){
data.table.forEach(item =>{
arr.push({
id: item.id,
name: item.name,
field: item.field,
comment: item.comment,
relationField: item.relationField,
relationName: item.relationName,
type: item.type
})
arr.push(item)
})
}
if(isAddVisible.value){ //
let schems
if(saveFormDatas.value.scheme.scheme){
@ -185,7 +176,6 @@
}
function submitClick(){
console.log('123333211',saveFormDatas)
isSubmitClick.value = true
@ -198,6 +188,7 @@
info: saveFormDatas.value.info,
scheme: saveFormDatas.value.scheme
}
console.log('param',param)
if(isAddVisible.value){
addFormDesignData( param ).then((res: AreaRespVO[]) =>{
console.log('addsuccess',res)

View File

@ -56,7 +56,7 @@ export const viewsColumns: BasicColumn[] = [
enable = false
}
const color = enable ? '#67c23a' : '#e6a23c';
const text = enable ? '主' : '子';
const text = enable ? '主' : '子';
return h(Tag, { color: color }, () => text);
},
},
@ -317,5 +317,18 @@ export const formSqlSchema: FormSchema[] = [
placeholder: '请输入',
},
},
{
field: 'sql',
component: 'InputTextArea',
label: 'SQL',
colProps: {
span: 24,
},
defaultValue: '',
required: true,
componentProps: {
placeholder: '@param 与主表关联参数值,比如 where id = @param;',
},
}
]

View File

@ -146,14 +146,14 @@
<BasicTable @register="registerViewsTable">
<template v-slot:bodyCell="{ column, record, index }">
<template v-if="column.key === 'relationFieldConnect'">
<div v-if="record.type == 'chlid'">
<div v-if="record.type !== 'main'">
<a-select
ref="select"
placeholder="请选择"
size="small"
v-model:value="record.field"
:options="record.outColumnKey"
:field-names="{ label: 'dbColumnName', value: 'dbColumnName' }"
:options="record.columns"
:field-names="{ label: 'label', value: 'value' }"
></a-select>
</div>
</template>
@ -185,14 +185,14 @@
<DataObject @register="registerModal" @backrows="handleBackRows" />
<sqlModal @register="registerSqlModal"></sqlModal>
<sqlModal @register="registerSqlModal" @backrows="handleSqlBackDatas"></sqlModal>
</template>
<script lang="ts" setup>
import { ref,defineProps,watch } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { getFormsTypeList,getOutKeyList } from '@/api/formdesign/index'
import { getOutKeyList } from '@/api/formdesign/index'
import { BasicForm, useForm } from '/@/components/Form';
import { formSchema , columns,connectColumns,viewsColumns } from './index.data';
@ -222,6 +222,7 @@
watch(()=>props.formScheme,(newValue,oldValue)=>{
if(newValue){
if(newValue.scheme.scheme){
console.log('newValue',newValue)
let schemObj = JSON.parse(newValue.scheme.scheme)
let obj = {
name: newValue.info.name,
@ -234,16 +235,22 @@
console.log('schemObj',schemObj)
setFieldsValue(obj)
setTimeout(()=>{
handleTableData(schemObj.db)
if(schemObj.rdb){
handleConnectTableData(schemObj.rdb)
if(newValue.info.formType == 1){
setTableDataViews(schemObj.db)
}else{
handleTableData(schemObj.db)
if(schemObj.rdb){
handleConnectTableData(schemObj.rdb)
}
}
},100)
}
}
},{immediate:true,deep:true})
watch(()=>props.isNextSteps,(newValue,oldValue)=>{
if(newValue){
validate()
let formArr = getFieldsValue()
if(!formArr.name || !formArr.category || !formArr.DbCode){
@ -252,7 +259,34 @@
}
if(formArr.formType==1){
emit('formDataNoBack');
let tableArr = getDataSourceViews()
let obj = {
form: formArr,
table: tableArr
}
let tabArr = []
if(tableArr && tableArr.length > 0){
if(tableArr.length == 1){
emit('formDataBack', obj);
}else{
tableArr.forEach(item =>{
if( item.type != 'main'){
tabArr.push(item.field)
}
})
}
}else{
emit('formDataNoBack');
message.error("请添加数据库表",2)
return
}
if(tabArr.includes(undefined)){
emit('formDataNoBack');
message.error("请完善数据库表信息",2)
}else{
emit('formDataBack', obj);
}
}else{
let tableArr = getDataSourceMain()
let connectTab = getDataSourceConnect()
@ -339,7 +373,7 @@
});
const [registerViewsTable, { setTableData:setTableDataViews, getDataSource:getDataSourceViews }] = useTable({
const [registerViewsTable, { reload:reloadViews,setTableData:setTableDataViews, getDataSource:getDataSourceViews }] = useTable({
title: '',
rowKey: 'id',
columns: viewsColumns,
@ -423,17 +457,6 @@
}
}
function handleAddSqlData() {
const anyformobj = ref<any>(myDataBaseFormRef.value.getFieldsValue());
if(anyformobj.value.DbCode){
openSqleModal(true, {});
}else{
message.warning("请选择数据库")
}
}
function handleDelete(record: Recordable) {
let arr = getDataSourceMain()
@ -635,9 +658,46 @@
setTableDataConnect(arr)
reloadConnect()
}
function handleAddSqlData() {
const anyformobj = ref<any>(myDataBaseFormRef.value.getFieldsValue());
let viesObj = getDataSourceViews() || []
if(anyformobj.value.DbCode){
openSqleModal(true, {DbCode:anyformobj.value.DbCode,length:viesObj.length});
}else{
message.warning("请选择数据库")
}
}
function handleSqlBackDatas(ModuleId = '') {
console.log('ModuleId',ModuleId)
let viesObj = getDataSourceViews() || []
console.log('viesObj',viesObj)
viesObj.push(ModuleId)
viesObj.forEach((item,index) =>{
item.index = index
if(index==0){
item.type = 'main'
}else{
item.type = 'chlid'
}
})
setTableDataViews(viesObj)
reloadViews()
}
function handleViewsDelete(record: Recordable) {
let arr = getDataSourceViews()
let newArr = []
arr.forEach(item =>{
if(item.index !== record.index){
newArr.push(item)
}
})
setTableDataViews(newArr)
reloadViews()
}
</script>
<style lang="less">
.l-rblock{

View File

@ -27,29 +27,76 @@
</template>
<script lang="ts" setup>
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form';
import { formSqlSchema } from './index.data';
import { ref,defineProps,watch } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form';
import { formSqlSchema } from './index.data';
import { message } from 'ant-design-vue';
import { addFormSqlData } from '@/api/formdesign/index'
const emit = defineEmits(['backrows']);
const [registerModal, { closeModal, setModalProps }] = useModalInner((data: any) => {
})
const [registerForm, { getFieldsValue, setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 100,
schemas: formSqlSchema,
showActionButtonGroup: false,
baseColProps: { lg: 24, md: 24 },
});
function modalSuReClick(){
const emit = defineEmits(['backrows']);
const receiveParam = ref()
const [registerModal, { closeModal, setModalProps }] = useModalInner((data: any) => {
console.log('dddd',data)
receiveParam.value = data
console.log('receiveParam',receiveParam)
setFieldsValue()
})
const [registerForm, { getFieldsValue, setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 100,
schemas: formSqlSchema,
showActionButtonGroup: false,
baseColProps: { lg: 24, md: 24 },
});
// emit('backrows', rows);
closeModal()
}
function modalSuReClick(){
let objData = getFieldsValue()
let param = {
dbCode: receiveParam.value.DbCode,
sql: objData.sql
}
validate()
if(objData.name && objData.sql){
addFormSqlData(param).then((res: AreaRespVO[]) => {
let arr = []
res.forEach(item =>{
arr.push({
label: item,
value: item
})
})
let backObj = {
name: objData.name,
sql: objData.sql,
columns:arr,
basetable: ''
}
if(receiveParam.value.length>0){
if(objData.sql.includes('where')){
backObj.basetable = objData.sql.split('from ')[1].split(' where')[1]
}else{
message.warning("请在语句中设置关联参数")
return
}
}else{
backObj.basetable = objData.sql.split('from ')[1]
}
emit('backrows', backObj)
closeModal()
})
}
// emit('backrows', rows);
// closeModal()
}
</script>

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>