feat: 新增页面布局

This commit is contained in:
wanglihui 2023-07-30 23:26:07 +08:00
parent 69bc60f7aa
commit 2ab7a579ce
40 changed files with 326 additions and 1903 deletions

View File

@ -23,6 +23,7 @@
"sortablejs": "^1.15.0",
"vue": "^3.3.4",
"vue-color-kit": "^1.0.5",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.2.2",
"vue-router": "^4.2.0"
},

218
pnpm-lock.yaml generated
View File

@ -41,6 +41,9 @@ dependencies:
vue-color-kit:
specifier: ^1.0.5
version: 1.0.5(vue@3.3.4)
vue-grid-layout:
specifier: ^3.0.0-beta1
version: 3.0.0-beta1(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
vue-i18n:
specifier: ^9.2.2
version: 9.2.2(vue@3.3.4)
@ -1048,6 +1051,192 @@ packages:
vue: 3.3.4
dev: false
/@interactjs/actions@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-BHJcW84WCMf/LsKmha/1Yog7aH3+QBXbLvowvZvwYvgjdUIb3xCa1a7FUYXuWAeKNMyKPVjFun+WPce75B+1tA==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/arrange@1.10.2:
resolution: {integrity: sha512-pPLA9o4RWMFN0VfalklOFSRLL4WqqXcD9no4XEuqV00goZPCxLBbMTztaWwnutlRy7evtOhUjUH+pZVsS+dZ4Q==}
dev: false
/@interactjs/auto-scroll@1.10.2(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-yYqzOawwvWd1NNnlqZdzrXoOMFafQ2/ws85erpJqdaNMQE221z2uP+QYhFRLQRgYUlTbHFfmjDpzhuJgq4uA8Q==}
peerDependencies:
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/auto-start@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-nZudj8VzJzz+uEyDHqXwtKpvUYr+Oj1+xBrJEu21CywroHQWM2J4fCIiCgeCo3d5/p/TrzFk5b+YfAWePKiLxA==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/clone@1.10.2:
resolution: {integrity: sha512-XzA8BRHSCwvysOegZ1kopg+IJF3erh4qzY6DRoZsIJovKAXawoa176E58IAzDbgYPJ2yoaSGT+XyzT2C0wa3pQ==}
dev: false
/@interactjs/core@1.10.2(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-SA5KRGo+gFJOhBj1Z2dLHhAf0/2nyHNd4SQ460aIQ3jj/QhqbJW6kGzmh7hBa2FzVGgxLhcQu7NZaP4rnDfUNw==}
peerDependencies:
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/utils': 1.10.2
dev: false
/@interactjs/dev-tools@1.10.2:
resolution: {integrity: sha512-aAd9NgTAGA3yVdFCYcAAYrM4TYQFuVqEvsF+xj+g5SlGyrJ7+GTjPZ2rScOyAsABY4Tz64L2pXvWmXMG87dncA==}
dependencies:
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/feedback@1.10.2:
resolution: {integrity: sha512-XlcoICGrFeUwwRtDgOpstOOvlU42WZoEg7gJHK3LwF7j0IctPd1+3blXofFlBeVvodle8MvUMepm5CRXz741fA==}
dev: false
/@interactjs/inertia@1.10.2(@interactjs/core@1.10.2)(@interactjs/modifiers@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-ZmN1joN6J36Q6SOp3V0iZOisXZOBMSAUj0STo8wbwCKy7K8IrC9vjUBbO2JM52cT6o7hg5ebHsp5c8FrebSHlg==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/modifiers': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/offset': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/interact@1.10.2:
resolution: {integrity: sha512-Ms5uVCY9IobVYpQyBnBdkP6Bk6iDY7TkC7GupsdUPUxzAvYSQCTEAGr/1PwxSrSS6dN/8O8TuyUWPbCaylr/JA==}
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/types': 1.10.2
'@interactjs/utils': 1.10.2
dev: false
/@interactjs/interactjs@1.10.2:
resolution: {integrity: sha512-OwLl70af6lfZOOg/bvWKSNm1DS1nDI72QnzDYljSKfc2D8stqLIGDO1wPY2rhZudUG5q3t50EhmMUQF76yll/g==}
dependencies:
'@interactjs/actions': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/arrange': 1.10.2
'@interactjs/auto-scroll': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/clone': 1.10.2
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/dev-tools': 1.10.2
'@interactjs/feedback': 1.10.2
'@interactjs/inertia': 1.10.2(@interactjs/core@1.10.2)(@interactjs/modifiers@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/interact': 1.10.2
'@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/multi-target': 1.10.2
'@interactjs/offset': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/pointer-events': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/react': 1.10.2
'@interactjs/reflow': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
'@interactjs/vue': 1.10.2
dev: false
/@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-3wYEucvZF2NTIslnVIKw5MWhkn9LM42cGCQaC19o3LZeWnbps7NnHJCyQp6zylJrCbwt7f+CSt4Oj2/s0f6XEA==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/snappers': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/multi-target@1.10.2:
resolution: {integrity: sha512-O2GiIqgZBzjAVTOpL8doTnAcM9AtM3+H/Bb+An12wWKtNutVK7JbqUAO2nYueOk55/PP3yDLY9Qdr15RJns3lQ==}
dev: false
/@interactjs/offset@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-xLgQqinFUY7ZqSX9d9on7XRcxvQdHNEAktj2QFwxMsEwrA6zbKROpPVwt8WQ1yBAeJSFjgYGcmCMPW5K41dT0w==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/pointer-events@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-O8s3N399hkGIzWGlcJVy0LJyDn5YWDh6XKjyowh/QivtlZSWPY8eglmlN2uZX0lmiqUYghbKI4CpQYP/cE0ZDA==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/react@1.10.2:
resolution: {integrity: sha512-JXzPdANft+W2vq3SCSzprCwom5UuC8TaiAAhVdt8R+/P6xHbOeAX93XLS5YmDwT8e0Zh9J9jYvz55tkTdwjFZQ==}
dev: false
/@interactjs/reflow@1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-pc6o6RRhSCYQC4auZexRb7z5FQkdSVev5HzlRfUAjfw4C076qgbcs63ESRKy4YXdSBtUTvARQZxpuWUNGquzJw==}
peerDependencies:
'@interactjs/core': 1.10.2
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
'@interactjs/utils': 1.10.2
optionalDependencies:
'@interactjs/interact': 1.10.2
dev: false
/@interactjs/snappers@1.10.2(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-wQ41Vn5GRn6VavjIEUtTkd9d5QgdKgC4+CPW0fjKkiQclLBmaic7VibNETO8twN0Jx5e73EoPj9K2nAVHIA0hA==}
peerDependencies:
'@interactjs/utils': 1.10.2
dependencies:
'@interactjs/utils': 1.10.2
dev: false
/@interactjs/types@1.10.2:
resolution: {integrity: sha512-l0T1bU8OHRv716ztQOYwP+K7b/lA76C0T3r/cdabbUv6CKeAFdFRRFlmNxYOM36SxMGWAiq5VWrN3SeXlB7Fow==}
requiresBuild: true
dev: false
/@interactjs/utils@1.10.2:
resolution: {integrity: sha512-sOr+pu7XGAN4qv+ikajMo3RJygbkbMLegmx0Tv5Qf6e80sF42FjkmHeMGuV7fL98nwat0VmDiWerOFBnKctXow==}
dev: false
/@interactjs/vue@1.10.2:
resolution: {integrity: sha512-msLdc42DFsCPQZt6YBGZrw8Ro23kQcNnj+iLz2OUQcOrp/lma7WjorUuAwwfyFna2DevLtiYlMLbT0dpO/cVhg==}
dev: false
/@intlify/core-base@9.2.2:
resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
engines: {node: '>= 14'}
@ -1966,6 +2155,10 @@ packages:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
/batch-processor@1.0.0:
resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==}
dev: false
/big-integer@1.6.51:
resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
engines: {node: '>=0.6'}
@ -2442,6 +2635,12 @@ packages:
- '@vue/composition-api'
dev: false
/element-resize-detector@1.2.4:
resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==}
dependencies:
batch-processor: 1.0.0
dev: false
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
@ -3812,6 +4011,10 @@ packages:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
/mitt@2.1.0:
resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==}
dev: false
/mitt@3.0.0:
resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==}
dev: false
@ -5282,6 +5485,21 @@ packages:
vue-demi: 0.13.11(vue@3.3.4)
dev: true
/vue-grid-layout@3.0.0-beta1(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2):
resolution: {integrity: sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==}
dependencies:
'@interactjs/actions': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
'@interactjs/dev-tools': 1.10.2
'@interactjs/interactjs': 1.10.2
'@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2)(@interactjs/utils@1.10.2)
element-resize-detector: 1.2.4
mitt: 2.1.0
transitivePeerDependencies:
- '@interactjs/core'
- '@interactjs/utils'
dev: false
/vue-i18n@9.2.2(vue@3.3.4):
resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
engines: {node: '>= 14'}

