This commit is contained in:
那些花儿
2025-07-28 10:48:38 +08:00
parent 030cf7cc4c
commit 3b51ed7a51
450 changed files with 82310 additions and 1213 deletions
@@ -0,0 +1,254 @@
const assigneeForm = {
template: /*language=HTML*/`
<el-form-item label="参与者">
<div>
<!-- 模式选择 -->
<div style="margin-bottom: 10px;">
<el-radio-group v-model="assigneeMode" @change="handleModeChange" size="small">
<el-radio-button label="manual" :disabled="isFirstTaskNode">手动输入</el-radio-button>
<el-radio-button label="select" :disabled="isFirstTaskNode">选择输入</el-radio-button>
<el-radio-button label="initiator">流程发起人</el-radio-button>
</el-radio-group>
</div>
<!-- 手动输入模式 -->
<el-input
v-if="assigneeMode === 'manual'"
v-model="manualInput"
placeholder="请输入参与者,多个参与者用逗号分隔"
@change="updateModelValue">
</el-input>
<!-- 选择输入模式 -->
<el-input
v-if="assigneeMode === 'select'"
v-model="assigneeText"
readonly
style="width: 100%"
placeholder="请选择参与者">
<el-button
slot="append"
type="primary"
style="color: #fff;background-color: var(--color-primary);border-color: var(--color-primary)"
icon="el-icon-setting"
@click="openUserDialog">
选择
</el-button>
</el-input>
<!-- 流程发起人模式 -->
<el-input
v-if="assigneeMode === 'initiator'"
value="initiator"
readonly
disabled
style="width: 100%"
placeholder="流程发起人">
</el-input>
</div>
<!-- 用户选择弹窗 -->
<el-dialog title="选择参与者" :visible.sync="dialogVisible" width="80%" append-to-body>
<div style="display: flex;">
<!-- 左侧表格 -->
<div style="flex: 3; margin-right: 10px; overflow: auto;">
<div style="margin-bottom: 10px;">
<el-input v-model="pageForm.searchKeyword" placeholder="请输入关键字搜索" style="width: 200px; margin-right: 10px;"></el-input>
<el-button type="primary" @click="searchUsers">搜索</el-button>
</div>
<el-table
ref="userTableRef"
:data="userList"
border
row-key="id"
>
<el-table-column type="selection" width="55" :reserve-selection="true"></el-table-column>
<el-table-column prop="loginname" label="工号" width="100"></el-table-column>
<el-table-column prop="username" label="姓名" width="120"></el-table-column>
<el-table-column prop="unitName" label="单位"></el-table-column>
</el-table>
<div style="margin-top: 10px; text-align: right;">
<el-pagination
@current-change="handleCurrentChange"
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
layout="total, prev, pager, next, jumper"
:total="pageForm.totalCount">
</el-pagination>
</div>
</div>
<!-- 右侧已选列表 -->
<div style="flex: 1; border: 1px solid #EBEEF5; border-radius: 4px; padding: 10px; overflow: auto;" v-if="$refs.userTableRef">
<div style="font-weight: bold; margin-bottom: 10px;">已选择的参与者</div>
<el-tag
v-for="user in $refs.userTableRef.selection"
:key="user.id"
style="margin: 0 5px 5px 0;">
{{ user.username }}
</el-tag>
<div v-if="$refs.userTableRef.selection.length === 0" style="color: #909399; font-size: 14px;">
暂无选择的参与者
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="confirmSelection">确 定</el-button>
</span>
</el-dialog>
</el-form-item>
`,
props: {
model: {
type: Object,
required: true
},
field: {
type: String,
required: true
},
start_node_next_node_ids: {
type: Array,
default: () => []
}
},
computed: {
/**
* 判断当前节点是否为第一个任务节点
* @returns {boolean}
*/
isFirstTaskNode() {
return this.start_node_next_node_ids.includes(this.model.name)
}
},
data() {
return {
assigneeMode: "manual",
manualInput: "",
assigneeText: "",
dialogVisible: false,
userList: [],
selectedUsers: [],
pageForm: {
pageNumber: 1,
pageSize: 10,
searchKeyword: "",
totalCount: 0
}
}
},
created() {
// 回显
this.initFromModel()
},
methods: {
initFromModel() {
const value = this.model[this.field]
this.assigneeText = this.model["assigneeText"] || ""
this.assigneeMode = this.model["assigneeMode"] || "manual"
if(this.assigneeMode === "initiator"){
this.assignee = "initiator"
this.assigneeText = "流程发起人"
}
if(this.assigneeMode === "manual"){
this.manualInput = value || ""
}
},
handleModeChange() {
this.assigneeText = ""
this.manualInput = ""
this.assignee = ""
// 如果切换到流程发起人模式,直接设置值
if (this.assigneeMode === "initiator") {
this.assignee = "{$initiator}"
this.assigneeText = "流程发起人"
}
this.updateModelValue()
},
openUserDialog() {
this.dialogVisible = true
this.pageForm.pageNumber = 1
this.pageForm.searchKeyword = ""
this.loadSelectUsers()
},
loadSelectUsers() {
let userIds = this.model[this.field].split(",")
$.get("/flow/design/assigneeEcho", { userIds: JSON.stringify(userIds) }).then(res => {
if (res.code === 0) {
this.selectedUsers = res.data
this.loadUsers()
}
})
},
loadUsers() {
$.get("/flow/design/assigneePage", {
...this.pageForm
}).then(res => {
if (res.code === 0) {
this.userList = res.data.list
this.pageForm.totalCount = res.data.totalCount
// 设置已选中的行
this.$nextTick(() => {
this.setTableSelection()
})
}
})
},
// 设置表格选中数据
setTableSelection() {
// 如果当前表格中有该用户,选中TA
this.userList.forEach(row => {
const exist = this.selectedUsers.map(item => item.id).includes(row.id)
if (exist) {
this.$refs.userTableRef.toggleRowSelection(row, true)
}
})
},
confirmSelection() {
const selection = this.$refs.userTableRef.selection
this.assignee = selection.map(item => item.id).join(",")
this.assigneeText = selection.map(item => item.username).join(",")
this.updateModelValue()
this.dialogVisible = false
},
updateModelValue() {
// 更新model中的值
if (this.assigneeMode === "manual") {
this.assignee = this.manualInput
this.assigneeText = this.manualInput
} else if (this.assigneeMode === "initiator") {
this.assignee = "initiator"
this.assigneeText = "流程发起人"
}
// 选择模式的处理在confirmSelection中
this.$set(this.model, this.field, this.assignee)
this.$set(this.model, "assigneeText", this.assigneeText)
this.$set(this.model, "assigneeMode", this.assigneeMode)
},
searchUsers() {
this.pageForm.pageNumber = 1
this.loadUsers()
},
handleSizeChange(val) {
this.pageForm.pageSize = val
this.loadUsers()
},
handleCurrentChange(val) {
this.pageForm.pageNumber = val
this.loadUsers()
}
}
}
@@ -0,0 +1,149 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>流程设计器</title>
<script src="/assets/platform/plugins/vue/vue.js"></script>
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
<script src="/assets/platform/plugins/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://cdn.staticfile.net/element-ui/2.15.14/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/override.css" />
<link rel="stylesheet" href="${base!}/assets/platform/css/root.css" />
<!-- 引入 core 包和对应 css-->
<script src="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/logic-flow.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/style/index.css" />
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
<style>
#app {
height: 100vh;
}
</style>
</head>
<body>
<div id="app">
<snaker-flow-designer ref="designer"
v-model="flowData"
@on-save="handleSave"
:show-doc="false"
node-render-type="html"
:extend-property-keys="[
'candidateUsers',
'candidateGroups',
'candidateHandler',
'assigneeText',
'assigneeMode'
]"
>
<!-- <template v-slot:form-item-task-form="{model,field}">-->
<!-- <el-form-item label="form">-->
<!-- -->
<!-- </el-form-item>-->
<!-- </template>-->
<template v-slot:form-item-task-assignee="{model,field}">
<assignee-form :model="model" :field="field"
:start_node_next_node_ids="startNodeNextNodeIds"></assignee-form>
</template>
<template v-slot:form-item-task-assignment-handler="{model,field}">
<el-form-item label="参与者处理类">
<el-select v-model="model[field]" style="width: 100%" clearable
:disabled="startNodeNextNodeIds.includes(model.name)">
<el-option v-for="item in assignmentHandlerClassOptions" :key="item.value"
:value="item.value"
:label="item.name">
</el-option>
</el-select>
</el-form-item>
</template>
</snaker-flow-designer>
</div>
</body>
<script>
<!--#include('assigneeForm.js'){}#-->
Vue.use(SnakerflowDesigner.default)
const vue = new Vue({
el: "#app",
components: {
"assignee-form": assigneeForm
},
data() {
return {
id: "${id!}",
designerData: {},
flowData: {},
assignmentHandlerClassOptions: []
}
},
computed: {
startNodeNextNodeIds() {
if (this.$refs.designer && this.$refs.designer.lf) {
const graphData = this.$refs.designer.lf.getGraphData()
if (graphData) {
const { nodes, edges } = graphData
const startNode = nodes.find(v => v.type === "snaker:start")
if (startNode) {
const targetNodeIds = edges.filter(v => v.sourceNodeId === startNode.id).map(v => v.targetNodeId)
return targetNodeIds
}
}
}
return []
}
},
methods: {
handleSave(val) {
const design = {
...this.designerData,
content: val.json
}
$.post("/flow/design/updateContent", { design: JSON.stringify(design) }).then(res => {
if (res.code === 0) {
window.parent.postMessage("success")
} else {
window.parent.postMessage("error")
}
})
},
getDetail() {
$.get("/flow/design/detail", { id: this.id }).then((res) => {
if (res.code === 0) {
this.designerData = res.data
if (res.data.content) {
this.flowData = res.data.content
} else {
this.flowData = {}
}
}
})
},
init() {
$.get("/flow/design/assigmentHandlerClass").then((res) => {
if (res.code === 0) {
this.assignmentHandlerClassOptions = res.data
}
})
},
getData() {
console.log(this.$refs.designer)
}
},
created() {
this.init()
},
mounted() {
if (this.id) {
this.getDetail()
}
}
})
</script>
</html>
@@ -0,0 +1,214 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.design-dialog .el-dialog__body {
padding: 0;
}
</style>
<div id="app" v-cloak>
<guava>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="名称">
<el-input v-model="pageForm.displayName" placeholder="名称" clearable></el-input>
</search-item>
<search-item label="编码">
<el-input v-model="pageForm.name" placeholder="编码" clearable></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool>
<el-button type="primary" @click="onAdd" size="small" icon="el-icon-plus">新增</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="displayName" label="流程定义名称"></el-table-column>
<el-table-column prop="name" label="流程定义编码"></el-table-column>
<el-table-column prop="type" label="流程分类"></el-table-column>
<el-table-column prop="isDeployed" label="是否部署">
<template slot-scope="{row}">
<el-tag size="mini" v-if="row.isDeployed===1" type="success"></el-tag>
<el-tag size="mini" v-else-if="row.isDeployed===0" type="info"></el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="450px">
<template slot-scope="{row}">
<el-button size="mini" type="primary" @click="onDesign(row)">设计</el-button>
</el-button>
<el-button size="mini" type="primary" @click="onEdit(row)">编辑</el-button>
<!-- v-if="row.isDeployed===0"-->
<el-button type="primary" size="mini" @click="onDeploy(row)">部署
</el-button>
<el-button type="danger" size="mini" @click="onDelete(row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" width="800px">
<el-form :model="formData" ref="formRef" label-width="120px">
<el-form-item label="流程定义名称" prop="displayName"
:rules="{ required: true, message: '请输入流程定义名称', trigger: 'blur' }">
<el-input v-model="formData.displayName" placeholder="请输入流程定义名称"></el-input>
</el-form-item>
<el-form-item label="流程定义编码" prop="name"
:rules="{ required: true, message: '请输入流程定义编码', trigger: 'blur' }">
<el-input v-model="formData.name" placeholder="请输入流程定义编码"></el-input>
</el-form-item>
<el-form-item label="流程分类" prop="category"
:rules="{ required: false, message: '请选择流程分类', trigger: 'change' }"
style="width: 100%">
<el-select v-model="formData.category" placeholder="请选择流程分类" clearable>
<el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电脑端发起地址" prop="instanceUrl">
<el-input v-model="formData.instanceUrl" placeholder="请输入电脑端发起地址"></el-input>
</el-form-item>
<el-form-item label="手机端发起地址" prop="h5InstanceUrl">
<el-input v-model="formData.h5InstanceUrl" placeholder="请输入手机端发起地址"></el-input>
</el-form-item>
<el-form-item label="图标" prop="icon">
<el-input v-model="formData.icon" placeholder="请输入图标"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</template>
</el-dialog>
<el-dialog title="流程设计" :visible.sync="designVisible" fullscreen class="design-dialog">
<div style="height: calc(100vh - 55px); width: 100%">
<iframe v-if="designVisible" :src="designUrl" frameborder="0" height="100%"
style="height: 100%; width: inherit"></iframe>
</div>
</el-dialog>
</div>
<script>
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
dialogVisible: false,
categoryOptions: [],
designVisible: false,
designUrl: null,
formData: {}
}
},
methods: {
onAdd() {
this.formData = {}
this.dialogVisible = true
},
onEdit(row) {
this.dialogVisible = true
this.formData = { ...row }
},
onSubmit() {
$.post("/flow/design/" + (this.formData.id ? "update" : "insert"), { design: JSON.stringify(this.formData) }).then(res => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
this.dialogVisible = false
}
})
},
onDesign(row) {
this.designVisible = true
this.iframeLoaded()
this.designUrl = "/flow/design/designer?id=" + row.id
},
iframeLoaded() {
// iframe监听组件内设计器保存事件
window.onmessage = (event) => {
console.log(event)
if (event.data === "success") {
this.$message.success("保存成功")
this.designVisible = false
this.pageData()
} else if (event.data === "error") {
this.$message.error("保存失败")
}
}
},
onDeploy(row) {
this.$confirm("部署会生成新的流程定义版本,确定部署吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/design/deploy", { id: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
},
onRedeploy(row) {
this.$confirm("重新部署会覆盖最新版流程定义,确定重新部署吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/design/redeploy", { id: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
}
})
})
},
onDelete(row) {
this.$confirm("您确定要删除吗", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/design/delete", { id: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
},
onView(row) {
},
listCategory() {
// this.$axios.get("/platform/warmFlow/category/list").then((res) => {
// if (res.code === 0) {
// this.categoryOptions = res.data
// }
// })
}
},
created() {
this.pageData()
this.listCategory()
}
})
</script>
<!--#
}
#-->