diff --git a/src/api/bpm/model/index.ts b/src/api/bpm/model/index.ts index 2e1d4e64..88d3b541 100644 --- a/src/api/bpm/model/index.ts +++ b/src/api/bpm/model/index.ts @@ -29,7 +29,7 @@ export const getModelPage = async (params) => { return await request.get({ url: '/bpm/model/page', params }) } -export const getModel = async (id: number) => { +export const getModel = async (id: string) => { return await request.get({ url: '/bpm/model/get?id=' + id }) } diff --git a/src/components/SimpleProcessDesignerV2/src/NodeHandler.vue b/src/components/SimpleProcessDesignerV2/src/NodeHandler.vue new file mode 100644 index 00000000..7ecbacb5 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/NodeHandler.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue b/src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue new file mode 100644 index 00000000..855ca63e --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue b/src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue new file mode 100644 index 00000000..85cf3e66 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue @@ -0,0 +1,140 @@ + + + diff --git a/src/components/SimpleProcessDesignerV2/src/consts.ts b/src/components/SimpleProcessDesignerV2/src/consts.ts new file mode 100644 index 00000000..72450b72 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/consts.ts @@ -0,0 +1,72 @@ +// @ts-ignore +import { DictDataVO } from '@/api/system/dict/types' + +export enum NodeType { + /** + * 发起人节点 + */ + START_EVENT_NODE = 0, + /** + * 结束节点 + */ + END_EVENT_NODE = -2, + + /** + * 审批人节点 + */ + USER_TASK_NODE = 1, + + /** + * 条件节点 + */ + CONDITION_NODE = 3, + /** + * 条件分支节点 + */ + EXCLUSIVE_NODE = 4, + /** + * 并行分支分叉节点 + */ + PARALLEL_NODE_FORK = 5, + /** + * 并行分支聚合 + */ + PARALLEL_NODE_JOIN = 6, + /** + * 包容分支分叉节点 + */ + INCLUSIVE_NODE_FORK = 7, + /** + * 包容分支聚合节点 + */ + INCLUSIVE_NODE_JOIN = 8 +} + +export type SimpleFlowNode = { + id: string, + type: NodeType, + name: string, + showText?: string, + attributes?: any, + // 孩子节点 + childNode?: SimpleFlowNode, + // 条件节点 + conditionNodes?: SimpleFlowNode[] +} + +export const NODE_DEFAULT_TEXT = new Map() +NODE_DEFAULT_TEXT.set(NodeType.USER_TASK_NODE, '请配置该节点') +NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件') + +export const NODE_DEFAULT_NAME = new Map() +NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人') +NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件') + + +export const APPROVE_METHODS: DictDataVO [] = [ + { label: '单人审批', value: 1 }, + { label: '多人会签(需所有审批人同意)', value: 2 }, + { label: '多人或签(一名审批人同意即可)', value: 3 }, + { label: '依次审批(按顺序依次审批)', value: 4 } + // TODO 更多的类型 +] \ No newline at end of file diff --git a/src/components/SimpleProcessDesignerV2/src/index.ts b/src/components/SimpleProcessDesignerV2/src/index.ts new file mode 100644 index 00000000..a53dcf38 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/index.ts @@ -0,0 +1,4 @@ +import SimpleProcessDesigner from './SimpleProcessDesigner.vue' +import '../theme/simple-process-designer.scss' + +export { SimpleProcessDesigner } \ No newline at end of file diff --git a/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue b/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue new file mode 100644 index 00000000..3278e7b8 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/SimpleProcessDesignerV2/src/nodes/StartEventNode.vue b/src/components/SimpleProcessDesignerV2/src/nodes/StartEventNode.vue new file mode 100644 index 00000000..7835c959 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/src/nodes/StartEventNode.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/components/SimpleProcessDesignerV2/theme/iconfont.ttf b/src/components/SimpleProcessDesignerV2/theme/iconfont.ttf new file mode 100644 index 00000000..c2dcd3f4 Binary files /dev/null and b/src/components/SimpleProcessDesignerV2/theme/iconfont.ttf differ diff --git a/src/components/SimpleProcessDesignerV2/theme/iconfont.woff b/src/components/SimpleProcessDesignerV2/theme/iconfont.woff new file mode 100644 index 00000000..e20cfa28 Binary files /dev/null and b/src/components/SimpleProcessDesignerV2/theme/iconfont.woff differ diff --git a/src/components/SimpleProcessDesignerV2/theme/iconfont.woff2 b/src/components/SimpleProcessDesignerV2/theme/iconfont.woff2 new file mode 100644 index 00000000..77bbd1b3 Binary files /dev/null and b/src/components/SimpleProcessDesignerV2/theme/iconfont.woff2 differ diff --git a/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss b/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss new file mode 100644 index 00000000..6d2b4130 --- /dev/null +++ b/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss @@ -0,0 +1,521 @@ +.simple-flow-canvas { + position: absolute; + inset: 0; + z-index: 1; + overflow: auto; + background-color: #fafafa; + user-select: none; + + .simple-flow-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .top-area-container { + position: sticky; + inset: 0; + display: flex; + width: 100%; + height: 42px; + z-index: 1; + // padding: 4px 0; + background-color: #fff; + justify-content: flex-end; + align-items: center; + + .top-actions { + display: flex; + margin: 4px; + margin-right: 8px; + align-items: center; + + .canvas-control { + font-size: 16px; + + .control-scale-group { + display: inline-flex; + align-items: center; + margin-right: 8px; + + .control-scale-button { + display: inline-flex; + width: 28px; + height: 28px; + padding: 2px; + text-align: center; + cursor: pointer; + justify-content: center; + align-items: center; + } + + .control-scale-label { + margin: 0 4px; + font-size: 14px; + } + } + } + } + } + + .scale-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 16px; + background-color: #fafafa; + transform-origin: 50% 0 0; + transform: scale(1); + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + + // 节点容器 定义节点宽度 + .node-container { + width: 200px; + } + // 节点 + .node-box { + position: relative; + display: flex; + min-height: 70px; + padding: 5px 10px 8px; + cursor: pointer; + background-color: #fff; + flex-direction: column; + border: 2px solid transparent; + // border-color: #0089ff; + border-radius: 8px; + // border-color: #0089ff; + box-shadow: 0 1px 4px 0 rgba(10, 30, 65, 0.16); + transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1); + + &:hover { + // border-color: #0089ff; + .node-toolbar { + opacity: 1; + } + } + + // 普通节点标题 + .node-title-container { + display: flex; + padding: 4px; + cursor: pointer; + border-radius: 4px 4px 0 0; + align-items: center; + + .node-title-icon { + display: flex; + align-items: center; + } + + .node-title { + margin-left: 4px; + font-size: 14px; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: #1f1f1f; + // vertical-align: middle; + } + } + + // 条件节点标题 + .branch-node-title-container { + display: flex; + padding: 4px; + cursor: pointer; + border-radius: 4px 4px 0 0; + align-items: center; + justify-content: space-between; + + .branch-title { + max-width: 120px; + font-size: 13px; + font-weight: 600; + color: #f60; + } + + .branch-priority { + min-width: 50px; + font-size: 13px; + } + } + + .node-content { + display: flex; + min-height: 32px; + padding: 4px 8px; + margin-top: 4px; + line-height: 32px; + justify-content: space-between; + align-items: center; + color: #111f2c; + background: rgba(0, 0, 0, 0.03); + border-radius: 4px; + + .node-text { + display: -webkit-box; + overflow: hidden; + font-size: 14px; + line-height: 24px; + text-overflow: ellipsis; + word-break: break-all; + -webkit-line-clamp: 2; /* 这将限制文本显示为两行 */ + -webkit-box-orient: vertical; + } + } + + .node-toolbar { + opacity: 0; + position: absolute; + top: -26px; + right: 10px; + display: flex; + + .toolbar-icon { + text-align: center; + vertical-align: middle; + color: #000; + border-radius: 4px; + } + } + } + + + .node-config-error { + border-color: #ff5219; + } + // 普通节点包装 + .node-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + // 节点连线处理 + .node-handler-wrapper { + position: relative; + display: flex; + height: 70px; + align-items: center; + user-select: none; + justify-content: center; + flex-direction: column; + + &::before { + position: absolute; + top: 0; + right: 0; + left: 0; + // bottom: 5px; + bottom: 0px; + z-index: 0; + width: 2px; + height: 100%; + // height: calc(100% - 5px); + margin: auto; + background-color: #dedede; + content: ''; + } + + .node-handler { + .add-icon { + position: relative; + top: -5px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + width: 25px; + height: 25px; + color: #fff; + background-color: #0089ff; + border-radius: 50%; + + &:hover{ + transform: scale(1.1); + + } + } + + } + + .node-handler-arrow { + position: absolute; + bottom: 0; + left: 50%; + display: flex; + transform: translateX(-50%); + } + } + + + // 条件节点包装 + .branch-node-wrapper { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 16px; + + .branch-node-container { + position: relative; + display: flex; + + &::before { + position: absolute; + height: 100%; + width: 4px; + background-color: #fafafa; + content: ""; + left: 50%; + transform: translate(-50%); + } + + .branch-node-add { + position: absolute; + top: -18px; + left: 50%; + z-index: 1; + height: 36px; + padding: 0 10px; + font-size: 12px; + line-height: 36px; + color: #222; + cursor: pointer; + background: #fff; + border: 2px solid #dedede; + border-radius: 18px; + transform: translateX(-50%); + transform-origin: center center; + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + } + + .branch-node-item { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + min-width: 280px; + padding: 40px 40px 0; + background: transparent; + border-top: 2px solid #dedede; + border-bottom: 2px solid #dedede; + + &::before { + position: absolute; + width: 2px; + height: 100%; + margin: auto; + inset: 0; + background-color: #dedede; + content: ''; + } + } + // 覆盖条件节点第一个节点左上角的线 + .branch-line-first-top { + position: absolute; + top: -5px; + left: -1px; + width: 50%; + height: 7px; + background-color: #fafafa; + content: ''; + } + // 覆盖条件节点第一个节点左下角的线 + .branch-line-first-bottom { + position: absolute; + bottom: -5px; + left: -1px; + width: 50%; + height: 7px; + background-color: #fafafa; + content: ''; + } + // 覆盖条件节点最后一个节点右上角的线 + .branch-line-last-top { + position: absolute; + top: -5px; + right: -1px; + width: 50%; + height: 7px; + background-color: #fafafa; + content: ''; + } + // 覆盖条件节点最后一个节点右下角的线 + .branch-line-last-bottom { + position: absolute; + right: -1px; + bottom: -5px; + width: 50%; + height: 7px; + background-color: #fafafa; + content: ''; + } + } + } + + .node-fixed-name { + display: inline-block; + width: auto; + padding: 0 4px; + overflow: hidden; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + } + // 开始节点包装 + .start-node-wrapper { + position: relative; + margin-top: 16px; + + .start-node-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .start-node-box { + display: flex; + justify-content: center; + align-items: center; + width: 90px; + height: 36px; + padding: 3px 4px; + color: #212121; + cursor: pointer; + // background: #2c2c2c; + background: #fafafa; + border-radius: 30px; + box-shadow: 0 1px 5px 0 rgba(10, 30, 65, 0.08); + box-sizing: border-box; + } + } + } + + // 结束节点包装 + .end-node-wrapper { + margin-bottom: 16px; + + .end-node-box { + display: flex; + justify-content: center; + align-items: center; + width: 80px; + height: 36px; + color: #212121; + // background: #6e6e6e; + background: #fafafa; + border-radius: 30px; + box-shadow: 0 1px 5px 0 rgba(10, 30, 65, 0.08); + box-sizing: border-box; + } + } + + // 可编辑的 title 输入框 + .editable-title-input { + height: 20px; + line-height: 20px; + font-size: 12px; + margin-left: 4px; + border: 1px solid #d9d9d9; + border-radius: 4px; + transition: all 0.3s; + + &:focus { + border-color: #40a9ff; + outline: 0; + box-shadow: 0 0 0 2px rgba(24, 144, 255, .2) + } + } + } + } + + +} + +// 节点连线气泡卡片样式 +.handler-item-wrapper { + display: flex; + cursor: pointer; + + .handler-item { + margin-right: 8px; + } + + .handler-item-icon { + width: 80px; + height: 80px; + background: #fff; + border: 1px solid #e2e2e2; + border-radius: 50%; + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + user-select: none; + text-align: center; + + &:hover { + background: #3296fa; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); + } + + .icon-size { + font-size: 35px; + line-height: 80px; + } + } + + .approve { + color: #ff943e; + } + + .handler-item-text { + margin-top: 4px; + width: 80px; + text-align: center; + } + +} + +// iconfont 样式 +@font-face { + font-family: "iconfont"; /* Project id 4495938 */ + src: url('iconfont.woff2?t=1712392083512') format('woff2'), + url('iconfont.woff?t=1712392083512') format('woff'), + url('iconfont.ttf?t=1712392083512') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-Inclusive:before { + content: "\e602"; +} + +.icon-copy:before { + content: "\e7eb"; +} + +.icon-handle:before { + content: "\e61c"; +} + +.icon-exclusive:before { + content: "\e717"; +} + +.icon-approve:before { + content: "\e715"; +} + +.icon-parallel:before { + content: "\e688"; +} \ No newline at end of file diff --git a/src/views/bpm/simpleWorkflow/index.vue b/src/views/bpm/simpleWorkflow/index.vue index 7ac48369..619b9c4a 100644 --- a/src/views/bpm/simpleWorkflow/index.vue +++ b/src/views/bpm/simpleWorkflow/index.vue @@ -1,169 +1,30 @@ - - diff --git a/src/views/bpm/simpleWorkflow/index1.vue b/src/views/bpm/simpleWorkflow/index1.vue new file mode 100644 index 00000000..6ee5b4e3 --- /dev/null +++ b/src/views/bpm/simpleWorkflow/index1.vue @@ -0,0 +1,169 @@ + + +