View File

@ -0,0 +1,15 @@
<template>
<div class="bd-page">
<slot />
</div>
</template>
<script lang="ts" name="BdPage" setup></script>
<style lang="scss" scoped>
.bd-page {
width: 100%;
height: 100%;
padding: 12px;
box-sizing: border-box;
}
</style>

View File

@ -1,37 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue';
defineProps<{ msg: string }>();
const count = ref(0);
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
<el-button>我是 ElButton</el-button>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -1,462 +0,0 @@
<template>
<div class="sc-workflow-design">
<div class="box-scale">
<!-- <node-wrap v-if="nodeConfig" v-model="nodeConfig"></node-wrap> -->
<div class="end-node">
<div class="end-node-circle"></div>
<div class="end-node-text">流程结束</div>
</div>
</div>
<!-- <use-select v-if="selectVisible" ref="useselect" @closed="selectVisible = false"></use-select> -->
</div>
</template>
<script lang="ts" setup>
</script>
<style lang="scss">
.sc-workflow-design {
width: 100%;
}
.sc-workflow-design .box-scale {
display: inline-block;
position: relative;
width: 100%;
padding: 54.5px 0px;
align-items: flex-start;
justify-content: center;
flex-wrap: wrap;
min-width: min-content;
}
.sc-workflow-design {
.node-wrap {
display: inline-flex;
width: 100%;
flex-flow: column wrap;
justify-content: flex-start;
align-items: center;
padding: 0px 50px;
position: relative;
z-index: 1;
}
.node-wrap-box {
display: inline-flex;
flex-direction: column;
position: relative;
width: 220px;
min-height: 72px;
flex-shrink: 0;
background: rgb(255, 255, 255);
border-radius: 4px;
cursor: pointer;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.1);
}
.node-wrap-box::before {
content: '';
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
width: 0px;
border-style: solid;
border-width: 8px 6px 4px;
border-color: rgb(202, 202, 202) transparent transparent;
background: #f6f8f9;
}
.node-wrap-box.start-node:before {
content: none;
}
.node-wrap-box .title {
height: 24px;
line-height: 24px;
color: #fff;
padding-left: 16px;
padding-right: 30px;
border-radius: 4px 4px 0 0;
position: relative;
display: flex;
align-items: center;
}
.node-wrap-box .title .icon {
margin-right: 5px;
}
.node-wrap-box .title .close {
font-size: 15px;
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 10px;
display: none;
}
.node-wrap-box .content {
position: relative;
padding: 15px;
}
.node-wrap-box .content .placeholder {
color: #999;
}
.node-wrap-box:hover .close {
display: block;
}
.add-node-btn-box {
width: 240px;
display: inline-flex;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.add-node-btn-box:before {
content: '';
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: -1;
margin: auto;
width: 2px;
height: 100%;
background-color: rgb(202, 202, 202);
}
.add-node-btn {
user-select: none;
width: 240px;
padding: 20px 0px 32px;
display: flex;
justify-content: center;
flex-shrink: 0;
flex-grow: 1;
}
.add-node-btn span {
}
.add-branch {
justify-content: center;
padding: 0px 10px;
position: absolute;
top: -16px;
left: 50%;
transform: translateX(-50%);
transform-origin: center center;
z-index: 1;
display: inline-flex;
align-items: center;
}
.branch-wrap {
display: inline-flex;
width: 100%;
}
.branch-box-wrap {
display: flex;
flex-flow: column wrap;
align-items: center;
min-height: 270px;
width: 100%;
flex-shrink: 0;
}
.col-box {
display: inline-flex;
flex-direction: column;
align-items: center;
position: relative;
background: #f6f8f9;
}
.branch-box {
display: flex;
overflow: visible;
min-height: 180px;
height: auto;
border-bottom: 2px solid #ccc;
border-top: 2px solid #ccc;
position: relative;
margin-top: 15px;
}
.branch-box .col-box::before {
content: '';
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 0;
margin: auto;
width: 2px;
height: 100%;
background-color: rgb(202, 202, 202);
}
.condition-node {
display: inline-flex;
flex-direction: column;
min-height: 220px;
}
.condition-node-box {
padding-top: 30px;
padding-right: 50px;
padding-left: 50px;
justify-content: center;
align-items: center;
flex-grow: 1;
position: relative;
display: inline-flex;
flex-direction: column;
}
.condition-node-box::before {
content: '';
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
margin: auto;
width: 2px;
height: 100%;
background-color: rgb(202, 202, 202);
}
.auto-judge {
position: relative;
width: 220px;
min-height: 72px;
background: rgb(255, 255, 255);
border-radius: 4px;
padding: 15px 15px;
cursor: pointer;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.1);
}
.auto-judge::before {
content: '';
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
width: 0px;
border-style: solid;
border-width: 8px 6px 4px;
border-color: rgb(202, 202, 202) transparent transparent;
background: rgb(245, 245, 247);
}
.auto-judge .title {
line-height: 16px;
}
.auto-judge .title .node-title {
color: #15bc83;
}
.auto-judge .title .close {
font-size: 15px;
position: absolute;
top: 15px;
right: 15px;
color: #999;
display: none;
}
.auto-judge .title .priority-title {
position: absolute;
top: 15px;
right: 15px;
color: #999;
}
.auto-judge .content {
position: relative;
padding-top: 15px;
}
.auto-judge .content .placeholder {
color: #999;
}
.auto-judge:hover {
.close {
display: block;
}
.priority-title {
display: none;
}
}
.top-left-cover-line,
.top-right-cover-line {
position: absolute;
height: 3px;
width: 50%;
background-color: #f6f8f9;
top: -2px;
}
.bottom-left-cover-line,
.bottom-right-cover-line {
position: absolute;
height: 3px;
width: 50%;
background-color: #f6f8f9;
bottom: -2px;
}
.top-left-cover-line {
left: -1px;
}
.top-right-cover-line {
right: -1px;
}
.bottom-left-cover-line {
left: -1px;
}
.bottom-right-cover-line {
right: -1px;
}
.end-node {
border-radius: 50%;
font-size: 14px;
color: rgba(25, 31, 37, 0.4);
text-align: left;
}
.end-node-circle {
width: 10px;
height: 10px;
margin: auto;
border-radius: 50%;
background: #dbdcdc;
}
.end-node-text {
margin-top: 5px;
text-align: center;
}
.auto-judge:hover {
.sort-left {
display: flex;
}
.sort-right {
display: flex;
}
}
.auto-judge .sort-left {
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
left: 0;
display: none;
justify-content: center;
align-items: center;
flex-direction: column;
}
.auto-judge .sort-right {
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
right: 0;
display: none;
justify-content: center;
align-items: center;
flex-direction: column;
}
.auto-judge .sort-left:hover,
.auto-judge .sort-right:hover {
background: #eee;
}
.auto-judge:after {
pointer-events: none;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
border-radius: 4px;
transition: all 0.1s;
}
.auto-judge:hover:after {
border: 1px solid #3296fa;
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
}
.node-wrap-box:after {
pointer-events: none;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
border-radius: 4px;
transition: all 0.1s;
}
.node-wrap-box:hover:after {
border: 1px solid #3296fa;
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
}
}
.tags-list {
margin-top: 15px;
width: 100%;
}
.add-node-popover-body {
}
.add-node-popover-body li {
display: inline-block;
width: 80px;
text-align: center;
padding: 10px 0;
}
.add-node-popover-body li i {
border: 1px solid var(--el-border-color-light);
width: 40px;
height: 40px;
border-radius: 50%;
text-align: center;
line-height: 38px;
font-size: 18px;
cursor: pointer;
}
.add-node-popover-body li i:hover {
border: 1px solid #3296fa;
background: #3296fa;
color: #fff !important;
}
.add-node-popover-body li p {
font-size: 12px;
margin-top: 5px;
}
.node-wrap-drawer__title {
padding-right: 40px;
}
.node-wrap-drawer__title label {
cursor: pointer;
}
.node-wrap-drawer__title label:hover {
border-bottom: 1px dashed #409eff;
}
.node-wrap-drawer__title .node-wrap-drawer__title-edit {
color: #409eff;
margin-left: 10px;
vertical-align: middle;
}
.dark .sc-workflow-design {
.node-wrap-box,
.auto-judge {
background: #2b2b2b;
}
.col-box {
background: var(--el-bg-color);
}
.top-left-cover-line,
.top-right-cover-line,
.bottom-left-cover-line,
.bottom-right-cover-line {
background-color: var(--el-bg-color);
}
.node-wrap-box::before,
.auto-judge::before {
background-color: var(--el-bg-color);
}
.branch-box .add-branch {
background: var(--el-bg-color);
}
.end-node .end-node-text {
color: #d0d0d0;
}
.auto-judge .sort-left:hover,
.auto-judge .sort-right:hover {
background: var(--el-bg-color);
}
}
</style>

