Compare commits

...

39 Commits

Author SHA1 Message Date
邓洁 dfeddfb341 dengjie : 修改人员选择器bug 2023-03-22 15:27:47 +08:00
邓洁 27b4cd4971 dengjie : 修改人员选择样式 2023-03-22 14:19:58 +08:00
邓洁 e1150abbb0 dengjie : 修改部分样式 2023-03-21 23:10:37 +08:00
邓洁 0f3da398ee dengjie : 修改部分样式 2023-03-21 23:08:36 +08:00
邓洁 08fe77cf2f Merge remote-tracking branch 'origin/master' 2023-03-21 22:59:39 +08:00
邓洁 2c8b9978e8 dengjie : 修改人员选择bug及部分样式 2023-03-21 22:59:31 +08:00
clay 92e66c2f45 clay : 节点渲染细节处理 2023-03-21 22:56:57 +08:00
clay a84bbbeedb clay : 任意节点退回完成(可以退回到并行网关或者条件网关) 2023-03-21 17:35:19 +08:00
clay 9285814edc clay : 任意节点退回完成 2023-03-20 17:28:25 +08:00
clay cde76e13cb clay : fix userPicker报错 2023-03-18 19:48:39 +08:00
clay f98ca9efbb clay : fix userPicker报错 2023-03-18 19:40:07 +08:00
邓洁 87b1d9d2c0 dengjie : 修改人员选择bug 2023-03-18 18:57:29 +08:00
邓洁 fc288926d5 dengjie : 审批流程部分样式修改 2023-03-13 00:55:08 +08:00
clay 74fbae3095 clay : 用户组件完成 2023-03-04 01:52:18 +08:00
邓洁 b271979372 dengjie : 用户选择器暂时能用,待优化样式 2023-03-04 01:18:43 +08:00
邓洁 dabdc0551e dengjie : 用户选择器暂时能用,待优化样式 2023-03-03 23:07:03 +08:00
邓洁 8df27644fb dengjie : 用户选择器占时能用,待优化样式 2023-03-03 22:51:19 +08:00
clay 2392ad5e0a 更新 'flowable-engine-web/src/api/request.js' 2023-03-01 08:01:43 +00:00
clay 49c295a5bc clay : 用户信息回显完成 2023-03-01 15:55:00 +08:00
clay fcfbafa62a clay : 退回做了一半 2023-02-28 00:31:11 +08:00
clay 17a00c86d4 clay : 用户信息回显完成 2023-02-27 01:56:32 +08:00
clay 4be0068978 clay : 用户信息回显完成 2023-02-27 01:39:18 +08:00
clay 5be2a2ecd4 clay : 表单权限校验完成 2023-02-26 00:07:09 +08:00
clay 73219c5c04 clay : 表单权限校验完成 2023-02-25 23:59:33 +08:00
clay 318e54f53f clay : 添加签名,评分两个组件 2023-02-25 22:06:50 +08:00
clay 219d33b07e clay : 添加签名,评分两个组件 2023-02-25 19:56:47 +08:00
clay a59157a525 clay : 抄送基本完成,现阶段进行夹具体的功能测试 2023-02-25 16:21:25 +08:00
clay 16c2d773b9 Merge remote-tracking branch 'origin/master' 2023-02-25 15:56:14 +08:00
clay e93c22da9f clay : 抄送基本完成,现阶段进行夹具体的功能测试 2023-02-25 15:56:00 +08:00
clay d6d3832475 更新 'flowable-engine-web/src/api/request.js' 2023-02-24 14:49:12 +00:00
clay 6dc15b6ae0 clay : 关于我的基本完成 2023-02-24 22:28:44 +08:00
邓洁 14b9e638e3 dengjie : 解决发起审批弹框timeline样式 2023-02-24 17:58:58 +08:00
clay 6bfd88fc73 clay : 时间线功能初步开发完成 2023-02-24 16:35:51 +08:00
odjbin 348de04db7 Merge pull request 'DJ' (#19) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/19
2023-02-17 10:14:31 +00:00
odjbin e78241c710 Merge pull request 'dengjie : 时长,处理耗时的工具类date.js' (#18) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/18
2023-02-16 17:19:22 +00:00
odjbin e6b901cbbe Merge pull request 'dengjie : 解决处理耗时时间,点击预览清空表单,审批通过时tag改变问题' (#17) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/17
2023-02-16 16:53:29 +00:00
odjbin ae13cebe3c Merge pull request 'dengjie : 解决上传图片/附件的组件placeholder问题' (#16) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/16
2023-02-16 11:47:02 +00:00
odjbin 424d86ba66 Merge pull request 'dengjie : 待我处理的审批详情的必填标志*已删除,审批流程条件右边的'优先级'已删除' (#15) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/15
2023-02-16 10:29:36 +00:00
odjbin a1a76b3efa Merge pull request 'dengjie : 上传图片组件的图片放大和删除' (#13) from DJ into master
Reviewed-on: http://git.hchyun.com/clay/workflow-engine-web/pulls/13
2023-02-15 09:15:59 +00:00
49 changed files with 2560 additions and 12985 deletions

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
"signature_pad": "^3.0.0-beta.4",
"trim-canvas": "^0.1.2",
"vue": "^2.6.11",
"vue-esign": "^1.1.4",
"vue-router": "^3.4.3",
"vuedraggable": "^2.24.1",
"vuex": "^3.5.1"

View File

@ -8,7 +8,6 @@
<script>
//import layout from './components/layout'
import {getToken} from "./main";
export default {
components: {},
@ -16,7 +15,6 @@
return {}
},
created() {
getToken()
},
mounted() {
let beforeUrl = sessionStorage.getItem('router-path')

View File

@ -34,6 +34,14 @@ export function getDepartmentTree() {
})
}
export default {
getOrgTree, getUserByName, getRole,getDepartmentTree
//根据角色或者部门获取到对应的数据
export function getUserTree(type,chooseId){
return request({
url:`/admin/user/choose/${type}/${chooseId}`,
method:'get'
})
}
export default {
getOrgTree, getUserByName, getRole,getDepartmentTree,getUserTree
}

View File

@ -13,6 +13,13 @@ export function startProcessInstance(param) {
})
}
export function getAboutInstanceList() {
return request({
url: "/flowable/process/instance/about",
method: "get",
})
}
export function getInitiatedInstanceList() {
return request({
url: "/flowable/process/instance/self",
@ -22,7 +29,7 @@ export function getInitiatedInstanceList() {
export function getInitiatedInstanceInfo(processInstanceId) {
return request({
url: "/flowable/process/instance/self/info/"+processInstanceId,
url: "/flowable/process/instance/info/"+processInstanceId,
method: "get",
})
}

View File

@ -0,0 +1,70 @@
import request from '@/api/request.js'
// 查询万能查询列表
export function listQuery(query) {
return request({
url: '/custom-query/uni/query',
method: 'get',
params: query
})
}
// 查询万能查询详细
export function getQuery(id) {
return request({
url: '/custom-query/uni/query/' + id,
method: 'get'
})
}
// 新增万能查询
export function addQuery(data) {
return request({
url: '/custom-query/uni/query',
method: 'post',
data: data
})
}
// 修改万能查询
export function updateQuery(data) {
return request({
url: '/custom-query/uni/query',
method: 'put',
data: data
})
}
// 删除万能查询
export function delQuery(id) {
return request({
url: '/custom-query/uni/query/' + id,
method: 'delete'
})
}
// 导出万能查询
export function exportQuery(query) {
return request({
url: '/custom-query/uni/query/export',
method: 'get',
params: query
})
}
// 获取条件
export function getQueryInfo(id) {
return request({
url: '/query/'+id,
method: 'get',
})
}
// 修改条件
export function editQueryInfo(data) {
return request({
url: '/query',
method: 'put',
data: data
})
}

View File

@ -5,13 +5,13 @@ import { Notification, MessageBox, Message } from "element-ui";
// 第三方插件
import "element-ui/lib/theme-chalk/index.css";
import {getToken} from "../main";
Vue.prototype.$axios = axios;
// 字体图标
export function getBaseUrl(){
return "http://gateway.mytwins.top"
// return "http://192.168.101.7:8000"
// return "http://localhost:8000"
}
@ -38,7 +38,7 @@ service.interceptors.response.use(
rsp => {
let data = rsp.data
if (data.code === 401){
getToken()
localStorage.clear()
}
if (data.code === 1000){
return data

View File

@ -0,0 +1,94 @@
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>

View File

@ -0,0 +1,38 @@
<!-- @author Shiyn/ huangmx 20200807优化-->
<template>
<div class="top-right-btn">
<el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
<el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
</el-tooltip>
</el-row>
</div>
</template>
<script>
export default {
name: "RightToolbar",
data() {
return {};
},
props: {
showSearch: {
type: Boolean,
default: true,
},
},
methods: {
//
toggleSearch() {
this.$emit("update:showSearch", !this.showSearch);
},
//
refresh() {
this.$emit("queryTable");
},
},
};
</script>

View File

@ -0,0 +1,115 @@
<template>
<div :class="{'line': row === 1, 'lines': row > 1}"
:title="hoverTip ? content: null"
:style="{'--row':row}">
<slot name="pre"></slot>
<div style="display: flex;flex-wrap: wrap;">
<div v-for="(user,index) in userInfo" :key="index" class="avatar_name">
<el-avatar size="large"
:src="user.avatar"></el-avatar>
<div v-if="user.icon"
class="el-timeline-item__node" :style="{
backgroundColor: user.color
}">
<i v-if="user.icon"
class="el-timeline-item__icon"
:class="user.icon"
></i>
</div>
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
<span class="item_name">{{ user.name }}</span>
</el-tooltip>
</div>
<!-- <el-button type="primary" icon="el-icon-plus" circle @click="$refs.userPicker.showUserPicker()"/>-->
</div>
</div>
</template>
<script>
export default {
name: "AvatarEllipsis",
install(Vue) {
Vue.component('avatarEllipsis', this)
},
components: {},
props: {
row: {
type: Number,
default: 1
},
hoverTip: {
type: Boolean,
default: false
},
userInfo: {
type: Array,
default: []
}
},
created() {
this.init()
},
data() {
return {}
},
methods: {
init() {
for (let user of this.userInfo) {
this.initUser(user)
}
},
initUser(user) {
let state = user.state
//
if (state === 'CREATE') {
this.$set(user, "icon", "el-icon-check")
this.$set(user, "color", "#0bbd87")
}
//
if (state === 'AGREE' || state === 'AUTO_PASS') {
this.$set(user, "icon", "el-icon-check")
this.$set(user, "color", "#0bbd87")
}
//
if (state === 'RUNNING') {
this.$set(user, "icon", "el-icon-loading")
this.$set(user, "color", "#f78f5f")
}
//
if (state === 'REFUSE' || state === 'AUTO_REFUSE') {
this.$set(user, "icon", "el-icon-close")
this.$set(user, "color", "#f56c6c")
}
if (state === 'PASS') {
this.$set(user, "icon", "el-icon-more")
this.$set(user, "color", "#c0c4cc")
}
return user;
},
}
}
</script>
<style scoped>
.avatar_name {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 1px;
/*width: 45px;*/
position: relative;
}
.el-timeline-item__node {
position: absolute;
bottom: 20px;
right: 1px;
}
.item_name{
width: 45px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding-top: 3px;
}
</style>

View File

@ -9,7 +9,7 @@
<!--显示退回节点弹出框-->
<div v-if="type === 4" style="margin-bottom: 10px">
<span>回退节点</span>
<el-select v-if="userTaskOption.length >0 " v-model="backNode" slot="prepend" placeholder="选择要回退到的节点">
<el-select v-if="userTaskOption.length >0 " v-model="rollBackId" slot="prepend" placeholder="选择要回退到的节点">
<el-option v-for="(option,index) in userTaskOption" :key="index"
:label="option.label" :value="option.value"/>
</el-select>
@ -61,7 +61,7 @@
</div>
<div slot="tip" class="el-upload__tip">添加图片 {{ sizeTip }}</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible" center append-to-body>
<el-dialog :visible.sync="dialogVisible" center append-to-body>
<div>
<img width="100%" :src="dialogImageUrl" alt="" style="z-index: 3435">
</div>
@ -140,7 +140,7 @@ export default {
authorization: localStorage.getItem("token")
},
context: null,
backNode: null,
rollBackId: null,
dialogImageUrl: '',
dialogVisible: false
};
@ -196,7 +196,7 @@ export default {
deleteFile(file.id).then(res => {
if (res.code === 1000) {
this.$message.success("删除成功")
this.fileList.splice( this.fileList.findIndex((item)=>item.id===file.id),1)
this.fileList.splice(this.fileList.findIndex((item) => item.id === file.id), 1)
}
})
},
@ -222,7 +222,8 @@ export default {
})
let data = {
context: this.context,
attachments: fileList
attachments: fileList,
rollBackId: this.rollBackId,
}
this.$emit("ok", data, this.type)
this.visible = false;

View File

@ -82,7 +82,6 @@ export default {
//
getRole() {
getRole().then(res => {
console.log('系统角色',res.data);
this.roleList= res.data.map(function (val){
return {roleId:val.value,roleName:val.label}
})

View File

@ -6,16 +6,34 @@
<div style="padding: 5px 8px;">
<el-input v-model="filterText" style="width: 100%;" size="small"
clearable placeholder="输入关键字进行过滤" prefix-icon="el-icon-search"/>
<div style="margin-top: 5px">
<el-radio-group v-model="radio" size="mini" @input="radioChange">
<el-radio-button :label="0">角色</el-radio-button>
<el-radio-button :label="1">部门</el-radio-button>
</el-radio-group>
</div>
</div>
<!-- 部门 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="deptList.length === 0"/>
<el-scrollbar style="height:350px">
<el-tree :data="deptList" ref="tree" :props="deptProps" empty-text="" node-key="deptId" default-expand-all
:show-checkbox="showCheckbox" check-strictly highlight-current
@check-change="handleCheckChange" @node-click="(node,check)=>handle(node,check)"
:filter-node-method="filterNode">
<div class="custom-tree-node" slot-scope="{ node }" style="width: 100%">
<i class="el-icon-folder-opened" style="margin-right: 5px"></i>{{ node.label }}
<!-- 人员选择 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/>
<el-scrollbar style="height:317px">
<el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value"
:default-expanded-keys="expandedKeys"
@node-click="handleChange"
:filter-node-method="filterNode"
>
<div class="tree-node" slot-scope="{ node,data }">
<div v-if="data.type === 0" style="display: flex;align-items: center">
<el-avatar :src="data.avatar"></el-avatar>
{{ node.label }}
</div>
<div v-else-if="data.type === 1">
<el-icon class="el-icon-user-solid"/>
{{ node.label }}
</div>
<div v-else>
<el-icon class="el-icon-folder-opened"/>
{{ node.label }}
</div>
</div>
</el-tree>
</el-scrollbar>
@ -29,9 +47,9 @@
<div class="org-items" style="height: 350px;">
<el-empty :image-size="100" description="请点击左侧列表选择数据" v-show="selectList.length === 0"/>
<div v-for="(selectItem, selectIndex) in selectList" :key="selectIndex" class="org-item">
<i class="el-icon-folder-opened"></i>
<span>{{ selectItem.label }}</span>
<i class="el-icon-close" @click="noSelected(selectItem)" v-if="showCheckbox===false"></i>
<el-avatar :src="selectItem.avatar" style="margin-right: 5px;"></el-avatar>
{{ selectItem.name }}
<i class="el-icon-close" @click="noSelected(selectItem)"></i>
</div>
</div>
</div>
@ -41,10 +59,10 @@
</template>
<script>
import {getDepartmentTree} from "@/api/org";
import {getUserTree} from "@/api/org";
export default {
name: "departmentPicker",
name: "Test",
props: {
value: {
type: Array,
@ -63,16 +81,24 @@ export default {
},
data() {
return {
radio: 0,
chooseId: 0,
selectItem: {
type: -1,
value: '0',
},
activeNames: ['1'],
visible: false,
loading: false,
title: "请选择",
selectList: [],
filterText: "",
deptList: [],
deptProps: {
dataList: [],
expandedKeys: [],
defaultProps: {
value: 'value',
label: 'label',
children: 'children'
label: 'name',
children: 'children',
}
};
},
@ -81,9 +107,6 @@ export default {
this.$refs.tree.filter(val);
}
},
created() {
this.getDepartmentTree();
},
computed: {
_value: {
get() {
@ -95,106 +118,77 @@ export default {
}
},
methods: {
//
getDepartmentTree() {
getDepartmentTree().then(res => {
// const jsona = JSON.stringify(res.data) // res.data
// const jsonb = jsona.replace(/"value"/g, '"deptId"') //
// const jsonc = jsonb.replace(/"label"/g, '"deptName"')
this.deptList = res.data
console.log("获取部门信息===========", this.deptList);
radioChange(e) {
this.selectItem.type = -2
this.chooseId = 0
this.radio = e
this.expandedKeys = []
this.getList();
},
getList() {
getUserTree(this.radio, this.chooseId).then(res => {
if (res.data) {
if (this.selectItem.type === -1 || this.selectItem.type === -2) {
this.dataList = this.setData(res.data)
} else if (this.selectItem.type === 1) {
this.selectItem.children = res.data
} else if (this.selectItem.type === 2) {
this.selectItem.children = this.setData(res.data)
}
}
});
},
setData(source) {
for (let item of source) {
this.$set(item, "value", this.selectItem.value + "-" + item.id)
this.$set(item, "children", [])
}
return source;
},
//
filterNode(value, data) {
if (!value) return true;
return data.deptName.indexOf(value) !== -1;
return data.name.indexOf(value) !== -1;
},
//
showDeptPicker() {
//
showUserPicker() {
this.getList();
this.visible = true;
},
/**
* 选中部门
* @param data 选择的每个节点item
* @param checked 是否选中
*/
handleCheckChange(data, checked) {
//
if (this.showCheckbox) {
// +
//
handleChange(item, check) {
this.selectItem = item
this.expandedKeys.push(item.value)
if (item.type !== 0) {
this.chooseId = item.id
this.getList()
}
if (!item.children) {
if (this.multiple) {
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === data.value) {
if (this.selectList[i].id === item.id) {
this.selectList.splice(i, 1);
break;
}
}
if (checked) {
this.selectList.push(data);
} else if (data === '1') {
this.$refs.tree.setCheckedKeys([]);
this.selectList = [];
}
} else {// +
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === data.value) {
this.selectList.splice(i, 1);
break;
}
}
if (checked) {
this.$refs.tree.setCheckedNodes([data]);
this.selectList = [data];
} else if (data === '1') {
this.$refs.tree.setCheckedKeys([]);
this.selectList = [];
}
this.selectList.push(item);
console.log('多选this.selectList', this.selectList);
} else {
this.selectList = [item];
}
}
this._value = this.selectList
},
//,tree-item
/**
* 可以点击树节点deptName,进行选择
* @param node 选择的每个节点item
* @param check checked(checkbox选择框)是否选中
*/
handle(node, check) {
if (this.multiple) {
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === node.value) {
this.selectList.splice(i, 1);
break;
}
}
check.checked = true
this.selectList.push(node);
} else {
check.checked = true
this.selectList = [node];
}
this._value = this.selectList
},
//,×
//×
noSelected(selectItem) {
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === selectItem.value) {
this.selectList.splice(i, 1);
this.$refs.tree.setCheckedKeys(i);
break;
}
}
if (this.showCheckbox) {
// +
if (this.multiple === false) {
this.$refs.tree.setCheckedKeys([]);
}
}
selectItem.selected = false;
},
//
clearSelected() {
@ -203,10 +197,7 @@ export default {
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.handleCheckChange("1");
if (!this.showCheckbox) {
this.selectList = []
}
this.selectList = []
});
},
//
@ -220,6 +211,23 @@ export default {
<style lang="less" scoped>
@containWidth: 278px;
/deep/ .el-tree-node {
.el-tree-node__children {
.el-tree-node__content {
height: 42px;
}
}
}
.tree-node {
div {
.el-avatar {
width: 40px;
height: 40px;
margin-right: 5px;
}
}
}
/deep/ .el-tree-node {
.is-leaf + .el-checkbox .el-checkbox__inner {

View File

@ -0,0 +1,353 @@
<template>
<w-dialog :border="false" closeFree width="600px" @ok="selectConfirm" :title="title" v-model="visible">
<div>
<div class="picker">
<div class="candidate" v-loading="loading">
<div style="padding: 5px 8px;">
<el-input v-model="filterText" style="width: 100%;" size="small"
clearable placeholder="输入关键字进行过滤" prefix-icon="el-icon-search"/>
<div style="margin-top: 5px">
<el-radio-group v-model="radio" size="mini" @input="radioChange">
<el-radio-button :label="0">角色</el-radio-button>
<el-radio-button :label="1">部门</el-radio-button>
</el-radio-group>
</div>
</div>
<!-- 人员选择 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/>
<el-scrollbar style="height:317px">
<el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value"
:default-expanded-keys="expandedKeys" lazy accordion
@node-click="handleChange"
>
<div class="tree-node" slot-scope="{ node,data }" >
<div v-if="data.type === 0" style="display: flex;align-items: center;">
<el-avatar :src="data.avatar"></el-avatar>
{{ node.label }}
</div>
<div v-else-if="data.type === 1" >
<el-icon class="el-icon-user-solid"/>
{{ node.label }}
</div>
<div v-else >
<el-icon class="el-icon-folder-opened"/>
{{ node.label }}
</div>
</div>
</el-tree>
</el-scrollbar>
</div>
<div class="selected">
<div class="count">
<span>已选 {{ selectList.length }} </span>
<span @click="clearSelected">清空</span>
</div>
<div class="org-items" style="height: 350px;">
<el-empty :image-size="100" description="请点击左侧列表选择数据" v-show="selectList.length === 0"/>
<div v-for="(selectItem, selectIndex) in selectList" :key="selectIndex" class="org-item">
<el-avatar :src="selectItem.avatar" style="margin-right: 5px;"></el-avatar>
{{ selectItem.name }}
<i class="el-icon-close" @click="noSelected(selectItem)"></i>
</div>
</div>
</div>
</div>
</div>
</w-dialog>
</template>
<script>
import {getUserTree} from "@/api/org";
export default {
name: "UserPicker",
props: {
value: {
type: Array,
default: () => {
return [];
}
},
multiple: { //
default: true,
type: Boolean
},
showCheckbox: { //
default: true,
type: Boolean
}
},
data() {
return {
radio: 0,
chooseId: 0,
selectItem: {
type: -1,
value: '0',
},
activeNames: ['1'],
visible: false,
loading: false,
title: "请选择",
selectList: [],
filterText: "",
dataList: [],
expandedKeys: [],
defaultProps: {
value: 'value',
label: 'name',
children: 'children',
isLeaf: function (data, node) {
return data.type === 0;
}
}
}
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
computed: {
_value: {
get() {
return this.value
},
set(val) {
this.$emit("input", val)
}
}
},
methods: {
radioChange(e) {
this.selectItem.type = -2
this.chooseId = 0
this.radio = e
this.expandedKeys = []
this.getList();
},
getList() {
getUserTree(this.radio, this.chooseId).then(res => {
// if (res.data) {
if (this.selectItem.type === -1 || this.selectItem.type === -2) {
this.dataList = res.data
} else if (this.selectItem.type === 1) {
this.selectItem.children = res.data
} else if (this.selectItem.type === 2) {
this.selectItem.children = res.data
}
// }
});
},
setData(source) {
// for (let item of source) {
// this.$set(item, "value", this.selectItem.value + "-" + item.id)
// }
return source;
},
//
// filterNode(value, data) {
// if (!value) return true;
// return data.name.indexOf(value) !== -1;
// },
//
showUserPicker() {
this.selectItem = {
type: -1,
value: '0',
}
this.dataList = []
// this.selectList = []
this.chooseId = 0
this.radio = 0
this.visible = true;
this.getList();
},
//
handleChange(item, data, node) {
this.selectItem = item
this.expandedKeys.push(item.value)
//getList()
if (node.expanded === false) {
if (item.type !== 0) {
this.chooseId = item.id
this.getList()
return
}
}
//
if (item.avatar !== null) {
if (this.multiple) {
//
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].id === item.id) {
this.selectList.splice(i, 1);
break;
}
}
this.selectList.push(item);
} else {
this.selectList = [item];
}
}
// this._value = this.selectList
},
//×
noSelected(selectItem) {
for (let i = 0; i < this.selectList.length; i++) {
if (this.selectList[i].value === selectItem.value) {
this.selectList.splice(i, 1);
break;
}
}
selectItem.selected = false;
},
//
clearSelected() {
this.$confirm("您确定要清空已选中的项?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.selectList = []
});
},
//
selectConfirm() {
this.$emit("ok", this.selectList);
// this.dataList = []
this.visible = false;
}
}
};
</script>
<style lang="less" scoped>
@containWidth: 278px;
/deep/ .el-tree-node {
.el-tree-node__children {
.el-tree-node {
.el-tree-node__content {
height: auto;
.tree-node{
.el-avatar{
}
}
}
}
}
}
.tree-node {
div {
.el-avatar {
width: 40px;
height: 40px;
margin-right: 5px;
}
}
}
/deep/ .el-tree-node {
.is-leaf + .el-checkbox .el-checkbox__inner {
display: inline-block;
}
.el-checkbox .el-checkbox__inner {
display: none;
}
}
/deep/ .el-dialog__body {
padding: 10px 20px;
}
.picker {
height: 402px;
position: relative;
text-align: left;
.candidate {
left: 0;
top: 0;
}
}
.candidate, .selected {
position: absolute;
display: inline-block;
width: @containWidth;
height: 400px;
border: 1px solid #e8e8e8;
}
.selected {
border-left: none;
right: 0;
top: 0;
.count {
width: calc(@containWidth - 20px);
padding: 10px;
display: inline-block;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 5px;
& > span:nth-child(2) {
float: right;
color: #c75450;
cursor: pointer;
}
}
}
.org-items {
overflow-y: auto;
height: 350px;
.el-icon-close {
position: absolute;
right: 5px;
cursor: pointer;
font-size: larger;
}
.org-item {
margin: 0 5px;
border-radius: 5px;
position: relative;
padding: 7px 5px;
display: flex;
align-items: center;
cursor: pointer;
&:hover {
background: #f1f1f1;
}
> span {
margin-left: 5px;
}
}
}
/deep/ .el-scrollbar .el-scrollbar__wrap {
overflow-x: hidden;
}
::-webkit-scrollbar {
float: right;
width: 4px;
height: 4px;
background-color: white;
}
::-webkit-scrollbar-thumb {
border-radius: 16px;
background-color: #efefef;
}
</style>

View File

@ -12,13 +12,13 @@ import "@/assets/theme.less";
import "@/assets/global.css";
import "@/assets/iconfont/iconfont.css"
import AvatarEllipsis from '@/components/common/AvatarEllipsis'
import Ellipsis from '@/components/common/Ellipsis'
import WDialog from '@/components/common/WDialog'
import Tip from '@/components/common/Tip'
import axios from "axios";
import {getBaseUrl} from "./api/request";
Vue.use(ElementUI);
Vue.use(AvatarEllipsis);
Vue.use(Ellipsis);
Vue.use(WDialog);
Vue.use(Tip);
@ -48,27 +48,13 @@ Vue.prototype.$deepCopy = function (obj) {
return JSON.parse(JSON.stringify(obj))
}
export function getToken() {
// let item = localStorage.getItem("token");
// if (item != null) {
// return
// }
axios.post(
// "http://security-react.mytwins.top/auth/login",
// getBaseUrl()+"/auth/login",
"http://gateway.mytwins.top/auth/login",
// "http://localhost:8000/auth/login",
{
code: "string",
password: "926425",
username: "admin",
uuid: "string"
}
).then(res => {
console.log(res)
localStorage.setItem("token", res.data.data)
})
}
import Pagination from "@/components/Pagination";
//自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
// 全局组件挂载
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
new Vue({
router,

View File

@ -33,6 +33,12 @@ const router = new Router({
component: () => import("@/views/admin/FormsPanel.vue"),
meta: {title: '表单列表', viewport: viewport}
},
{
path: "/query",
name: "query",
component: () => import("@/views/query/index.vue"),
meta: {title: '自定义查询', viewport: viewport}
},
{
path: "/admin/design",
name: "design",

View File

@ -8,12 +8,15 @@ export default new Vuex.Store({
state: {
nodeMap: new Map(),
parentMap: new Map(),
selectUserMap: new Map(),
isEdit: null,
selectedNode: {},
selectFormItem: null,
runningList: [],
endList: [],
noTakeList: [],
refuseList: [],
passList: [],
design:{},
},
mutations: {
@ -27,7 +30,11 @@ export default new Vuex.Store({
state.isEdit = val
}
},
getters: {},
getters: {
selectedNode(){
return state.selectedNode
}
},
actions: {},
modules: {}
})

View File

@ -1,14 +1,18 @@
<template>
<div style="text-align: center">
<!-- <h4>先选择本次登录人员的身份再进入相应的系统 😅</h4>-->
<!-- <h4>先选择本次登录人员的身份再进入相应的系统 😅</h4>-->
<h4>欢迎进入Spring Security 配套 Workflow工作流模块</h4>
<div v-if="!token">
<el-input v-model="username"/>
<el-button @click="getToken">登录</el-button>
</div>
<div class="work-panel">
<!-- <div class="user">-->
<!-- <el-button type="primary" round size="small" @click="showRolePicker()" icon="el-icon-user">选择本次登录者</el-button>-->
<!-- <div v-if="loginUser !== '' && loginUser !== null">-->
<!-- <span>{{ loginUser.name }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="user">-->
<!-- <el-button type="primary" round size="small" @click="showRolePicker()" icon="el-icon-user">选择本次登录者</el-button>-->
<!-- <div v-if="loginUser !== '' && loginUser !== null">-->
<!-- <span>{{ loginUser.name }}</span>-->
<!-- </div>-->
<!-- </div>-->
<div class="panel">
<div class="panel-item" @click="to('/workSpace')">
<div>
@ -32,8 +36,8 @@
</div>
<!-- <test type="user" ref="orgPicker" :v-model="select" :value="select" @dengjie="dengjie"-->
<!-- @ok="selected"></test>-->
<!-- <test type="user" ref="orgPicker" :v-model="select" :value="select" @dengjie="dengjie"-->
<!-- @ok="selected"></test>-->
</div>
@ -41,12 +45,15 @@
<script>
import Test from "@/components/common/Test";
import axios from "axios";
export default {
name: "Index",
components: {Test},
data() {
return {
token: localStorage.getItem("token"),
username: null,
select: [],
roleData: [
{
@ -78,9 +85,30 @@ export default {
}
},
methods: {
dengjie() {
console.log("==================================")
console.log(this.select)
getToken() {
// let item = localStorage.getItem("token");
// if (item != null) {
// return
// }
axios.post(
// "http://security-react.mytwins.top/auth/login",
// getBaseUrl()+"/auth/login",
"http://gateway.mytwins.top/auth/login",
// "http://localhost:8000/auth/login",
{
code: "string",
password: "926425",
// username: "admin",
username: this.username,
uuid: "string"
}
).then(res => {
console.log(res)
this.token = res.data.data
localStorage.setItem("token", this.token)
})
},

View File

@ -91,8 +91,7 @@
<el-radio-button label="E">编辑</el-radio-button>
<el-radio-button label="R">查看</el-radio-button>
</el-radio-group>
<!--todo 表单渲染-->
<form-render-view v-if="viewFormVisibleRender" ref="form" :form-items="forms" v-model="formData" :is-preview="true" :model="formPreviewModel"/>
<form-render-view v-if="viewFormVisibleRender" ref="form" :form-items="forms" v-model="formValue" :is-preview="true" :model="formPreviewModel"/>
</w-dialog>
</el-container>
</template>
@ -109,7 +108,7 @@ export default {
components: {draggable, FormComponentConfig, FormDesignRender, FormRenderView},
data() {
return {
formData: {},
// formData: {},
libSelect: 0,
viewFormVisible: false,
viewFormVisibleRender: true,
@ -205,8 +204,8 @@ export default {
this.formValue=this.$refs.form.value
})
for (const key in this.formValue) {
if(this.formValue[key]!=undefined){
this.formValue[key]=[]
if(this.formValue[key]!==undefined){
this.formValue[key]=undefined
}
}
},

View File

@ -30,7 +30,6 @@
<script>
import ProcessTree from './process/ProcessTree.vue'
// import ProcessTree from './process/ProcessTreeBack.vue'
import NodeConfig from '../../common/process/config/NodeConfig'
//
export default {

View File

@ -9,7 +9,7 @@
</div>
<el-main>
<div class="design" :style="'transform: scale('+ scale / 100 +');'">
<process-tree ref="process-tree-view"/>
<process-tree ref="process-tree"/>
</div>
</el-main>
</div>
@ -34,6 +34,21 @@ export default {
},
beforeDestroy() {
this.$store.state.diagramMode = "design";
},
methods:{
//
validate() {
return this.$refs["process-tree"].validateProcess()
},
validate_ASSIGN_USER(err) {
if (this.config.props.assignedUser.length > 0) {
return true;
} else {
this.errorInfo = '请指定审批人员'
err.push(`${this.config.name} 未指定审批人员`)
return false
}
},
}
};
</script>

View File

@ -1,6 +1,6 @@
<template>
<!--todo 预览时渲染的component-->
<component ref="form" :is="config.name" :mode="mode" :perm="perm" v-model="_value" v-bind="config.props"/>
<component ref="form" :is="config.name" :config="config" :mode="mode" :perm="perm" v-model="_value" v-bind="config.props"/>
</template>
<script>

View File

@ -30,23 +30,28 @@ export default {
return this.$store.state.parentMap;
},
dom() {
console.log(this.$store.state.design)
return this.$store.state.design.process;
},
viewer(){
viewer() {
return this.$store.state.diagramMode === 'viewer'
},
preview(){
preview() {
return this.$store.state.preview;
},
runningList(){
return this.$store.state.runningList
runningList() {
return this.$store.state.runningList
},
endList(){
return this.$store.state.endList
endList() {
return this.$store.state.endList
},
noTakeList(){
return this.$store.state.noTakeList
noTakeList() {
return this.$store.state.noTakeList
},
refuseList() {
return this.$store.state.refuseList
},
passList() {
return this.$store.state.passList
},
},
render(h, ctx) {
@ -64,17 +69,19 @@ export default {
return []
}
if (node.props && this.viewer && !this.preview) {
let headerBgc = '#ff943e'
if (this.runningList.includes(node.id)) {
headerBgc = '#1e90ff'
}
else if (this.endList.includes(node.id)) {
headerBgc = '#20b2aa'
}
else if (this.noTakeList.includes(node.id)) {
headerBgc = '#909399'
}
node.props.headerBgc = headerBgc
let headerBgc = '#ff943e'
if (this.runningList.includes(node.id)) {
headerBgc = '#1e90ff'
} else if (this.endList.includes(node.id)) {
headerBgc = '#20b2aa'
} else if (this.noTakeList.includes(node.id)) {
headerBgc = '#909399'
} else if (this.refuseList.includes(node.id)) {
headerBgc = '#f56c6c'
} else if (this.passList.includes(node.id)) {
headerBgc = '#ff943e'
}
node.props.headerBgc = headerBgc
}
if (this.isPrimaryNode(node)) {
//
@ -98,7 +105,7 @@ export default {
///
branchItems.unshift(h('div', {'class': {'add-branch-btn': true}}, [
h('el-button', {
'class': {'add-branch-btn-el': !this.viewer},
'class': {'add-branch-btn-el': !this.viewer},
props: {size: 'small', round: true},
on: {click: () => this.addBranchNode(node)},
domProps: {
@ -161,7 +168,7 @@ export default {
this.nodeMap.set(nodeItem.id, nodeItem)
this.parentMap.set(nodeItem.parentId, nodeItem)
})
}catch (e){
} catch (e) {
}
},
//idmap
@ -254,6 +261,7 @@ export default {
},
//
selectNode(node) {
console.log(node)
this.$store.commit('selectedNode', node)
console.log(node, "node")
if (!this.isConcurrentNode(node)) {

View File

@ -14,16 +14,17 @@ let Location = () => import('./components/Location.vue')
let MoneyInput = () => import('./components/MoneyInput.vue')
let DeptPicker = () => import('./components/DeptPicker.vue')
let UserPicker = () => import('./components/UserPicker.vue')
let SignPanel = () => import('./components/SignPannel.vue')
let RatePicker = () => import('./components/RatePicker.vue')
let SpanLayout = () => import('./components/SpanLayout.vue')
let TableList = () => import('./components/TableList.vue')
let SignPanel = () => import('./components/SignPanel.vue')
export default {
//基础组件
TextInput, NumberInput, AmountInput, TextareaInput, SelectInput, MultipleSelect,
DateTime, DateTimeRange, UserPicker, DeptPicker,
DateTime, DateTimeRange, UserPicker, DeptPicker,RatePicker,
//高级组件
Description, FileUpload, ImageUpload, MoneyInput, Location, SignPanel,
SpanLayout, TableList
SpanLayout, TableList,
}

View File

@ -6,6 +6,7 @@ export const ValueType = {
date: 'Date',
user: 'User',
dept: 'Dept',
star: 'star',
dateRange: 'DateRange'
}
@ -179,6 +180,22 @@ export const baseComponents = [
multiple: false
}
},
{
title: '评分',
name: 'RatePicker',
icon: 'el-icon-star-off',
value: '',
valueType: ValueType.star,
props: {
color: '#f0a732',
max: 5,
required: false,
enablePrint: true,
showScore: true,
enableHalf: true,
placeholder: undefined,
}
},
{
title: '说明文字',
name: 'Description',
@ -210,7 +227,20 @@ export const baseComponents = [
maxSize: 0, //最大条数为0则不限制
columns:[] //列设置
}
}
},
{
title: '签名',
name: 'SignPanel',
icon: 'el-icon-edit',
value: [],
valueType: ValueType.string,
props: {
required: false,
enablePrint: true,
isCrop: true,
lineColor: '#ff0000',
}
},
]
}
]

View File

@ -1,6 +1,9 @@
<template>
<div>
<el-form label-width="90px" v-if="form.name !== 'SpanLayout'">
<el-form-item label="表单ID">
<el-input size="small" clearable v-model="form.id" disabled/>
</el-form-item>
<el-form-item label="表单名称">
<el-input size="small" clearable v-model="form.title"/>
</el-form-item>
@ -14,7 +17,6 @@
</el-form>
<el-empty v-else description="当前组件不支持配置"></el-empty>
</div>
</template>
<script>
@ -33,6 +35,8 @@ import MoneyInput from './config/MoneyInputConfig.vue'
import DeptPicker from './config/OrgPickerConfig.vue'
import UserPicker from './config/OrgPickerConfig.vue'
import TableList from './config/TableListConfig.vue'
import SignPanel from './config/SignPanelConfig.vue'
import RatePicker from './config/RatePickerConfig.vue'
export default {
name: "FormComponentConfig",
@ -51,7 +55,9 @@ export default {
MoneyInput,
DeptPicker,
UserPicker,
TableList
TableList,
SignPanel,
RatePicker
},
props:{},
computed:{

View File

@ -36,7 +36,7 @@ export default {
rules: {},
}
},
mounted() {
created() {
this.loadFormConfig(this.formItems)
},
computed: {

View File

@ -1,5 +1,5 @@
<template>
<el-form ref="form" class="process-form" label-position="top" :rules="rules" :model="_value">
<el-form ref="formView" class="process-form" label-position="top" :rules="rules" :model="_value">
<div v-for="(item, index) in formItems" :key="item.name + index">
<el-form-item v-if="item.name !== 'SpanLayout' && item.name !== 'Description'"
:prop="item.id" :label="item.title">
@ -30,15 +30,15 @@ export default {
return {}
}
},
model:{
model: {
type: String,
default:() => {
default: () => {
return "R"
}
},
isPreview:{
isPreview: {
type: Boolean,
default:() => {
default: () => {
return false
}
}
@ -58,25 +58,52 @@ export default {
}
}
},
mounted() {
created() {
this.loadFormConfig(this.formItems)
},
methods: {
validate(call) {
console.log("我被执行了")
let success = true
this.$refs.formView.validate(valid => {
success = valid
if(valid){
//
for (let i = 0; i < this.formItems.length; i++) {
if (this.formItems[i].name === 'TableList'){
let formRef = this.$refs[`sub-item_${this.formItems[i].id}`]
if (formRef && Array.isArray(formRef) && formRef.length > 0){
formRef[0].validate(subValid => {
success = subValid
})
if (!success){
break
}
}
}
}
}
call(success)
});
},
loadFormConfig(formItems) {
formItems.forEach(item => {
if (item.name === 'SpanLayout') {
this.loadFormConfig(item.props.items)
} else {
this.$set(this._value, item.id, this.value[item.id])
if (this.isPreview){
if (this.isPreview) {
this.$set(item, 'perm', this.model)
}
if (item.props.required && this.model === "E") {
this.$set(this.rules, item.id, [{
type: item.valueType === 'Array' ? 'array' : undefined,
required: true,
message: `请填写${item.title}`, trigger: 'blur'
}])
if (item.perm === 'E') {
if (item.props.required) {
this.$set(this.rules, item.id, [{
type: item.valueType === 'Array' ? 'array' : undefined,
required: true,
message: `请填写${item.title}`, trigger: 'blur'
}])
}
}
}
})
@ -90,6 +117,7 @@ export default {
margin: 15px 0;
padding: 10px 10px 1px 10px;
background: #fff;
/deep/ .el-form-item__label {
padding: 0 0;
}
@ -98,6 +126,7 @@ export default {
padding-left: 5px;
line-height: 30px;
}
/deep/ .el-form-item {
margin-bottom: 10px;
}

View File

@ -0,0 +1,81 @@
<template>
<div>
<template v-if="mode === 'DESIGN'">
<el-rate
disabled
:show-score="showScore"
:allow-half="enableHalf"
:text-color="color"
text-color="#ff9900"
score-template="{value}">
</el-rate>
<p>{{ placeholder }}</p>
</template>
<template v-else>
<template v-if="perm === 'E'">
<el-rate
v-model="_value"
:show-score="showScore"
:allow-half="enableHalf"
:text-color="color"
score-template="{value}">
</el-rate>
<p>{{ placeholder }}</p>
</template>
<template v-else-if="perm === 'R'">
<el-rate
v-model="_value"
disabled
:show-score="showScore"
:allow-half="enableHalf"
:text-color="color"
score-template="{value}">
</el-rate>
</template>
</template>
</div>
</template>
<script>
import componentMinxins from '../ComponentMinxins'
export default {
mixins: [componentMinxins],
name: "RatePicker",
props: {
value: {
type: Number,
default: 0
},
perm: {
type: String,
default: 'E'
},
color: {
type: String,
default: '#f0a732'
},
placeholder: {
type: String,
default: '请打分!'
},
enableHalf: {
type: Boolean,
default: true
},
showScore: {
type: Boolean,
default: true
},
}
}
</script>
<style scoped>
p{
background-color: #fff;
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div>
<template v-if="mode === 'DESIGN'">
<el-icon class="el-icon-location-information"/>
请签名
</template>
<template v-else>
<template v-if="perm === 'E'">
<div v-if="!_value">
<el-button icon="el-icon-edit" @click="doSign">请签字</el-button>
</div>
<div v-else>
<div>
<el-image fit="contain" class="sign-border-preview" style="width: 200px; height: 100px" @click="doSign"
:src="_value"/>
</div>
<div>
点击签名重签
</div>
</div>
</template>
<template v-else-if="perm === 'R'">
<div v-if="_value">
<el-image fit="contain" style="width: 200px; height: 100px" :src="_value"/>
</div>
<div>
请签名
</div>
</template>
</template>
<el-dialog title="请使用鼠标签字" width="800px" :visible.sync="sign.open" @close="signClose" append-to-body>
<div class="sign-border">
<vue-esign v-if="sign.open" ref="sign" :width="800" :height="300" :isCrop="isCrop"
:lineWidth="sign.lineWidth" :lineColor="sign.lineColor"
:bgColor.sync="sign.bgColor"/>
</div>
<div slot="footer">
<el-button size="mini" @click="signClose">取消</el-button>
<el-button size="mini" type="primary" @click="handleGenerate">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import vueEsign from 'vue-esign'
import componentMinxins from '../ComponentMinxins'
export default {
mixins: [componentMinxins],
name: "SignPanel",
components: {vueEsign},
props: {
value: {
type: String,
default: null
},
perm: {
type: String,
default: 'E'
},
isCrop: {
type: Boolean,
default: false
},
lineColor: {
type: String,
default: '#000000'
}
},
data() {
return {
sign: {
open: false,
lineWidth: 6,
bgColor: '',
}
}
},
methods: {
signClose() {
this.sign.open = false
this.$refs.sign.reset()
},
doSign() {
this.sign.open = true
},
handleGenerate() {
this.$refs.sign.generate().then(res => {
this._value = res
this.sign.open = false
}).catch(err => {
alert(err) // 'Not Signned'
})
}
}
}
</script>
<style scoped>
.sign-border {
border: 1px dashed #2b2b2b;
}
.sign-border-preview {
border: 1px solid #ffffff;
}
.sign-border-preview:hover {
border: 1px dashed #1989fa;
}
</style>

View File

@ -1,18 +0,0 @@
<template>
</template>
<script>
export default {
name: "SignPannel",
components: {},
data() {
return {}
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@ -2,37 +2,50 @@
<div style="max-width: 350px">
<template v-if="mode === 'DESIGN'">
<el-button disabled icon="el-icon-user" type="primary" size="mini" round>选择人员</el-button>
<span class="placeholder"> {{placeholder}}</span>
<span class="placeholder"> {{ placeholder }}</span>
</template>
<template v-else>
<template v-if="perm === 'E'">
<el-button icon="el-icon-user" type="primary" size="mini" round @click="$refs.orgPicker.show()">选择人员</el-button>
<org-picker type="user" :multiple="multiple" ref="orgPicker" :selected="_value" @ok="selected"/>
<span class="placeholder"> {{placeholder}}</span>
<div style="margin-top: 5px">
<el-tag size="mini" style="margin: 5px" closable v-for="(dept, i) in _value" :key="i" @close="delDept(i)">{{dept.name}}</el-tag>
<el-button icon="el-icon-user" type="primary" size="mini" round @click="$refs.userPicker.showUserPicker()">
选择人员
</el-button>
<user-select :multiple="multiple" ref="userPicker" title="请选择系统用户" :v-mode="_value" @ok="selected"/>
<span class="placeholder"> {{ placeholder }}</span>
<div style="display: flex;">
<div class="userStyle" v-for="(user, i) in _value" :key="i">
<span @click="delDept(i)">×</span>
<el-avatar :src="user.avatar"/>
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
<span>{{ user.name }}</span>
</el-tooltip>
</div>
</div>
</template>
<template v-else-if="perm === 'R'">
{{ _value }}
<div style="display: flex;">
<div v-for="(user, i) in _value" :key="i" class="view_user">
<el-avatar :src="user.avatar"/>
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
<span>{{ user.name }}</span>
</el-tooltip>
</div>
</div>
</template>
</template>
</div>
</template>
<script>
import componentMinxins from '../ComponentMinxins'
import OrgPicker from "@/components/common/Test";
import UserSelect from "@/components/common/UserPicker";
export default {
mixins: [componentMinxins],
name: "DeptPicker",
components: {OrgPicker},
name: "UserPicker",
components: {UserSelect},
props: {
value:{
value: {
type: Array,
default: () => {
return []
@ -46,32 +59,75 @@ export default {
type: String,
default: '请选择人员'
},
multiple:{
config: {
type: Object,
default: () => {
return {}
}
},
multiple: {
type: Boolean,
default: false
}
},
data() {
return {
showOrgSelect: false
showPickerSelect: false
}
},
methods: {
selected(values){
this.showOrgSelect = false
selected(values) {
this.showPickerSelect = false
this._value = values
},
delDept(i){
delDept(i) {
this._value.splice(i, 1)
}
}
}
</script>
<style scoped>
.placeholder{
<style scoped lang="less">
.placeholder {
margin-left: 10px;
color: #adabab;
font-size: smaller;
}
.userStyle {
width: 45px;
margin-top: 10px;
display: flex;
flex-direction: column;
position: relative;
margin-right: 10px;
span:first-child {
position: absolute;
right: -3px;
top: -11px;
cursor: pointer;
}
span:last-child {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden
}
}
.view_user {
width: 45px;
display: flex;
flex-direction: column;
margin-right: 10px;
span {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden
}
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div>
<el-form-item label="提示文字">
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
</el-form-item>
<el-form-item label="最大分值">
<el-input-number size="small" :min="1" v-model="value.max" />
</el-form-item>
<el-form-item label="允许半分">
<el-switch v-model="value.enableHalf"></el-switch>
</el-form-item>
<el-form-item label="显示分值">
<el-switch v-model="value.showScore"></el-switch>
</el-form-item>
</div>
</template>
<script>
export default {
name: "RatePickerConfig",
components: {},
props:{
value:{
type: Object,
default: ()=>{
return {}
}
}
},
data() {
return {}
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<div>
<el-form-item label="是否裁剪">
<el-switch v-model="value.isCrop"></el-switch>
</el-form-item>
</div>
</template>
<script>
export default {
name: "SignPanelConfig",
components: {},
props:{
value:{
type: Object,
default: ()=>{
return {}
}
}
},
data() {
return {}
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,267 @@
<template>
<div style="margin: 10px">
<el-timeline>
<el-timeline-item v-for="(operation,index) in operationList"
:key="index" :timestamp="operation.startTime"
:icon="operation.icon"
:color="operation.color"
size="large"
placement="top">
<el-card>
<div style="display: flex;">
<div v-for="(user,index) in operation.userInfo" :key="index" class="avatar_name">
<el-avatar size="large" :src="user.avatar"></el-avatar>
<!--v-if="!$slots.dot && operation.userInfo.length > 1" -->
<div v-if="user.icon"
class="el-timeline-item__node" :style="{
backgroundColor: user.color
}">
<i v-if="user.icon"
class="el-timeline-item__icon"
:class="user.icon"
></i>
</div>
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
<span class="username">{{ user.name }}</span>
</el-tooltip>
</div>
<div style="margin-left: 10px;">
<div style="color: #c0bebe">{{ operation.operationName }}</div>
<div style="font-size: 14px; font-weight: bold;">{{ operation.remark }}</div>
</div>
</div>
<template v-if="operation.comment">
<div style="margin-top: 10px;background:#f5f5f5;padding: 10px;">
<div>
{{ operation.comment.context }}
</div>
<div style="margin-top: 10px;" v-if="operation.comment.attachments && operation.comment.attachments.length > 0">
<template v-for="(item) in getAttachmentList(operation.comment.attachments,true)">
<el-image
style="width: 100px; height: 100px"
:src="item.url"
:preview-src-list="[item.url]">
</el-image>
</template>
<div v-for="(file) in getAttachmentList(operation.comment.attachments,false)">
<el-link style="color: #2a99ff" :href="file.url" icon="el-icon-document">{{ file.name }}</el-link>
</div>
</div>
</div>
</template>
</el-card>
</el-timeline-item>
<el-timeline-item :color="timeline.color" :icon="timeline.icon" size="large">
<el-card style="font-size: 16px;font-weight: bold;">
{{ timeline.context }}
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</template>
<script>
export default {
name: "OperationRender",
props: {
operationList: {
type: Array,
default: () => {
return []
}
},
state: {
type: String,
default: () => {
return '1'
}
}
},
data() {
return {
timeline: {},
}
},
created() {
this.init()
},
methods: {
init() {
switch (this.state) {
case '1':
this.timeline = {
color: '#f78f5f',
icon: 'el-icon-more',
context: '审批进行中'
}
break
case '2':
this.timeline = {
color: '#0bbd87',
icon: 'el-icon-check',
context: '审批通过'
}
break
case '3':
this.timeline = {
color: '#f56c6c',
icon: 'el-icon-close',
context: '审核已驳回'
}
break
case '4':
this.timeline = {
color: '#0bbd87',
icon: 'el-icon-check',
context: '审批通过'
}
break
default:
break
}
// let operationListNew = []
for (let i = 0;i<this.operationList.length;i++) {
let operationNew = this.initOperationFun(this.operationList[i])
let userList = []
if (operationNew.userInfo){
for (let user of operationNew.userInfo) {
let userNew = this.initUser(user)
userList.push(userNew)
}
operationNew.userInfo = userList
}
// operationListNew.push(operationNew)
// this.operationList.push(operationNew)
this.operationList[i] = operationNew
}
},
//
getAttachmentList(attachments, image) {
let result = [];
console.log(attachments)
if (attachments){
for (let attachment of attachments) {
if (attachment.isImage === image) {
result.push(attachment)
}
}
}
return result;
},
initUser(user) {
let state = user.state
//
if (state === 'CREATE') {
this.$set(user, "icon", "el-icon-check")
this.$set(user, "color", "#0bbd87")
}
//
if (state === 'AGREE' || state === 'AUTO_PASS') {
this.$set(user, "icon", "el-icon-check")
this.$set(user, "color", "#0bbd87")
}
//
if (state === 'RUNNING') {
this.$set(user, "icon", "el-icon-loading")
this.$set(user, "color", "#f78f5f")
}
//
if (state === 'REFUSE') {
this.$set(user, "icon", "el-icon-close")
this.$set(user, "color", "#f56c6c")
}
if (state === 'PASS'){
this.$set(user, "icon", "el-icon-more")
this.$set(user, "color", "#c0c4cc")
}
return user;
},
initOperationFun(operation) {
let state = operation.state
let type = operation.operation
//
if (state === 'CREATE') {
this.$set(operation, "icon", "el-icon-check")
this.$set(operation, "color", "#0bbd87")
}
//
if (state === 'AGREE' || state === 'AUTO_PASS') {
this.$set(operation, "icon", "el-icon-check")
this.$set(operation, "color", "#0bbd87")
this.$set(operation, "remark", ' (已同意)')
}
if (state === 'PASS'){
this.$set(operation, "icon", "el-icon-more")
this.$set(operation, "color", "#c0c4cc")
}
//
if (state === 'RUNNING') {
this.$set(operation, "icon", "el-icon-loading")
this.$set(operation, "color", "#f78f5f")
this.$set(operation, "remark",' (处理中)')
}
//退
if (state === 'ROLLBACK') {
this.$set(operation, "icon", "el-icon-refresh")
this.$set(operation, "color", "#f78f5f")
this.$set(operation, "remark", ' (回退成功)')
}
//
if (type === 'CC') {
this.$set(operation, "icon", "el-icon-s-promotion")
this.$set(operation, "color", "#3395f8")
this.$set(operation, "remark", ' (抄送成功)')
}
//
if (state === 'COMMENT') {
this.$set(operation, "icon", "el-icon-chat-dot-round")
this.$set(operation, "color", "#0bbd87")
this.$set(operation, "remark", ' (添加了评论)')
}
//
if (state === 'REFUSE' && type === 'COMMENT') {
this.$set(operation, "icon", "el-icon-chat-dot-round")
this.$set(operation, "color", "#f56c6c")
this.$set(operation, "remark", ' (填写拒绝理由)')
}
//
if ((state === 'REFUSE' || state === 'AUTO_REFUSE')&& type === 'OPINION') {
this.$set(operation, "icon", "el-icon-close")
this.$set(operation, "color", "#f56c6c")
this.$set(operation, "remark", ' (拒绝)')
}
return operation;
},
}
}
</script>
<style scoped>
/deep/ .el-card__body, .el-main {
padding: 10px;
}
.avatar_name{
width: 45px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
margin-right: 5px;
}
.el-timeline-item__node{
position: absolute;
bottom: 20px;
right: 1px;
}
.username{
width: 45px;
padding-top: 2px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden
}
</style>

View File

@ -7,7 +7,7 @@
</el-radio-group>
<div v-if="nodeProps.assignedType === 'ASSIGN_USER'">
<el-button size="mini" icon="el-icon-plus" type="primary" @click="selectUser" round>选择人员</el-button>
<org-items v-model="nodeProps.assignedUser"/>
<avatar-ellipsis :row="3" :user-info="nodeProps.assignedUser"/>
</div>
<div v-else-if="nodeProps.assignedType === 'SELF_SELECT'">
<el-radio-group size="mini" v-model="nodeProps.selfSelect.multiple">
@ -118,8 +118,8 @@
</el-form-item>
<el-form-item label="🙅‍ 如果审批被驳回 👇">
<el-radio-group v-model="nodeProps.refuse.type">
<el-radio label="TO_END">直接结束流程</el-radio>
<!-- <el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>-->
<el-radio label="TO_INITIAL">直接结束流程</el-radio>
<el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>
<!-- <el-radio label="TO_NODE">驳回到指定节点</el-radio>-->
</el-radio-group>
<div v-if="nodeProps.refuse.type === 'TO_NODE'">
@ -133,19 +133,22 @@
</el-form-item>
</el-form>
<role-picker :title="pickerTitle" multiple ref="rolePicker" :v-model="select" @ok="selectedRole"/>
<role-picker title="请选择人员" multiple ref="rolePicker" :v-model="roleList" @ok="selectedRole"/>
<user-picker title="请选择系统角色" multiple ref="userPicker" :v-model="assignedUser" @ok="selectedUser"/>
</div>
</template>
<script>
import RolePicker from "@/components/common/RolePicker";
import UserPicker from "@/components/common/UserPicker";
import RoleItems from "../RoleItems";
import OrgItems from "../OrgItems";
export default {
name: "ApprovalNodeConfig",
components: {RoleItems, OrgItems, RolePicker},
components: {RoleItems, OrgItems, RolePicker, UserPicker},
props: {
config: {
type: Object,
@ -158,7 +161,6 @@ export default {
return {
showOrgSelect: false,
orgPickerSelected: [],
orgPickerType: "user",
approvalTypes: [
{name: "指定人员", type: "ASSIGN_USER"},
{name: "发起人自选", type: "SELF_SELECT"},
@ -174,27 +176,27 @@ export default {
nodeProps() {
return this.$store.state.selectedNode.props;
},
select() {
return this.config.assignedUser || [];
assignedUser: {
get() {
return this.config.assignedUser || [];
},
set(val) {
this.config.assignedUser = val
}
},
roleList() {
return this.config.roleList || [];
roleList:{
get() {
return this.config.roleList || [];
},
set(val) {
this.config.roleList = val
}
},
forms() {
return this.$store.state.design.formItems.filter(f => {
return f.name === "UserPicker";
});
},
pickerTitle() {
switch (this.orgPickerType) {
case "user":
return "请选择人员";
case "role":
return "请选择系统角色";
default:
return null;
}
},
nodeOptions() {
let values = [];
const excType = ["ROOT", "EMPTY", "CONDITION", "CONDITIONS", "CONCURRENT", "CONCURRENTS"];
@ -224,34 +226,30 @@ export default {
},
methods: {
selectUser() {
this.orgPickerSelected = this.select;
this.orgPickerType = "user";
this.$refs.orgPicker.show();
this.$refs.userPicker.showUserPicker()
},
selectNoSetUser() {
this.orgPickerSelected = this.config.nobody.assignedUser;
this.orgPickerType = "user";
this.$refs.orgPicker.show();
},
//,
selectRole() {
this.orgPickerType = "role";
this.$refs.rolePicker.showRolePicker();
},
selectedRole(select) {
select.forEach(val => {
for (let i = 0; i < this.roleList.length; i++) {
if (this.roleList[i].roleId === val.roleId) {
this.roleList.splice(i, 1);
break;
}
}
this.roleList.push(val);
});
this.roleList = select
},
selectedUser(select) {
let userInfoList = []
for (let val of select) {
let userInfo = {
id: val.id,
name: val.name,
avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.assignedUser = userInfoList
},
removeOrgItem(index) {
this.select.splice(index, 1);
}
}
};
</script>

View File

@ -1,21 +1,21 @@
<template>
<div>
<el-button size="mini" icon="el-icon-plus" type="primary" @click="selectOrg" round>选择抄送人</el-button>
<el-button size="mini" icon="el-icon-plus" type="primary" @click="selectUser" round>选择抄送人</el-button>
<div class="option">
<el-checkbox label="允许发起人添加抄送人" v-model="config.shouldAdd"></el-checkbox>
<el-checkbox label="允许发起人添加抄送人" v-model="shouldAdd"></el-checkbox>
</div>
<org-items v-model="select"/>
<org-picker multiple ref="orgPicker" :selected="select" @ok="selected"/>
<!-- <org-items v-model="select"/>-->
<avatar-ellipsis :row="3" :user-info="assignedUser"/>
<user-picker title="请选择系统角色" multiple ref="userPicker" :v-model="assignedUser" @ok="selectedUser"/>
</div>
</template>
<script>
import OrgPicker from "@/components/common/Test";
import OrgItems from "../OrgItems";
import UserPicker from "@/components/common/UserPicker";
export default {
name: "CcNodeConfig.vue",
components: {OrgPicker, OrgItems},
components: {UserPicker},
props:{
config:{
type: Object,
@ -25,7 +25,16 @@ export default {
}
},
computed:{
select: {
shouldAdd:{
get(){
return this.config.shouldAdd || false
},
set(val){
console.log("asdjhajdhasjdhasdhasjdad")
this.config.shouldAdd = val
}
},
assignedUser: {
get(){
return this.config.assignedUser || []
},
@ -38,16 +47,21 @@ export default {
return {}
},
methods: {
selectOrg() {
this.$refs.orgPicker.show()
selectUser() {
this.$refs.userPicker.showUserPicker()
},
selected(select) {
console.log(select)
this.select = Object.assign([], select)
selectedUser(select) {
let userInfoList = []
for (let val of select) {
let userInfo = {
id: val.id,
name: val.name,
avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.assignedUser = userInfoList
},
removeOrgItem(index){
this.select.splice(index, 1)
}
}
}
</script>

View File

@ -1,5 +1,8 @@
<template>
<node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
<node :title="config.name" :show-error="showError" :content="content"
:show-avatar="config.props.assignedType === 'ASSIGN_USER'" :user-info="assignedUser"
:error-info="errorInfo"
:select-user="selectUser"
@selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
placeholder="请设置审批人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/>
</template>
@ -9,8 +12,8 @@ import Node from './Node'
export default {
name: "ApprovalNode",
props:{
config:{
props: {
config: {
type: Object,
default: () => {
return {}
@ -24,120 +27,136 @@ export default {
errorInfo: '',
}
},
computed:{
viewer(){
computed: {
selectUser() {
return {
show: this.config.props.assignedType === 'SELF_SELECT',
multiple: this.config.props.selfSelect.multiple
};
},
assignedUser() {
if (this.config.props.assignedType === 'SELF_SELECT') {
this.config.props.assignedUser = []
}
return this.config.props.assignedUser;
},
viewer() {
return this.$store.state.diagramMode === 'viewer'
},
preview(){
preview() {
return this.$store.state.preview;
},
headerBgc() {
if (this.preview || !this.viewer){
if (this.preview || !this.viewer) {
return '#ff943e'
}else {
} else {
return this.config.props.headerBgc
}
},
content(){
content() {
const config = this.config.props
switch (config.assignedType){
switch (config.assignedType) {
case "ASSIGN_USER":
if (config.assignedUser.length > 0){
if (config.assignedUser.length > 0) {
let texts = []
config.assignedUser.forEach(org => texts.push(org.name))
return String(texts).replaceAll(',', '、')
}else {
} else {
return '请指定审批人'
}
case "SELF":
return '发起人自己'
case "SELF_SELECT":
return config.selfSelect.multiple ? '发起人自选多人':'发起人自选一人'
return config.selfSelect.multiple ? '发起人自选多人' : '发起人自选一人'
case "LEADER_TOP":
return '多级主管依次审批'
case "LEADER":
return config.leader.level > 1 ? '发起人的第 ' + config.leader.level + ' 级主管' : '发起人的直接主管'
case "FORM_USER":
if (!config.formUser || config.formUser === ''){
if (!config.formUser || config.formUser === '') {
return '表单内联系人(未选择)'
}else {
} else {
let text = this.getFormItemById(config.formUser)
if (text && text.title){
if (text && text.title) {
return `表单(${text.title})内的人员`
}else {
} else {
return '该表单已被移除😥'
}
}
case "ROLE":
if (config.roleList.length > 0){
return config.roleList.map(role=>{
if (config.roleList.length > 0) {
return config.roleList.map(role => {
return role.roleName;
}).join("、")
}else {
} else {
return '指定角色(未设置)'
}
default: return '未知设置项😥'
default:
return '未知设置项😥'
}
}
},
methods: {
getFormItemById(id){
getFormItemById(id) {
return this.$store.state.design.formItems.find(item => item.id === id)
},
//
validate(err){
validate(err) {
try {
this.showError = !this[`validate_${this.config.props.assignedType}`](err)
if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) {
this.errorInfo = '审批人为空时, 转交给指定人员:【请指定一个具体的人】'
err.push('审批人为空时, 转交给指定人员:【请指定一个具体的人】')
this.showError = true
} else if (this.viewer) {
this.showError = !this.validate_ASSIGN_USER(err)
console.log(this.showError)
}
return this.showError
} catch (e) {
console.log(e)
return true;
}
},
validate_ASSIGN_USER(err){
if(this.config.props.assignedUser.length > 0){
validate_ASSIGN_USER(err) {
if (this.config.props.assignedUser.length > 0) {
return true;
}else {
} else {
this.errorInfo = '请指定审批人员'
err.push(`${this.config.name} 未指定审批人员`)
return false
}
},
validate_SELF_SELECT(err){
validate_SELF_SELECT(err) {
return true;
},
validate_LEADER_TOP(err){
validate_LEADER_TOP(err) {
return true;
},
validate_LEADER(err){
validate_LEADER(err) {
return true;
},
validate_ROLE(err){
if (this.config.props.roleList.length <= 0){
validate_ROLE(err) {
if (this.config.props.roleList.length <= 0) {
this.errorInfo = '请指定负责审批的系统角色'
err.push(`${this.config.name} 未指定审批角色`)
return false
}
return true;
},
validate_SELF(err){
validate_SELF(err) {
return true;
},
validate_FORM_USER(err){
if (this.config.props.formUser === ''){
validate_FORM_USER(err) {
if (this.config.props.formUser === '') {
this.errorInfo = '请指定表单中的人员组件'
err.push(`${this.config.name} 审批人为表单中人员,但未指定`)
return false
}
return true;
},
validate_REFUSE(err){
validate_REFUSE(err) {
return true;
},
}

View File

@ -1,5 +1,6 @@
<template>
<node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
<node :title="config.name" :show-error="showError" :select-user="selectUser" :content="content"
:error-info="errorInfo" :show-avatar="true" :user-info="config.props.assignedUser"
@selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
placeholder="请设置抄送人" :header-bgc="headerBgc" header-icon="el-icon-s-promotion"/>
</template>
@ -9,8 +10,8 @@ import Node from './Node'
export default {
name: "CcNode",
props:{
config:{
props: {
config: {
type: Object,
default: () => {
return {}
@ -24,24 +25,30 @@ export default {
errorInfo: '',
}
},
computed:{
viewer(){
computed: {
selectUser() {
return {
show: this.config.props.assignedType !== 'ASSIGN_USER',
multiple: true
};
},
viewer() {
return this.$store.state.diagramMode === 'viewer'
},
preview(){
preview() {
return this.$store.state.preview;
},
headerBgc() {
if (this.preview || !this.viewer){
if (this.preview || !this.viewer) {
return '#3296fa'
}else {
} else {
return this.config.props.headerBgc
}
},
content() {
if (this.config.props.shouldAdd){
if (this.config.props.shouldAdd) {
return '由发起人指定'
}else if (this.config.props.assignedUser.length > 0) {
} else if (this.config.props.assignedUser.length > 0) {
let texts = []
this.config.props.assignedUser.forEach(org => texts.push(org.name))
return String(texts).replaceAll(',', '、')
@ -52,15 +59,15 @@ export default {
},
methods: {
//
validate(err){
validate(err) {
this.showError = false
if(this.config.props.shouldAdd){
if (this.config.props.shouldAdd) {
this.showError = false
}else if(this.config.props.assignedUser.length === 0){
} else if (this.config.props.assignedUser.length === 0) {
this.showError = true
this.errorInfo = '请选择需要抄送的人员'
}
if (this.showError){
if (this.showError) {
err.push(`抄送节点 ${this.config.name} 未设置抄送人`)
}
return !this.showError

View File

@ -1,7 +1,7 @@
<template>
<div class="node">
<div class="node">
<!-- 并行分支选择后右侧出现操作面板,占时不需要 <div class="node-body" @click="$emit('selected')">-->
<div class="node-body" @click="$emit('selected')">
<div v-if="designStart()" class="node-body" @click="$emit('selected')">
<div class="node-body-left" @click.stop="$emit('leftMove')" v-if="level > 1 && designStart() ">
<i class="el-icon-arrow-left"></i>
</div>
@ -27,7 +27,7 @@
</div>
</div>
<div class="node-footer">
<div class="btn">
<div class="btn" :style="(designStart() ? '' : 'height:0px')">
<insert-button v-if="designStart()" @insertNode="type => $emit('insertNode', type)"></insert-button>
</div>
</div>

View File

@ -1,17 +1,29 @@
<template>
<div :class="{'node': true, 'root': isRoot || !show, 'node-error-state': showError}">
<div v-if="show" @click="$emit('selected')" :class="{'node-body': true, 'error': showError}" >
<div v-if="show" @click="$emit('selected')" :class="{'node-body': true, 'error': showError}">
<div>
<div class="node-body-header" :style="{'background-color': headerBgc}">
<i :class="headerIcon" style="margin-right: 5px" v-if="(headerIcon || '') !== ''"></i>
<ellipsis class="name" hover-tip :content="title"/>
<i class="el-icon-close" v-if="!isRoot && $store.state.diagramMode !== 'viewer'" style="float:right;" @click="$emit('delNode')"></i>
<i class="el-icon-close" v-if="!isRoot && $store.state.diagramMode !== 'viewer'" style="float:right;"
@click="$emit('delNode')"></i>
</div>
<div class="node-body-content">
<i :class="leftIcon" v-if="leftIcon"></i>
<span class="placeholder" v-if="(content || '').trim() === ''">{{placeholder}}</span>
<ellipsis :row="3" :content="content" v-else/>
<i class="el-icon-arrow-right" v-if="$store.state.diagramMode !== 'viewer'" ></i>
<template v-if="viewer && selectUser.show">
<div class="avatar_button">
<avatar-ellipsis :row="3" v-if="userInfo.length > 0" :user-info="userInfo"/>
<el-button type="primary" icon="el-icon-plus" circle @click="$refs.userPicker.showUserPicker()"/>
</div>
</template>
<template v-else-if="showAvatar">
<span class="placeholder" v-if="userInfo.length === 0">{{ placeholder }}</span>
<avatar-ellipsis :row="3" :user-info="userInfo" v-else/>
</template>
<template v-else>
<span class="placeholder" v-if="(content || '').trim() === ''">{{ placeholder }}</span>
<ellipsis :row="3" :content="content" v-else/>
</template>
</div>
<div class="node-error" v-if="showError">
<el-tooltip effect="dark" :content="errorInfo" placement="top-start">
@ -21,21 +33,28 @@
</div>
</div>
<div class="node-footer">
<div v-if="merge" class="branch-merge"><img data-v-1e7b1da5="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg==" alt=""></div>
<div v-if="merge" class="branch-merge">
<img data-v-1e7b1da5=""
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg=="
alt=""></div>
<div class="btn">
<insert-button v-show="$store.state.diagramMode !== 'viewer'" @insertNode="type => $emit('insertNode', type)"></insert-button>
<insert-button v-show="$store.state.diagramMode !== 'viewer'"
@insertNode="type => $emit('insertNode', type)"></insert-button>
</div>
</div>
<user-picker v-if="selectUser.show" title="请选择系统用户" :multiple="selectUser.multiple" ref="userPicker"
@ok="selectedUser"/>
</div>
</template>
<script>
import UserPicker from "@/components/common/UserPicker";
import InsertButton from '@/views/common/InsertButton.vue'
export default {
name: "Node",
components: {InsertButton},
props:{
components: {InsertButton, UserPicker},
props: {
//
isRoot: {
type: Boolean,
@ -56,11 +75,25 @@ export default {
type: String,
default: ""
},
title:{
//
showAvatar: {
type: Boolean,
default: false
},
selectUser: {
type: Object,
default() {
return {
show: false,
multiple: false,
}
}
},
title: {
type: String,
default: "标题"
},
placeholder:{
placeholder: {
type: String,
default: "请设置"
},
@ -70,49 +103,73 @@ export default {
default: undefined
},
//
headerIcon:{
headerIcon: {
type: String,
default: ''
},
//
headerBgc:{
headerBgc: {
type: String,
default: '#576a95'
},
//
showError:{
showError: {
type: Boolean,
default: false
},
errorInfo:{
errorInfo: {
type: String,
default: '无信息'
},
},
data() {
return {}
return {
userInfo:[]
}
},
methods: {}
computed: {
viewer() {
return this.$store.state.diagramMode === 'viewer'
}
},
methods: {
selectedUser(select) {
let userInfoList = []
for (let val of select) {
let userInfo = {
id: val.id,
name: val.name,
avatar: val.avatar,
}
userInfoList.push(userInfo)
}
this.userInfo = userInfoList
this.$store.state.selectUserMap.set(this.$store.state.selectedNode.id, userInfoList)
},
}
}
</script>
<style lang="less" scoped>
.root{
&:before{
.root {
&:before {
display: none !important;
}
}
.node-error-state{
.node-body{
.node-error-state {
.node-body {
box-shadow: 0px 0px 5px 0px #F56C6C !important;
}
}
.node{
.node {
padding: 0 50px;
width: 220px;
position: relative;
&:before{
&:before {
content: '';
position: absolute;
top: -12px;
@ -125,15 +182,18 @@ export default {
border-color: #CACACA transparent transparent;
background: #F5F5F7;
}
.node-body{
.node-body {
cursor: pointer;
max-height: 120px;
min-height: 63px;
position: relative;
border-radius: 5px;
background-color: white;
box-shadow: 0px 0px 5px 0px #d8d8d8;
&:hover{
&:hover {
box-shadow: 0px 0px 3px 0px @theme-primary;
.node-body-header {
.el-icon-close {
display: inline;
@ -141,36 +201,56 @@ export default {
}
}
}
.node-body-header{
.node-body-header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
padding: 5px 15px;
color: white;
font-size: xx-small;
.el-icon-close{
.el-icon-close {
display: none;
}
.name{
.name {
height: 14px;
width: 150px;
display: inline-block
}
}
.node-body-content{
.node-body-content {
padding: 18px;
color: #656363;
font-size: 14px;
i{
.avatar_button {
//float: left;
display: flex;
//flex: 1;
flex-wrap: wrap;
button {
margin-top: 3px;
height: 40px;
//flex-shrink: 0;
//flex-grow: 0;
}
}
i {
position: absolute;
top: 55%;
right: 5px;
font-size: medium;
}
.placeholder{
.placeholder {
color: #8c8c8c;
}
}
.node-error{
.node-error {
position: absolute;
right: -40px;
top: 20px;
@ -179,9 +259,10 @@ export default {
}
}
.node-footer{
.node-footer {
position: relative;
.branch-merge{
.branch-merge {
font-size: 12px;
display: flex;
width: 38px;
@ -194,17 +275,20 @@ export default {
flex-direction: column;
box-shadow: 0 0 5px 0 #d8d8d8;
}
.btn{
.btn {
width: 100%;
display: flex;
padding: 20px 0 32px;
justify-content: center;
z-index: 10;
}
/deep/ .el-button{
/deep/ .el-button {
height: 32px;
}
&::before{
&::before {
content: "";
position: absolute;
top: 0;

View File

@ -1,5 +1,5 @@
<template>
<node title="发起人" :is-root="true" :content="content"
<node title="发起人" :is-root="true" :content="content" show-avatar :user-info="config.props.assignedUser"
@selected="$emit('selected')" @insertNode="type => $emit('insertNode', type)"
placeholder="所有人" :header-bgc="config.props.headerBgc" header-icon="el-icon-user-solid"/>
</template>

View File

@ -0,0 +1,352 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名称" prop="uqName">
<el-input
v-model="queryParams.uqName"
placeholder="请输入名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="描述" prop="uqDescribe">
<el-input
v-model="queryParams.uqDescribe"
placeholder="请输入描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="daterangeCreateTime"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
>修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="queryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"/>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="名称" align="center" prop="uqName"/>
<el-table-column label="描述" align="center" prop="uqDescribe"/>
<el-table-column label="创建时间" align="center" prop="createTime">
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="updateTime">
<template slot-scope="scope">
<span>{{ scope.row.updateTime }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<span v-if="scope.row.isRelease===2">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
type="text"
size="small"
icon="el-icon-edit-outline"
@click="handleEditTable(scope.row)"
>设计</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</span>
<span v-if="scope.row.isRelease===1">
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handRelease(scope.row.id)"
>撤销</el-button>
</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改万能查询对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="名称" prop="uqName">
<el-input v-model="form.uqName" placeholder="请输入名称"/>
</el-form-item>
<el-form-item label="描述" prop="uqDescribe">
<el-input v-model="form.uqDescribe" placeholder="请输入描述"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {listQuery, getQuery, delQuery, addQuery, updateQuery, exportQuery, Release} from "@/api/query";
import {Message} from "element-ui";
export default {
name: "Query",
components: {},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
queryList: [],
//
title: "",
//
open: false,
//
daterangeCreateTime: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
uqName: null,
uqDescribe: null,
type: 1
},
//
form: {},
//
rules: {
uqName: [
{required: true, message: "名称不能为空}", trigger: "blur"},
],
uqDescribe: [
{required: true, message: "描述不能为空}", trigger: "blur"},
],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询万能查询列表 */
getList() {
this.loading = true;
listQuery(this.queryParams).then(response => {
let data = response.data
this.queryList = data.rows;
this.total = data.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
uqName: null,
uqDescribe: null,
};
// this.$refs['form'].resetFields()
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
handleEditTable(row) {
this.$router.push("/query/edit/" + row.id);
},
/** 发布与撤销 */
handRelease(id) {
let that = this
this.$confirm('是否确认撤销该查询?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function () {
let data = {
id: id,
isRelease: 2
}
Release(data).then(res => {
Message({
message: res.msg,
type: 'success'
})
that.getList()
})
})
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeCreateTime = [];
this.$refs.queryForm.resetFields()
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加自定义查询";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getQuery(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改自定义查询";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
this.form.type = 1
if (valid) {
if (this.form.id != null) {
updateQuery(this.form).then(response => {
Message({
message: "修改成功",
type: 'success'
})
this.open = false;
this.getList();
});
} else {
addQuery(this.form).then(response => {
Message({
message: "新增成功",
type: 'success'
})
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$confirm('是否确认删除自定义查询编号为"' + ids + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
return delQuery(ids);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有自定义查询数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
return exportQuery(queryParams);
}).then(response => {
this.download(response.msg);
})
}
}
};
</script>

View File

@ -0,0 +1,205 @@
<template>
<div>
<el-table :data="tableData"
@row-click="clickRow"
style="width: 100%">
<el-table-column
prop="deploymentName"
label="审批类型"
width="180"/>
<el-table-column
prop="approveName"
label="发起人"
width="180"/>
<el-table-column
prop="submitTime"
label="提交时间"/>
<el-table-column
prop="endTime"
label="结束时间"/>
<el-table-column
prop="taskName"
label="当前节点"/>
<el-table-column
label="处理耗时">
<template slot-scope="scope">
{{ getTimeConsuming(scope.row) }}
</template>
</el-table-column>
<el-table-column
prop="state"
label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.state === '1'">进行中</el-tag>
<el-tag v-if="scope.row.state === '4'" type="success">审批通过</el-tag>
<el-tag v-if="scope.row.state === '3'" type="danger">审批驳回</el-tag>
<el-tag v-if="scope.row.state === '2'" type="info">已撤销</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作">
<template slot-scope="scope">
<el-button size="min" type="text" icon="el-icon-thumb">再次提交</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer
title="审批详情"
size="500px"
:visible.sync="approveOpen"
direction="rtl">
<div v-loading="loading">
<div class="top">
<div class="top_left">
<el-avatar size="large" :src="processInstanceData.userInfo.avatar"></el-avatar>
<span style="text-align: center;color: #19191a;font-size: 14px;">{{ processInstanceData.userInfo.name }}</span>
</div>
<div class="top_right">
<div style="margin-bottom: 12px">
<span style="font-size: 15px;">{{ selectProcessInstance.deploymentName }}</span>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '1'" size="mini" >进行中</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '4'" size="mini" type="success">审批通过</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '3'" size="mini" type="danger">审批驳回</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '2'" size="mini" type="info">已撤销</el-tag>
<el-tooltip class="item" effect="dark" content="查看详细流程" placement="top-start">
<el-icon class="el-icon-view" style="float: right;font-size: 20px;cursor: pointer" @click.native="processDiagramViewer = true"></el-icon>
</el-tooltip>
</div>
<div>
<span style="color: rgb(108, 108, 108);">编号: {{ selectProcessInstance.processInstanceId }}</span>
</div>
</div>
</div>
<div style="height: 15px;background:#f5f5f5;"></div>
<form-render-view v-if="!loading" ref="taskViewForm" :form-items="processInstanceData.formItems"
v-model="processInstanceData.formData"/>
<div style="height: 15px;background:#f5f5f5;"></div>
<operation-render v-if="!loading"
:operation-list="processInstanceData.operationList"
:state="selectProcessInstance.state"/>
<div style="height: 15px;background:#f5f5f5;"></div>
</div>
</el-drawer>
<el-dialog title="流程详情" :visible.sync="processDiagramViewer" :close-on-click-modal="true">
<process-diagram-viewer v-if="processDiagramViewer"/>
<div style="height: 70px;"></div>
</el-dialog>
</div>
</template>
<script>
import FormRenderView from '@/views/common/form/FormRenderView'
import ProcessDiagramViewer from "../admin/layout/ProcessDiagramViewer";
import {getAboutInstanceList, getInitiatedInstanceInfo} from "@/api/processInstance";
import {timeLength} from '@/utils/date'
import OperationRender from "../common/operation/OperationRender";
export default {
name: "AboutInstance",
components: {OperationRender, FormRenderView, ProcessDiagramViewer},
data() {
return {
approveOpen: false,
processDiagramViewer: false,
selectProcessInstance: {},
tableData: [],
loading: false,
processInstanceData: {
userInfo: {},
formData: {},
formItems: [],
processList: [],
operationList: [],
},
};
},
created() {
this.getList();
},
methods: {
clickRow(row) {
this.approveOpen = true;
this.loadProcessInstance(row.processInstanceId)
this.selectProcessInstance = row
},
loadProcessInstance(processInstanceId) {
let that = this;
this.init();
getInitiatedInstanceInfo(processInstanceId).then(res => {
let data = res.data
that.processInstanceData = data
that.$store.state.design = data;
that.$store.state.runningList = data.runningList;
that.$store.state.endList = data.endList;
that.$store.state.noTakeList = data.noTakeList;
that.$store.state.refuseList = data.refuseList;
that.$store.state.passList = data.passList;
this.loading = false;
console.log(data, "获取到的结果数据")
})
},
init() {
this.processInstanceData = {
userInfo: {},
formData: {},
formItems: [],
processList: [],
};
this.loading = true;
this.$store.state.design = this.taskData;
this.$store.state.runningList = [];
this.$store.state.endList = [];
this.$store.state.noTakeList = [];
this.$store.state.refuseList = [];
this.$store.state.passList = [];
this.$store.state.diagramMode = "viewer";
this.$store.state.preview = false;
},
getList() {
getAboutInstanceList().then(res => {
let data = res.data
this.tableData = data.rows
})
},
getTimeConsuming(instance) {
if (instance.state != 1) {
//dateFormat(,)
return timeLength(instance.submitTime,instance.endTime);
}
}
}
}
</script>
<style lang="scss" scoped>
.top {
padding: 0 10px 10px 10px;
display: flex;
.top_left {
display: flex;
flex-direction: column;
width: 50px;
align-items: center;
}
.top_right {
width: 85%;
display: flex;
flex-direction: column;
//justify-content: space-between;
font-size: 13px;
margin-left: 10px;
}
}
.state_tag{
font-size: 12px;
//margin-right: 15px;
}
</style>

View File

@ -29,10 +29,10 @@
prop="state"
label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.state == 1">待处理</el-tag>
<el-tag v-if="scope.row.state == 4" type="success">已同意</el-tag>
<el-tag v-if="scope.row.state == 3" type="danger">已拒绝</el-tag>
<el-tag v-if="scope.row.state == 2" type="warning">已退回</el-tag>
<el-tag v-if="scope.row.state === '1'">待处理</el-tag>
<el-tag v-if="scope.row.state === '4'" type="success">已同意</el-tag>
<el-tag v-if="scope.row.state === '3'" type="danger">已拒绝</el-tag>
<el-tag v-if="scope.row.state === '2'" type="warning">已退回</el-tag>
</template>
</el-table-column>
</el-table>
@ -44,13 +44,18 @@
<div v-loading="loading">
<div class="top">
<div class="top_left">
<el-avatar size="large" :src="avatar"></el-avatar>
<span style="text-align: center;color: #19191a;font-size: 14px;">{{ selectTask.initiatorName }}</span>
<el-avatar size="large" :src="taskData.userInfo.avatar"></el-avatar>
<span style="text-align: center;color: #19191a;font-size: 14px;">{{ taskData.userInfo.name }}</span>
</div>
<div class="top_right">
<div style="margin-bottom: 12px">
<span style="font-size: 15px;margin-right: 15px">{{ selectTask.processName }}</span>
<el-tag style="font-size: 12px" size="mini">进行中</el-tag>
<el-tag class="state_tag" size="mini" v-if="selectTask.state === '1'">进行中</el-tag>
<el-tag class="state_tag" size="mini" v-if="selectTask.state === '4'" type="success">审批通过</el-tag>
<el-tag class="state_tag" size="mini" v-if="selectTask.state === '3'" type="danger">审批驳回</el-tag>
<el-tag class="state_tag" size="mini" v-if="selectTask.state === '2'" type="info">已撤销</el-tag>
<!-- <el-tag style="font-size: 12px" size="mini">进行中</el-tag>-->
</div>
<div>
<span style="color: rgb(108, 108, 108);">编号: {{ selectTask.taskId }}</span>
@ -58,15 +63,21 @@
</div>
</div>
<div style="height: 15px;background:#f5f5f5;"></div>
<form-render v-if="!loading" ref="taskViewForm" :form-items="taskData.formItems"
v-model="taskData.formData"/>
<div>
<form-render-view v-if="!loading" ref="taskViewForm" :form-items="taskData.formItems"
v-model="taskData.formData"/>
</div>
<div style="height: 15px;background:#f5f5f5;"></div>
<operation-render v-if="!loading"
:operation-list="taskData.operationList"
:state="selectTask.state"/>
<div style="height: 15px;background:#f5f5f5;"></div>
<el-footer class="footer">
<div class="footer_comment" @click="showCommentBox">
<i class="el-icon-chat-line-round" style="font-size: 18px;"></i>
<span style="font-size: 12px;">评论</span>
</div>
<!-- <process-diagram-viewer/>-->
<!-- <process-diagram-viewer/>-->
<div style="margin-right: 20px;">
<el-dropdown>
<span class="el-dropdown-link" style="color: #2a99ff;cursor: pointer">
@ -76,7 +87,7 @@
<el-dropdown-item icon="el-icon-s-custom" @click.native="showTransferBox()">转交</el-dropdown-item>
<el-dropdown-item icon="el-icon-s-release" @click.native="showBackNodeBox()">退回</el-dropdown-item>
<el-dropdown-item icon="el-icon-s-check" @click.native="showAddApprovalBox()">加签</el-dropdown-item>
<el-dropdown-item icon="el-icon-refresh-left" @click.native="showRevocationBox()">撤销</el-dropdown-item>
<el-dropdown-item icon="el-icon-refresh-left" @click.native="showRevocationBox()">撤销</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button type="danger" size="mini" plain round style="margin-left: 20px"
@ -97,13 +108,14 @@
<script>
import Comment from "../../components/common/Comment";
import FormRender from "@/views/common/form/FormRender";
import ProcessDiagramViewer from "../admin/layout/ProcessDiagramViewer";
import {addComment, completeTask, getTaskInfo, getTaskList, refuseTask,rollBackTask} from "@/api/task";
import {addComment, completeTask, getTaskInfo, getTaskList, refuseTask, rollBackTask} from "@/api/task";
import OperationRender from "@/views/common/operation/OperationRender";
import FormRenderView from "../common/form/FormRenderView";
export default {
name: "DisposalTask",
components: { Comment, FormRender, ProcessDiagramViewer},
components: {FormRenderView, OperationRender, Comment, ProcessDiagramViewer},
data() {
return {
approveOpen: false,
@ -115,9 +127,11 @@ export default {
},
loading: false,
taskData: {
userInfo: {},
formData: {},
formItems: [],
processList: []
processList: [],
operationList: []
},
avatar: require("@/assets/image/avatar.png")
};
@ -140,9 +154,11 @@ export default {
},
init() {
this.taskData = {
userInfo: {},
formData: {},
formItems: [],
processList: []
processList: [],
operationList: []
};
this.loading = true;
this.$store.state.design = this.taskData;
@ -153,29 +169,28 @@ export default {
let that = this;
this.init();
getTaskInfo(taskId).then(res => {
let data=res.data
data.formItems.map(item=>{
if(item.props.required){
item.props.required=!item.props.required
}
return item
})
that.taskData = res.data;
that.$store.state.design = that.taskData;
that.$store.state.userTaskOption = that.taskData.userTaskOption;
console.log(that.taskData.userTaskOption,"that.taskData.userTaskOption")
this.loading = false;
});
},
submitTask() {
let params = {
taskId: this.selectTask.taskId,
formData: JSON.stringify(this.taskData.formData)
};
completeTask(params).then(res => {
this.approveOpen = false;
this.getList();
this.$message.success(res.msg);
});
this.$refs.taskViewForm.validate(valid =>{
if (valid){
let params = {
taskId: this.selectTask.taskId,
formData: JSON.stringify(this.taskData.formData)
};
completeTask(params).then(res => {
this.approveOpen = false;
this.getList();
this.$message.success(res.msg);
});
}
})
},
showCommentBox() {
this.commentInfo = {
@ -223,7 +238,28 @@ export default {
this.$refs.comment.show()
},
/**
* 拒绝提交
* 评论回调
* @param data
* @param type
*/
commentConfirm(data, type) {
switch (type) {
case 1:
this.submitComment(data);
break;
case 2:
this.submitRefuse(data);
break;
case 4:
this.submitRollBack(data);
break;
default :
break;
}
},
/**
* 拒绝任务
* @param data
*/
submitRefuse(data) {
@ -238,32 +274,17 @@ export default {
})
},
/**
* 评论回调
* @param data
* @param type
* 回退
*/
commentConfirm(data, type) {
switch (type) {
case 1:
this.submitComment(data);
break;
case 2:
this.submitRollBack(data);
break;
case 3:
this.submitRefuse(data);
break;
default :
break;
}
},
submitRollBack(data){
submitRollBack(data) {
let params = {
taskId: this.selectTask.taskId,
rollBackId: data.rollBackId,
comment: data,
};
rollBackTask(params).then(res=>{
rollBackTask(params).then(res => {
this.approveOpen = false;
this.getList();
this.$message.success(res.msg);
})
},
@ -274,6 +295,7 @@ export default {
submitComment(data) {
this.$set(data, "taskId", this.selectTask.taskId);
addComment(data).then(res => {
this.approveOpen = false;
this.$message.success(res.msg);
});
}
@ -282,6 +304,7 @@ export default {
</script>
<style scoped lang="less">
.top {
padding: 0 10px 10px 10px;
display: flex;

View File

@ -2,11 +2,11 @@
<div v-loading="loading" class="initiate_process">
<div v-if="!loading" style="min-width: 338px;">
<!--渲染表单-->
<form-render class="process-form" ref="form" :form-items="processDefinition.formItems" v-model="formData"/>
<form-render class="process-form" ref="initiateForm" :form-items="processDefinition.formItems" v-model="formData"/>
</div>
<div style="display: flex;justify-content: center;flex-direction: column;min-width: 1036px;">
<span style="font-size: 18px;text-align: center;padding-top: 20px;">审批流程</span>
<process-diagram-viewer/>
<process-diagram-viewer ref="processDiagramViewer"/>
<!--渲染流程执行过程-->
<el-timeline :reverse="false">
<!-- <el-timeline-item v-for="(activity, index) in activities" :key="index">-->
@ -64,7 +64,6 @@ export default {
methods: {
loadProcessDefinitionInfo(processDefinitionKey) {
getInitiateInfo(processDefinitionKey).then(res => {
console.log(res);
let processDefinition = res.data;
this.processDefinition = processDefinition;
//
@ -75,7 +74,8 @@ export default {
});
},
validate(call) {
this.$refs.form.validate(call);
this.$refs.initiateForm.validate(call);
this.$refs.processDiagramViewer.validate(call);
}
}
};

View File

@ -30,10 +30,10 @@
prop="state"
label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.state == 1">进行中</el-tag>
<el-tag v-if="scope.row.state == 4" type="success">审批通过</el-tag>
<el-tag v-if="scope.row.state == 3" type="danger">审批驳回</el-tag>
<el-tag v-if="scope.row.state == 2" type="info">已撤销</el-tag>
<el-tag v-if="scope.row.state === '1'">进行中</el-tag>
<el-tag v-if="scope.row.state === '4'" type="success">审批通过</el-tag>
<el-tag v-if="scope.row.state === '3'" type="danger">审批驳回</el-tag>
<el-tag v-if="scope.row.state === '2'" type="info">已撤销</el-tag>
</template>
</el-table-column>
<el-table-column
@ -51,27 +51,32 @@
<div v-loading="loading">
<div class="top">
<div class="top_left">
<el-avatar size="large" :src="avatar"></el-avatar>
<span style="text-align: center;color: #19191a;font-size: 14px;">{{ selectProcessInstance.approveName }}</span>
<el-avatar size="large" :src="processInstanceData.userInfo.avatar"></el-avatar>
<span style="text-align: center;color: #19191a;font-size: 14px;">{{ processInstanceData.userInfo.name }}</span>
</div>
<div class="top_right">
<div style="margin-bottom: 12px">
<span style="font-size: 15px;">{{ selectProcessInstance.deploymentName }}</span>
<el-tag style="font-size: 12px;margin:0 15px;" size="mini" type="success" v-if="selectProcessInstance.state==4">审批通过</el-tag>
<el-tag style="font-size: 12px;margin:0 15px;" size="mini" v-else >进行中</el-tag>
<span style="font-size: 15px;margin-right: 15px">{{ selectProcessInstance.deploymentName }}</span>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '1'" size="mini" >进行中</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '4'" size="mini" type="success">审批通过</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '3'" size="mini" type="danger">审批驳回</el-tag>
<el-tag class="state_tag" v-if="selectProcessInstance.state === '2'" size="mini" type="info">已撤销</el-tag>
<el-tooltip class="item" effect="dark" content="查看详细流程" placement="top-start">
<el-icon class="el-icon-view" style="float: right;font-size: 20px;cursor: pointer" @click.native="processDiagramViewer = true"></el-icon>
</el-tooltip>
</div>
<div>
<span style="color: rgb(108, 108, 108);">编号: {{ selectProcessInstance.taskId }}</span>
<span style="color: rgb(108, 108, 108);">编号: {{ selectProcessInstance.processInstanceId }}</span>
</div>
</div>
</div>
<div style="height: 15px;background:#f5f5f5;"></div>
<form-render-view ref="taskViewForm" :form-items="processInstanceData.formItems"
<form-render-view v-if="!loading" ref="taskViewForm" :form-items="processInstanceData.formItems"
v-model="processInstanceData.formData"/>
<!-- <process-diagram-viewer v-if="!loading"/>-->
<div style="height: 15px;background:#f5f5f5;"></div>
<operation-render v-if="!loading"
:operation-list="processInstanceData.operationList"
:state="selectProcessInstance.state"/>
<div style="height: 15px;background:#f5f5f5;"></div>
</div>
</el-drawer>
@ -84,14 +89,14 @@
</template>
<script>
import Comment from "@/components/common/Comment";
import FormRenderView from '@/views/common/form/FormRender'
import FormRenderView from '@/views/common/form/FormRenderView'
import ProcessDiagramViewer from "../admin/layout/ProcessDiagramViewer";
import {getInitiatedInstanceList, getInitiatedInstanceInfo} from "@/api/processInstance";
import {timeLength} from '@/utils/date'
import OperationRender from "../common/operation/OperationRender";
export default {
name: "InitiatedInstance",
components: {Comment, FormRenderView, ProcessDiagramViewer},
components: {OperationRender, FormRenderView, ProcessDiagramViewer},
data() {
return {
approveOpen: false,
@ -100,11 +105,12 @@ export default {
tableData: [],
loading: false,
processInstanceData: {
userInfo: {},
formData: {},
formItems: [],
processList: [],
operationList: [],
},
avatar: require("@/assets/image/avatar.png")
};
},
created() {
@ -126,23 +132,27 @@ export default {
that.$store.state.runningList = data.runningList;
that.$store.state.endList = data.endList;
that.$store.state.noTakeList = data.noTakeList;
that.$store.state.refuseList = data.refuseList;
that.$store.state.passList = data.passList;
this.loading = false;
console.log(data, "获取到的结果数据")
})
},
init() {
this.processInstanceData = {
userInfo: {},
formData: {},
formItems: [],
processList: []
processList: [],
};
this.loading = true;
this.$store.state.design = this.taskData;
this.$store.state.runningList = [];
this.$store.state.endList = [];
this.$store.state.noTakeList = [];
this.$store.state.refuseList = [];
this.$store.state.passList = [];
this.$store.state.diagramMode = "viewer";
this.$store.state.preview = false;
},
@ -185,4 +195,9 @@ export default {
}
}
.state_tag{
font-size: 12px;
//margin-right: 15px;
}
</style>

View File

@ -51,9 +51,7 @@ export default {
taskId: this.taskId,
formData : JSON.stringify(that.taskData.formData)
}
console.log(params,"需要提交的数据")
completeTask(params).then(res =>{
console.log(res)
that.$parent.approveOpen = false
that.$parent.getList()
that.$message.success(res.msg)

View File

@ -41,13 +41,13 @@
</div>
</el-tab-pane>
<el-tab-pane label="待我处理(3)" name="disposalTask">
<disposal-task v-if="active == 'disposalTask'" ref="disposalTask"/>
<disposal-task v-if="active === 'disposalTask'" ref="disposalTask"/>
</el-tab-pane>
<el-tab-pane label="我发起的" name="initiated">
<initiated-instance v-if="active == 'initiated'" ref="InitiatedInstance"/>
<initiated-instance v-if="active === 'initiated'" ref="InitiatedInstance"/>
</el-tab-pane>
<el-tab-pane label="关于我的" name="aboutMe">
<div class="no-data">暂无数据 😀</div>
<about-instance v-if="active === 'aboutMe'" ref="AboutInstance"/>
</el-tab-pane>
</el-tabs>
@ -67,10 +67,11 @@ import InitiatedInstance from "./InitiatedInstance";
import DisposalTask from "./DisposalTask";
import {getProcessDefinitionList} from "@/api/processDefinition";
import {startProcessInstance} from "@/api/processInstance";
import AboutInstance from "./AboutInstance";
export default {
name: "workSpace",
components: {InitiateProcess,DisposalTask,InitiatedInstance},
components: {AboutInstance, InitiateProcess,DisposalTask,InitiatedInstance},
data() {
return {
active: 'disposalTask',
@ -119,9 +120,12 @@ export default {
// todo
submitForm(){
let processInstance = this.$refs.processInstance;
let selectUserMap = {}
this.$store.state.selectUserMap.forEach(((value, key) => {selectUserMap[key]=value}))
let paramsData = {
processDefinitionId: this.selectForm.processDefinitionId,
formData: JSON.stringify(processInstance.formData)
formData: JSON.stringify(processInstance.formData),
optionalUser: selectUserMap
}
processInstance.validate(valid => {
if (valid) {
@ -129,7 +133,6 @@ export default {
this.openItemDl = false
this.$message.success(res.msg)
this.$refs.disposalTask.getList()
})
} else {
this.$message.warning("请完善表单😥")