View File

@ -1,53 +0,0 @@
<template>
<promoter v-if="nodeConfig.type == 0" v-model="nodeConfig"></promoter>
<approver v-if="nodeConfig.type == 1" v-model="nodeConfig"></approver>
<send v-if="nodeConfig.type == 2" v-model="nodeConfig"></send>
<branch v-if="nodeConfig.type == 4" v-model="nodeConfig">
<template v-slot="slot">
<node-wrap v-if="slot.node" v-model="slot.node.childNode"></node-wrap>
</template>
</branch>
<node-wrap v-if="nodeConfig.childNode" v-model="nodeConfig.childNode"></node-wrap>
</template>
<script>
import approver from './nodes/approver.vue';
import promoter from './nodes/promoter.vue';
import branch from './nodes/branch.vue';
import send from './nodes/send.vue';
export default {
props: {
modelValue: { type: Object, default: () => {} }
},
components: {
approver,
promoter,
branch,
send
},
data() {
return {
nodeConfig: {}
};
},
watch: {
modelValue(val) {
this.nodeConfig = val;
},
nodeConfig(val) {
this.$emit('update:modelValue', val);
}
},
mounted() {
this.nodeConfig = this.modelValue;
},
methods: {}
};
</script>
<style></style>

View File

@ -1,95 +0,0 @@
<template>
<div class="add-node-btn-box">
<div class="add-node-btn">
<el-popover placement="right-start" :width="270" trigger="click" :hide-after="0" :show-after="0">
<template #reference>
<el-button type="primary" icon="el-icon-plus" circle></el-button>
</template>
<div class="add-node-popover-body">
<ul>
<li>
<el-icon style="color: #ff943e" @click="addType(1)"><el-icon-user-filled /></el-icon>
<p>审批节点</p>
</li>
<li>
<el-icon style="color: #3296fa" @click="addType(2)"><el-icon-promotion /></el-icon>
<p>抄送节点</p>
</li>
<li>
<el-icon style="color: #15bc83" @click="addType(4)"><el-icon-share /></el-icon>
<p>条件分支</p>
</li>
</ul>
</div>
</el-popover>
</div>
</div>
</template>
<script>
export default {
props: {
modelValue: { type: Object, default: () => {} }
},
data() {
return {};
},
mounted() {},
methods: {
addType(type) {
var node = {};
if (type == 1) {
node = {
nodeName: '审核人',
type: 1, //
setType: 1, //
nodeUserList: [], //
nodeRoleList: [], //
examineLevel: 1, //
directorLevel: 1, //
selectMode: 1, //
termAuto: false, //
term: 0, //
termMode: 1, //
examineMode: 1, //
directorMode: 0, //
childNode: this.modelValue
};
} else if (type == 2) {
node = {
nodeName: '抄送人',
type: 2,
userSelectFlag: true,
nodeUserList: [],
childNode: this.modelValue
};
} else if (type == 4) {
node = {
nodeName: '条件路由',
type: 4,
conditionNodes: [
{
nodeName: '条件1',
type: 3,
priorityLevel: 1,
conditionMode: 1,
conditionList: []
},
{
nodeName: '条件2',
type: 3,
priorityLevel: 2,
conditionMode: 1,
conditionList: []
}
],
childNode: this.modelValue
};
}
this.$emit('update:modelValue', node);
}
}
};
</script>
<style></style>

View File

@ -1,204 +0,0 @@
<template>
<div class="node-wrap">
<div class="node-wrap-box" @click="show">
<div class="title" style="background: #ff943e">
<el-icon class="icon"><el-icon-user-filled /></el-icon>
<span>{{ nodeConfig.nodeName }}</span>
<el-icon class="close" @click.stop="delNode()"><el-icon-close /></el-icon>
</div>
<div class="content">
<span v-if="toText(nodeConfig)">{{ toText(nodeConfig) }}</span>
<span v-else class="placeholder">请选择</span>
</div>
</div>
<add-node v-model="nodeConfig.childNode"></add-node>
<el-drawer v-model="drawer" title="审批人设置" destroy-on-close append-to-body :size="500">
<template #header>
<div class="node-wrap-drawer__title">
<label v-if="!isEditTitle" @click="editTitle">
{{ form.nodeName }}
<el-icon class="node-wrap-drawer__title-edit"> <el-icon-edit /></el-icon>
</label>
<el-input
v-if="isEditTitle"
ref="nodeTitle"
v-model="form.nodeName"
clearable
@blur="saveTitle"
@keyup.enter="saveTitle"
></el-input>
</div>
</template>
<el-container>
<el-main style="padding: 0 20px 20px 20px">
<el-form label-position="top">
<el-form-item label="审批人员类型">
<el-select v-model="form.setType">
<el-option :value="1" label="指定成员"></el-option>
<el-option :value="2" label="主管"></el-option>
<el-option :value="3" label="角色"></el-option>
<el-option :value="4" label="发起人自选"></el-option>
<el-option :value="5" label="发起人自己"></el-option>
<el-option :value="7" label="连续多级主管"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="form.setType == 1" label="选择成员">
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(1, form.nodeUserList)">选择人员</el-button>
<div class="tags-list">
<el-tag v-for="(user, index) in form.nodeUserList" :key="user.id" closable @close="delUser(index)">{{
user.name
}}</el-tag>
</div>
</el-form-item>
<el-form-item v-if="form.setType == 2" label="指定主管">
发起人的第 <el-input-number v-model="form.examineLevel" :min="1" /> 级主管
</el-form-item>
<el-form-item v-if="form.setType == 3" label="选择角色">
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(2, form.nodeRoleList)">选择角色</el-button>
<div class="tags-list">
<el-tag v-for="(role, index) in form.nodeRoleList" :key="role.id" type="info" closable @close="delRole(index)">{{
role.name
}}</el-tag>
</div>
</el-form-item>
<el-form-item v-if="form.setType == 4" label="发起人自选">
<el-radio-group v-model="form.selectMode">
<el-radio :label="1">自选一个人</el-radio>
<el-radio :label="2">自选多个人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.setType == 7" label="连续主管审批终点">
<el-radio-group v-model="form.directorMode">
<el-radio :label="0">直到最上层主管</el-radio>
<el-radio :label="1">自定义审批终点</el-radio>
</el-radio-group>
<p v-if="form.directorMode == 1">直到发起人的第 <el-input-number v-model="form.directorLevel" :min="1" /> 级主管</p>
</el-form-item>
<el-divider></el-divider>
<el-form-item label="">
<el-checkbox v-model="form.termAuto" label="超时自动审批"></el-checkbox>
</el-form-item>
<template v-if="form.termAuto">
<el-form-item label="审批期限(为 0 则不生效)">
<el-input-number v-model="form.term" :min="0" /> 小时
</el-form-item>
<el-form-item label="审批期限超时后执行">
<el-radio-group v-model="form.termMode">
<el-radio :label="0">自动通过</el-radio>
<el-radio :label="1">自动拒绝</el-radio>
</el-radio-group>
</el-form-item>
</template>
<el-divider></el-divider>
<el-form-item label="多人审批时审批方式">
<el-radio-group v-model="form.examineMode">
<p style="width: 100%"><el-radio :label="1">按顺序依次审批</el-radio></p>
<p style="width: 100%"><el-radio :label="2">会签 (可同时审批每个人必须审批通过)</el-radio></p>
<p style="width: 100%"><el-radio :label="3">或签 (有一人审批通过即可)</el-radio></p>
</el-radio-group>
</el-form-item>
</el-form>
</el-main>
<el-footer>
<el-button type="primary" @click="save">保存</el-button>
<el-button @click="drawer = false">取消</el-button>
</el-footer>
</el-container>
</el-drawer>
</div>
</template>
<script>
import addNode from './addNode.vue';
export default {
inject: ['select'],
props: {
modelValue: { type: Object, default: () => {} }
},
components: {
addNode
},
data() {
return {
nodeConfig: {},
drawer: false,
isEditTitle: false,
form: {}
};
},
watch: {
modelValue() {
this.nodeConfig = this.modelValue;
}
},
mounted() {
this.nodeConfig = this.modelValue;
},
methods: {
show() {
this.form = {};
this.form = JSON.parse(JSON.stringify(this.nodeConfig));
this.drawer = true;
},
editTitle() {
this.isEditTitle = true;
this.$nextTick(() => {
this.$refs.nodeTitle.focus();
});
},
saveTitle() {
this.isEditTitle = false;
},
save() {
this.$emit('update:modelValue', this.form);
this.drawer = false;
},
delNode() {
this.$emit('update:modelValue', this.nodeConfig.childNode);
},
delUser(index) {
this.form.nodeUserList.splice(index, 1);
},
delRole(index) {
this.form.nodeRoleList.splice(index, 1);
},
selectHandle(type, data) {
this.select(type, data);
},
toText(nodeConfig) {
if (nodeConfig.setType == 1) {
if (nodeConfig.nodeUserList && nodeConfig.nodeUserList.length > 0) {
const users = nodeConfig.nodeUserList.map(item => item.name).join('、');
return users;
} else {
return false;
}
} else if (nodeConfig.setType == 2) {
return nodeConfig.examineLevel == 1 ? '直接主管' : `发起人的第${nodeConfig.examineLevel}级主管`;
} else if (nodeConfig.setType == 3) {
if (nodeConfig.nodeRoleList && nodeConfig.nodeRoleList.length > 0) {
const roles = nodeConfig.nodeRoleList.map(item => item.name).join('、');
return '角色-' + roles;
} else {
return false;
}
} else if (nodeConfig.setType == 4) {
return '发起人自选';
} else if (nodeConfig.setType == 5) {
return '发起人自己';
} else if (nodeConfig.setType == 7) {
return '连续多级主管';
}
}
}
};
</script>
<style></style>

View File

@ -1,235 +0,0 @@
<template>
<div class="branch-wrap">
<div class="branch-box-wrap">
<div class="branch-box">
<el-button class="add-branch" type="success" plain round @click="addTerm">添加条件</el-button>
<div v-for="(item, index) in nodeConfig.conditionNodes" :key="index" class="col-box">
<div class="condition-node">
<div class="condition-node-box">
<div class="auto-judge" @click="show(index)">
<div v-if="index != 0" class="sort-left" @click.stop="arrTransfer(index, -1)">
<el-icon><el-icon-arrow-left /></el-icon>
</div>
<div class="title">
<span class="node-title">{{ item.nodeName }}</span>
<span class="priority-title">优先级{{ item.priorityLevel }}</span>
<el-icon class="close" @click.stop="delTerm(index)"><el-icon-close /></el-icon>
</div>
<div class="content">
<span v-if="toText(nodeConfig, index)">{{ toText(nodeConfig, index) }}</span>
<span v-else class="placeholder">请设置条件</span>
</div>
<div v-if="index != nodeConfig.conditionNodes.length - 1" class="sort-right" @click.stop="arrTransfer(index)">
<el-icon><el-icon-arrow-right /></el-icon>
</div>
</div>
<add-node v-model="item.childNode"></add-node>
</div>
</div>
<slot v-if="item.childNode" :node="item"></slot>
<div v-if="index == 0" class="top-left-cover-line"></div>
<div v-if="index == 0" class="bottom-left-cover-line"></div>
<div v-if="index == nodeConfig.conditionNodes.length - 1" class="top-right-cover-line"></div>
<div v-if="index == nodeConfig.conditionNodes.length - 1" class="bottom-right-cover-line"></div>
</div>
</div>
<add-node v-model="nodeConfig.childNode"></add-node>
</div>
<el-drawer v-model="drawer" title="条件设置" destroy-on-close append-to-body :size="600">
<template #header>
<div class="node-wrap-drawer__title">
<label v-if="!isEditTitle" @click="editTitle">
{{ form.nodeName }}
<el-icon class="node-wrap-drawer__title-edit"><el-icon-edit /></el-icon>
</label>
<el-input
v-if="isEditTitle"
ref="nodeTitle"
v-model="form.nodeName"
clearable
@blur="saveTitle"
@keyup.enter="saveTitle"
></el-input>
</div>
</template>
<el-container>
<el-main style="padding: 0 20px 20px 20px">
<el-form label-position="top">
<el-form-item label="条件关系">
<el-radio-group v-model="form.conditionMode">
<el-radio :label="1"></el-radio>
<el-radio :label="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-divider></el-divider>
<el-form-item>
<el-table :data="form.conditionList">
<el-table-column prop="label" label="描述">
<template #default="scope">
<el-input v-model="scope.row.label" placeholder="描述"></el-input>
</template>
</el-table-column>
<el-table-column prop="field" label="条件字段" width="130">
<template #default="scope">
<el-input v-model="scope.row.field" placeholder="条件字段"></el-input>
</template>
</el-table-column>
<el-table-column prop="operator" label="运算符" width="130">
<template #default="scope">
<el-select v-model="scope.row.operator" placeholder="Select">
<el-option label="等于" value="="></el-option>
<el-option label="不等于" value="!="></el-option>
<el-option label="大于" value=">"></el-option>
<el-option label="大于等于" value=">="></el-option>
<el-option label="小于" value="<"></el-option>
<el-option label="小于等于" value="<="></el-option>
<el-option label="包含" value="include"></el-option>
<el-option label="不包含" value="notinclude"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="value" label="值" width="100">
<template #default="scope">
<el-input v-model="scope.row.value" placeholder="值"></el-input>
</template>
</el-table-column>
<el-table-column prop="value" label="移除" width="55">
<template #default="scope">
<el-link type="danger" :underline="false" @click="deleteConditionList(scope.$index)">移除</el-link>
</template>
</el-table-column>
</el-table>
</el-form-item>
<p><el-button type="primary" icon="el-icon-plus" round @click="addConditionList">增加条件</el-button></p>
</el-form>
</el-main>
<el-footer>
<el-button type="primary" @click="save">保存</el-button>
<el-button @click="drawer = false">取消</el-button>
</el-footer>
</el-container>
</el-drawer>
</div>
</template>
<script>
import addNode from './addNode.vue';
export default {
props: {
modelValue: { type: Object, default: () => {} }
},
components: {
addNode
},
data() {
return {
nodeConfig: {},
drawer: false,
isEditTitle: false,
index: 0,
form: {}
};
},
watch: {
modelValue() {
this.nodeConfig = this.modelValue;
}
},
mounted() {
this.nodeConfig = this.modelValue;
},
methods: {
show(index) {
this.index = index;
this.form = {};
this.form = JSON.parse(JSON.stringify(this.nodeConfig.conditionNodes[index]));
this.drawer = true;
},
editTitle() {
this.isEditTitle = true;
this.$nextTick(() => {
this.$refs.nodeTitle.focus();
});
},
saveTitle() {
this.isEditTitle = false;
},
save() {
this.nodeConfig.conditionNodes[this.index] = this.form;
this.$emit('update:modelValue', this.nodeConfig);
this.drawer = false;
},
addTerm() {
let len = this.nodeConfig.conditionNodes.length + 1;
this.nodeConfig.conditionNodes.push({
nodeName: '条件' + len,
type: 3,
priorityLevel: len,
conditionMode: 1,
conditionList: []
});
},
delTerm(index) {
this.nodeConfig.conditionNodes.splice(index, 1);
if (this.nodeConfig.conditionNodes.length == 1) {
if (this.nodeConfig.childNode) {
if (this.nodeConfig.conditionNodes[0].childNode) {
this.reData(this.nodeConfig.conditionNodes[0].childNode, this.nodeConfig.childNode);
} else {
this.nodeConfig.conditionNodes[0].childNode = this.nodeConfig.childNode;
}
}
this.$emit('update:modelValue', this.nodeConfig.conditionNodes[0].childNode);
}
},
reData(data, addData) {
if (!data.childNode) {
data.childNode = addData;
} else {
this.reData(data.childNode, addData);
}
},
arrTransfer(index, type = 1) {
this.nodeConfig.conditionNodes[index] = this.nodeConfig.conditionNodes.splice(
index + type,
1,
this.nodeConfig.conditionNodes[index]
)[0];
this.nodeConfig.conditionNodes.map((item, index) => {
item.priorityLevel = index + 1;
});
this.$emit('update:modelValue', this.nodeConfig);
},
addConditionList() {
this.form.conditionList.push({
label: '',
field: '',
operator: '=',
value: ''
});
},
deleteConditionList(index) {
this.form.conditionList.splice(index, 1);
},
toText(nodeConfig, index) {
var { conditionList } = nodeConfig.conditionNodes[index];
if (conditionList && conditionList.length == 1) {
const text = conditionList.map(item => `${item.label}${item.operator}${item.value}`).join(' 和 ');
return text;
} else if (conditionList && conditionList.length > 1) {
const conditionModeText = nodeConfig.conditionNodes[index].conditionMode == 1 ? '且行' : '或行';
return conditionList.length + '个条件,' + conditionModeText;
} else {
if (index == nodeConfig.conditionNodes.length - 1) {
return '其他条件进入此流程';
} else {
return false;
}
}
}
}
};
</script>
<style></style>

View File

@ -1,122 +0,0 @@
<template>
<div class="node-wrap">
<div class="node-wrap-box start-node" @click="show">
<div class="title" style="background: #576a95">
<el-icon class="icon"><el-icon-user-filled /></el-icon>
<span>{{ nodeConfig.nodeName }}</span>
</div>
<div class="content">
<span>{{ toText(nodeConfig) }}</span>
</div>
</div>
<add-node v-model="nodeConfig.childNode"></add-node>
<el-drawer v-model="drawer" title="发起人" destroy-on-close append-to-body :size="500">
<template #header>
<div class="node-wrap-drawer__title">
<label v-if="!isEditTitle" @click="editTitle">
{{ form.nodeName }}
<el-icon class="node-wrap-drawer__title-edit"><el-icon-edit /></el-icon>
</label>
<el-input
v-if="isEditTitle"
ref="nodeTitle"
v-model="form.nodeName"
clearable
@blur="saveTitle"
@keyup.enter="saveTitle"
></el-input>
</div>
</template>
<el-container>
<el-main style="padding: 0 20px 20px 20px">
<el-form label-position="top">
<el-form-item label="谁可以发起此审批">
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(2, form.nodeRoleList)">选择角色</el-button>
<div class="tags-list">
<el-tag v-for="(role, index) in form.nodeRoleList" :key="role.id" type="info" closable @close="delRole(index)">{{
role.name
}}</el-tag>
</div>
</el-form-item>
<el-alert
v-if="form.nodeRoleList.length == 0"
title="不指定则默认所有人都可发起此审批"
type="info"
:closable="false"
/>
</el-form>
</el-main>
<el-footer>
<el-button type="primary" @click="save">保存</el-button>
<el-button @click="drawer = false">取消</el-button>
</el-footer>
</el-container>
</el-drawer>
</div>
</template>
<script>
import addNode from './addNode.vue';
export default {
inject: ['select'],
props: {
modelValue: { type: Object, default: () => {} }
},
components: {
addNode
},
data() {
return {
nodeConfig: {},
drawer: false,
isEditTitle: false,
form: {}
};
},
watch: {
modelValue() {
this.nodeConfig = this.modelValue;
}
},
mounted() {
this.nodeConfig = this.modelValue;
},
methods: {
show() {
this.form = {};
this.form = JSON.parse(JSON.stringify(this.nodeConfig));
this.isEditTitle = false;
this.drawer = true;
},
editTitle() {
this.isEditTitle = true;
this.$nextTick(() => {
this.$refs.nodeTitle.focus();
});
},
saveTitle() {
this.isEditTitle = false;
},
selectHandle(type, data) {
this.select(type, data);
},
delRole(index) {
this.form.nodeRoleList.splice(index, 1);
},
save() {
this.$emit('update:modelValue', this.form);
this.drawer = false;
},
toText(nodeConfig) {
if (nodeConfig.nodeRoleList && nodeConfig.nodeRoleList.length > 0) {
return nodeConfig.nodeRoleList.map(item => item.name).join('、');
} else {
return '所有人';
}
}
}
};
</script>
<style></style>

View File

@ -1,128 +0,0 @@
<template>
<div class="node-wrap">
<div class="node-wrap-box" @click="show">
<div class="title" style="background: #3296fa">
<el-icon class="icon"><el-icon-promotion /></el-icon>
<span>{{ nodeConfig.nodeName }}</span>
<el-icon class="close" @click.stop="delNode()"><el-icon-close /></el-icon>
</div>
<div class="content">
<span v-if="toText(nodeConfig)">{{ toText(nodeConfig) }}</span>
<span v-else class="placeholder">请选择人员</span>
</div>
</div>
<add-node v-model="nodeConfig.childNode"></add-node>
<el-drawer v-model="drawer" title="抄送人设置" destroy-on-close append-to-body :size="500">
<template #header>
<div class="node-wrap-drawer__title">
<label v-if="!isEditTitle" @click="editTitle">
{{ form.nodeName }}
<el-icon class="node-wrap-drawer__title-edit"><el-icon-edit /></el-icon>
</label>
<el-input
v-if="isEditTitle"
ref="nodeTitle"
v-model="form.nodeName"
clearable
@blur="saveTitle"
@keyup.enter="saveTitle"
></el-input>
</div>
</template>
<el-container>
<el-main style="padding: 0 20px 20px 20px">
<el-form label-position="top">
<el-form-item label="选择要抄送的人员">
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(1, form.nodeUserList)">选择人员</el-button>
<div class="tags-list">
<el-tag v-for="(user, index) in form.nodeUserList" :key="user.id" closable @close="delUser(index)">{{
user.name
}}</el-tag>
</div>
</el-form-item>
<el-form-item label="">
<el-checkbox v-model="form.userSelectFlag" label="允许发起人自选抄送人"></el-checkbox>
</el-form-item>
</el-form>
</el-main>
<el-footer>
<el-button type="primary" @click="save">保存</el-button>
<el-button @click="drawer = false">取消</el-button>
</el-footer>
</el-container>
</el-drawer>
</div>
</template>
<script>
import addNode from './addNode.vue';
export default {
inject: ['select'],
props: {
modelValue: { type: Object, default: () => {} }
},
components: {
addNode
},
data() {
return {
nodeConfig: {},
drawer: false,
isEditTitle: false,
form: {}
};
},
watch: {
modelValue() {
this.nodeConfig = this.modelValue;
}
},
mounted() {
this.nodeConfig = this.modelValue;
},
methods: {
show() {
this.form = {};
this.form = JSON.parse(JSON.stringify(this.nodeConfig));
this.drawer = true;
},
editTitle() {
this.isEditTitle = true;
this.$nextTick(() => {
this.$refs.nodeTitle.focus();
});
},
saveTitle() {
this.isEditTitle = false;
},
save() {
this.$emit('update:modelValue', this.form);
this.drawer = false;
},
delNode() {
this.$emit('update:modelValue', this.nodeConfig.childNode);
},
delUser(index) {
this.form.nodeUserList.splice(index, 1);
},
selectHandle(type, data) {
this.select(type, data);
},
toText(nodeConfig) {
if (nodeConfig.nodeUserList && nodeConfig.nodeUserList.length > 0) {
const users = nodeConfig.nodeUserList.map(item => item.name).join('、');
return users;
} else {
if (nodeConfig.userSelectFlag) {
return '发起人自选';
} else {
return false;
}
}
}
}
};
</script>
<style></style>

View File

@ -1,402 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="titleMap[type - 1]"
:width="type == 1 ? 680 : 460"
destroy-on-close
append-to-body
@closed="$emit('closed')"
>
<template v-if="type == 1">
<div class="sc-user-select">
<div class="sc-user-select__left">
<div class="sc-user-select__search">
<el-input v-model="keyword" prefix-icon="el-icon-search" placeholder="搜索成员">
<template #append>
<el-button icon="el-icon-search" @click="search"></el-button>
</template>
</el-input>
</div>
<div class="sc-user-select__select">
<div v-loading="showGrouploading" class="sc-user-select__tree">
<el-scrollbar>
<el-tree
ref="groupTree"
class="menu"
:data="group"
:node-key="groupProps.key"
:props="groupProps"
highlight-current
:expand-on-click-node="false"
:current-node-key="groupId"
@node-click="groupClick"
/>
</el-scrollbar>
</div>
<div v-loading="showUserloading" class="sc-user-select__user">
<div class="sc-user-select__user__list">
<el-scrollbar ref="userScrollbar">
<el-tree
ref="userTree"
class="menu"
:data="user"
:node-key="userProps.key"
:props="userProps"
:default-checked-keys="selectedIds"
show-checkbox
check-on-click-node
@check-change="userClick"
></el-tree>
</el-scrollbar>
</div>
<footer>
<el-pagination
v-model:currentPage="currentPage"
background
layout="prev,next"
small
:total="total"
:page-size="pageSize"
@current-change="paginationChange"
></el-pagination>
</footer>
</div>
</div>
</div>
<div class="sc-user-select__toicon">
<el-icon><el-icon-arrow-right /></el-icon>
</div>
<div class="sc-user-select__selected">
<header>已选 ({{ selected.length }})</header>
<ul>
<el-scrollbar>
<li v-for="(item, index) in selected" :key="item.id">
<span class="name">
<el-avatar size="small">{{ item.name.substring(0, 1) }}</el-avatar>
<label>{{ item.name }}</label>
</span>
<span class="delete">
<el-button type="danger" icon="el-icon-delete" circle size="small" @click="deleteSelected(index)"></el-button>
</span>
</li>
</el-scrollbar>
</ul>
</div>
</div>
</template>
<template v-if="type == 2">
<div class="sc-user-select sc-user-select-role">
<div class="sc-user-select__left">
<div class="sc-user-select__select">
<div v-loading="showGrouploading" class="sc-user-select__tree">
<el-scrollbar>
<el-tree
ref="groupTree"
class="menu"
:data="role"
:node-key="roleProps.key"
:props="roleProps"
show-checkbox
check-strictly
check-on-click-node
:expand-on-click-node="false"
:default-checked-keys="selectedIds"
@check-change="roleClick"
/>
</el-scrollbar>
</div>
</div>
</div>
<div class="sc-user-select__toicon">
<el-icon><el-icon-arrow-right /></el-icon>
</div>
<div class="sc-user-select__selected">
<header>已选 ({{ selected.length }})</header>
<ul>
<el-scrollbar>
<li v-for="(item, index) in selected" :key="item.id">
<span class="name">
<label>{{ item.name }}</label>
</span>
<span class="delete">
<el-button type="danger" icon="el-icon-delete" circle size="small" @click="deleteSelected(index)"></el-button>
</span>
</li>
</el-scrollbar>
</ul>
</div>
</div>
</template>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</template>
</el-dialog>
</template>
<script>
import config from '@/config/workflow';
export default {
props: {
modelValue: { type: Boolean, default: false }
},
data() {
return {
groupProps: config.group.props,
userProps: config.user.props,
roleProps: config.role.props,
titleMap: ['人员选择', '角色选择'],
dialogVisible: false,
showGrouploading: false,
showUserloading: false,
keyword: '',
groupId: '',
pageSize: config.user.pageSize,
total: 0,
currentPage: 1,
group: [],
user: [],
role: [],
type: 1,
selected: [],
value: []
};
},
computed: {
selectedIds() {
return this.selected.map(t => t.id);
}
},
mounted() {},
methods: {
//
open(type, data) {
this.type = type;
this.value = data || [];
this.selected = JSON.parse(JSON.stringify(data || []));
this.dialogVisible = true;
if (this.type == 1) {
this.getGroup();
this.getUser();
} else if (this.type == 2) {
this.getRole();
}
},
//
async getGroup() {
this.showGrouploading = true;
var res = await config.group.apiObj.get();
this.showGrouploading = false;
var allNode = { [config.group.props.key]: '', [config.group.props.label]: '所有' };
res.data.unshift(allNode);
this.group = config.group.parseData(res).rows;
},
//
async getUser() {
this.showUserloading = true;
var params = {
[config.user.request.keyword]: this.keyword || null,
[config.user.request.groupId]: this.groupId || null,
[config.user.request.page]: this.currentPage,
[config.user.request.pageSize]: this.pageSize
};
var res = await config.user.apiObj.get(params);
this.showUserloading = false;
this.user = config.user.parseData(res).rows;
this.total = config.user.parseData(res).total || 0;
this.$refs.userScrollbar.setScrollTop(0);
},
//
async getRole() {
this.showGrouploading = true;
var res = await config.role.apiObj.get();
this.showGrouploading = false;
this.role = config.role.parseData(res).rows;
},
//
groupClick(data) {
this.keyword = '';
this.currentPage = 1;
this.groupId = data[config.group.props.key];
this.getUser();
},
//
userClick(data, checked) {
if (checked) {
this.selected.push({
id: data[config.user.props.key],
name: data[config.user.props.label]
});
} else {
this.selected = this.selected.filter(item => item.id != data[config.user.props.key]);
}
},
//
paginationChange() {
this.getUser();
},
//
search() {
this.groupId = '';
this.$refs.groupTree.setCurrentKey(this.groupId);
this.currentPage = 1;
this.getUser();
},
//
deleteSelected(index) {
this.selected.splice(index, 1);
if (this.type == 1) {
this.$refs.userTree.setCheckedKeys(this.selectedIds);
} else if (this.type == 2) {
this.$refs.groupTree.setCheckedKeys(this.selectedIds);
}
},
//
roleClick(data, checked) {
if (checked) {
this.selected.push({
id: data[config.role.props.key],
name: data[config.role.props.label]
});
} else {
this.selected = this.selected.filter(item => item.id != data[config.role.props.key]);
}
},
//
save() {
this.value.splice(0, this.value.length);
this.selected.map(item => {
this.value.push(item);
});
this.dialogVisible = false;
}
}
};
</script>
<style scoped>
.sc-user-select {
display: flex;
}
.sc-user-select__left {
width: 400px;
}
.sc-user-select__right {
flex: 1;
}
.sc-user-select__search {
padding-bottom: 10px;
}
.sc-user-select__select {
display: flex;
border: 1px solid var(--el-border-color-light);
background: var(--el-color-white);
}
.sc-user-select__tree {
width: 200px;
height: 300px;
border-right: 1px solid var(--el-border-color-light);
}
.sc-user-select__user {
width: 200px;
height: 300px;
display: flex;
flex-direction: column;
}
.sc-user-select__user__list {
flex: 1;
overflow: auto;
}
.sc-user-select__user footer {
height: 36px;
padding-top: 5px;
border-top: 1px solid var(--el-border-color-light);
}
.sc-user-select__toicon {
display: flex;
justify-content: center;
align-items: center;
margin: 0 10px;
}
.sc-user-select__toicon i {
display: flex;
justify-content: center;
align-items: center;
background: #ccc;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
border-radius: 50%;
color: #fff;
}
.sc-user-select__selected {
height: 345px;
width: 200px;
border: 1px solid var(--el-border-color-light);
background: var(--el-color-white);
}
.sc-user-select__selected header {
height: 43px;
line-height: 43px;
border-bottom: 1px solid var(--el-border-color-light);
padding: 0 15px;
font-size: 12px;
}
.sc-user-select__selected ul {
height: 300px;
overflow: auto;
}
.sc-user-select__selected li {
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 5px 5px 15px;
height: 38px;
}
.sc-user-select__selected li .name {
display: flex;
align-items: center;
}
.sc-user-select__selected li .name .el-avatar {
background: #409eff;
margin-right: 10px;
}
.sc-user-select__selected li .name label {
}
.sc-user-select__selected li .delete {
display: none;
}
.sc-user-select__selected li:hover {
background: var(--el-color-primary-light-9);
}
.sc-user-select__selected li:hover .delete {
display: inline-block;
}
.sc-user-select-role .sc-user-select__left {
width: 200px;
}
.sc-user-select-role .sc-user-select__tree {
border: none;
height: 343px;
}
.sc-user-select-role .sc-user-select__selected {
}
[data-theme='dark'] .sc-user-select__selected li:hover {
background: rgba(0, 0, 0, 0.2);
}
[data-theme='dark'] .sc-user-select__toicon i {
background: #383838;
}
</style>

View File

@ -83,7 +83,7 @@ const onExitMaximizeClick = () => {
<style lang="scss" scoped>
.el-main {
box-sizing: border-box;
padding: 10px 12px;
padding: 0;
overflow-x: hidden;
background-color: var(--el-bg-color-page);
}

View File

@ -1,6 +1,7 @@
import { createApp } from 'vue';
import App from '@/App.vue';
import ElementPlus from 'element-plus';
import VueGridLayout from 'vue-grid-layout';
// icon-park
import { install } from '@icon-park/vue-next/es/all';
// element icons
@ -28,6 +29,7 @@ Object.keys(Icons).forEach(key => {
app.component(key, Icons[key as keyof typeof Icons]);
});
app.use(ElementPlus);
app.use(VueGridLayout);
app.use(directives);
app.use(router);
app.use(I18n);

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -56,7 +56,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -53,7 +53,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -53,7 +53,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -56,7 +56,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,20 +0,0 @@
<template>
<div class="h-1000px">
<!-- 布局 -->
啊啊啊
</div>
</template>
<route lang="yaml">
meta:
title: AA标题
isAffix: false
</route>
<script lang="ts" setup name="HomeAa">
//
</script>
<style lang="scss" scoped>
//
</style>

View File

@ -1,103 +0,0 @@
<template>
<div class="h-full">
<!-- 布局 -->
<bd-workflow v-model="data.nodeConfig"></bd-workflow>
</div>
</template>
<route lang="yaml">
meta:
title: BB标题
isAffix: false
</route>
<script lang="ts" setup name="HomeBb">
import bdWorkflow from '@/components/bdWorkflow/index.vue';
//
const data = ref({
id: 1,
name: '请假审批',
nodeConfig: {
nodeName: '发起人',
type: 0,
nodeRoleList: [],
childNode: {
nodeName: '条件路由',
type: 4,
conditionNodes: [
{
nodeName: '长期',
type: 3,
priorityLevel: 1,
conditionMode: 1,
conditionList: [
{
label: '请假天数',
field: 'day',
operator: '>',
value: '7'
}
],
childNode: {
nodeName: '领导审批',
type: 1,
setType: 1,
nodeUserList: [
{
id: '360000197302144442',
name: '何敏'
}
],
nodeRoleList: [],
examineLevel: 1,
directorLevel: 1,
selectMode: 1,
termAuto: false,
term: 0,
termMode: 1,
examineMode: 1,
directorMode: 0
}
},
{
nodeName: '短期',
type: 3,
priorityLevel: 2,
conditionMode: 1,
conditionList: [],
childNode: {
nodeName: '直接主管审批',
type: 1,
setType: 2,
nodeUserList: [],
nodeRoleList: [],
examineLevel: 1,
directorLevel: 1,
selectMode: 1,
termAuto: false,
term: 0,
termMode: 1,
examineMode: 1,
directorMode: 0
}
}
],
childNode: {
nodeName: '抄送人',
type: 2,
userSelectFlag: true,
nodeUserList: [
{
id: '220000200908305857',
name: '何秀英'
}
]
}
}
}
});
</script>
<style lang="scss" scoped>
//
</style>

View File

@ -0,0 +1,8 @@
<template>
<div class="el-card box-border p-12px h-full w-full">
<div>我的</div>
<div>100</div>
</div>
</template>
<script lang="ts" name="Statistics" setup></script>

View File

@ -1,5 +1,20 @@
<template>
<div v-waterMarker="{ text: '版权所有', textColor: 'rgba(180, 180, 180, 0.4)' }" class="h-600px w-400px">首页</div>
<bd-page class="!p-0">
<grid-layout
v-model:layout="layout"
:is-draggable="settings"
:is-resizable="settings"
:col-num="12"
:row-height="30"
:vertical-compact="true"
:use-css-transforms="true"
:margin="[12, 12]"
>
<grid-item v-for="item in layout" :key="item.i" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i">
<component :is="item.render" />
</grid-item>
</grid-layout>
</bd-page>
</template>
<route lang="yaml">
@ -7,3 +22,24 @@ meta:
title: 首页
isAffix: true
</route>
<script lang="tsx" setup>
import Statistics from './components/Statistics.vue';
const layout = ref([
{ x: 0, y: 0, w: 3, h: 4, i: '0', render: Statistics },
{ x: 3, y: 0, w: 3, h: 4, i: '1', render: Statistics },
{ x: 6, y: 0, w: 3, h: 4, i: '2', render: Statistics },
{ x: 9, y: 0, w: 3, h: 4, i: '3', render: Statistics },
{ x: 0, y: 1, w: 12, h: 12, i: '4', render: Statistics },
{ x: 4, y: 1, w: 12, h: 12, i: '5', render: Statistics }
]);
const settings = ref(false);
</script>
<style lang="scss" scoped>
.el-main {
padding: 0 !important;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -54,7 +54,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
@ -57,7 +57,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
@ -59,7 +59,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -50,7 +50,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -53,7 +53,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -44,7 +44,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -44,7 +44,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -43,7 +43,7 @@
</div>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -28,7 +28,7 @@
</div>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -50,7 +50,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -31,7 +31,7 @@
</el-table>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -38,7 +38,7 @@
</div>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -53,7 +53,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
@ -57,7 +57,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
@ -56,7 +56,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -1,5 +1,5 @@
<template>
<div class="wh-full flex-col">
<bd-page class="flex-col">
<!-- 布局 -->
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
@ -54,7 +54,7 @@
/>
</div>
</div>
</div>
</bd-page>
</template>
<route lang="yaml">

View File

@ -9,6 +9,7 @@ declare module 'vue' {
export interface GlobalComponents {
AddNode: typeof import('./../components/bdWorkflow/nodes/addNode.vue')['default']
Approver: typeof import('./../components/bdWorkflow/nodes/approver.vue')['default']
BdPage: typeof import('./../components/BdPage/index.vue')['default']
BdWorkflow: typeof import('./../components/bdWorkflow/index.vue')['default']
Branch: typeof import('./../components/bdWorkflow/nodes/branch.vue')['default']
HelloWorld: typeof import('./../components/HelloWorld.vue')['default']

3
src/types/env.d.ts vendored
View File

@ -7,3 +7,6 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>;
export default component;
}
// vue-grid-layout
declare module 'vue-grid-layout';