whx 4 달 전
부모
커밋
23e2de6c02
45개의 변경된 파일18415개의 추가작업 그리고 85개의 파일을 삭제
  1. 5 0
      virgo.wzfrontend/workark/package-lock.json
  2. 1 0
      virgo.wzfrontend/workark/package.json
  3. 13 2
      virgo.wzfrontend/workark/src/App.vue
  4. 289 0
      virgo.wzfrontend/workark/src/api/organization.js
  5. 165 0
      virgo.wzfrontend/workark/src/api/system.js
  6. 411 0
      virgo.wzfrontend/workark/src/assets/css/common.scss
  7. 2 2
      virgo.wzfrontend/workark/src/assets/css/element-variables.scss
  8. 74 0
      virgo.wzfrontend/workark/src/components/common/avatar.vue
  9. 101 0
      virgo.wzfrontend/workark/src/components/common/cc.vue
  10. 63 0
      virgo.wzfrontend/workark/src/components/common/city.vue
  11. 378 0
      virgo.wzfrontend/workark/src/components/common/selectUser.vue
  12. 359 0
      virgo.wzfrontend/workark/src/components/common/upload.vue
  13. 18 0
      virgo.wzfrontend/workark/src/components/work/organization/depart/detail.vue
  14. 93 0
      virgo.wzfrontend/workark/src/components/work/organization/depart/edit.vue
  15. 125 0
      virgo.wzfrontend/workark/src/components/work/organization/manager/edit.vue
  16. 64 0
      virgo.wzfrontend/workark/src/components/work/organization/seal/detail.vue
  17. 107 0
      virgo.wzfrontend/workark/src/components/work/organization/seal/edit.vue
  18. 96 0
      virgo.wzfrontend/workark/src/components/work/organization/user/detail.vue
  19. 103 0
      virgo.wzfrontend/workark/src/components/work/organization/user/edit.vue
  20. 147 0
      virgo.wzfrontend/workark/src/components/work/organization/user/role.vue
  21. 59 0
      virgo.wzfrontend/workark/src/components/work/system/main/edit.vue
  22. 419 0
      virgo.wzfrontend/workark/src/config/field.js
  23. 99 0
      virgo.wzfrontend/workark/src/layout/components/breadCrumb.vue
  24. 35 0
      virgo.wzfrontend/workark/src/layout/components/subMenu.vue
  25. 153 0
      virgo.wzfrontend/workark/src/layout/components/topNav.vue
  26. 1 1
      virgo.wzfrontend/workark/src/layout/loginLayout.vue
  27. 268 0
      virgo.wzfrontend/workark/src/layout/workLayout.vue
  28. 17 2
      virgo.wzfrontend/workark/src/main.js
  29. 9 2
      virgo.wzfrontend/workark/src/router/index.js
  30. 0 0
      virgo.wzfrontend/workark/src/router/modules/login.js
  31. 25 0
      virgo.wzfrontend/workark/src/router/modules/work.js
  32. 30 0
      virgo.wzfrontend/workark/src/router/modules/work/organization.js
  33. 9 0
      virgo.wzfrontend/workark/src/router/modules/work/system.js
  34. 3 1
      virgo.wzfrontend/workark/src/store/modules/app.js
  35. 13346 0
      virgo.wzfrontend/workark/src/uitls/map.js
  36. 16 0
      virgo.wzfrontend/workark/src/uitls/permission.js
  37. 148 0
      virgo.wzfrontend/workark/src/views/error/401.vue
  38. 148 0
      virgo.wzfrontend/workark/src/views/error/404.vue
  39. 18 75
      virgo.wzfrontend/workark/src/views/login/login.vue
  40. 8 0
      virgo.wzfrontend/workark/src/views/work/index.vue
  41. 145 0
      virgo.wzfrontend/workark/src/views/work/organization/depart.vue
  42. 199 0
      virgo.wzfrontend/workark/src/views/work/organization/manager.vue
  43. 123 0
      virgo.wzfrontend/workark/src/views/work/organization/seal.vue
  44. 253 0
      virgo.wzfrontend/workark/src/views/work/organization/user.vue
  45. 270 0
      virgo.wzfrontend/workark/src/views/work/system/main.vue

+ 5 - 0
virgo.wzfrontend/workark/package-lock.json

@@ -7193,6 +7193,11 @@
         "websocket-driver": "^0.7.4"
       }
     },
+    "sortablejs": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz",
+      "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A=="
+    },
     "source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",

+ 1 - 0
virgo.wzfrontend/workark/package.json

@@ -11,6 +11,7 @@
 		"axios": "^0.19.0",
 		"core-js": "^3.8.3",
 		"element-ui": "^2.15.14",
+		"sortablejs": "^1.15.6",
 		"vue": "^2.6.14",
 		"vue-router": "^3.1.3",
 		"vuex": "^3.0.1"

+ 13 - 2
virgo.wzfrontend/workark/src/App.vue

@@ -6,9 +6,20 @@
 
 <script>
 	export default {
-		name: 'App'
+		name: 'App',
+		created() {
+			if (sessionStorage.getItem('store')) {
+				this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem(
+					'store'))));
+				sessionStorage.setItem('store', ''); //重新置完清空session
+			}
+			window.addEventListener('beforeunload', () => {
+				sessionStorage.setItem('store', JSON.stringify(this.$store.state));
+			});
+		}
 	}
 </script>
 
-<style>
+<style lang="scss">
+	@import './assets/css/common.scss'
 </style>

+ 289 - 0
virgo.wzfrontend/workark/src/api/organization.js

@@ -0,0 +1,289 @@
+import request from '@/axios'
+/* 
+ * 获取组织详情
+ * 
+ * 
+ */
+export function getOrganizationDetailById(id) {
+	return request({
+		url: `/manager/base/organization/${id}`,
+		method: 'get'
+	})
+}
+/* 
+ * 更新组织信息
+ * 
+ * 
+ */
+export function updateOrganization(data) {
+	return request({
+		url: `/manager/base/organization/update`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 通过code获取组织信息
+ * 
+ * 
+ */
+export function getOrganizationByCode(organizationCode) {
+	return request({
+		url: `/manager/base/organizationCode/${organizationCode}`,
+		method: 'get'
+	})
+}
+/* 
+ * 绑定客户/经纪人/组织至组织项目
+ * 
+ * 
+ */
+export function bindProject(data) {
+	return request({
+		url: `/manager/bindProject`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 获取客户/经纪人/组织绑定信息
+ * 
+ * 
+ */
+export function bindProjectDetail(data) {
+	return request({
+		url: `/manager/bindProject/list`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 更改绑定客户/经纪人/组织至组织项目
+ * 
+ * 
+ */
+export function putBindProject(data) {
+	return request({
+		url: `/manager/bindProject/update`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+
+ * 获取组织项目部门列表
+ * 
+ * 
+ */
+export function getPartList(organizationId, projectId) {
+	return request({
+		url: `/manager/role/${organizationId}/${projectId}`,
+		method: 'get'
+	})
+}
+/* 
+ * 新增部门
+ * 
+ * 
+ */
+export function insertDepartment(data) {
+	return request({
+		url: `/manager/role`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 编辑部门
+ * 
+ * 
+ */
+export function updateDepartment(data) {
+	return request({
+		url: `/manager/role`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 删除部门
+ * 
+ * 
+ */
+export function deleteDepartment(id) {
+	return request({
+		url: `/manager/role/${id}`,
+		method: 'delete'
+	})
+}
+/* 
+ * 获取组织项目部门用户列表
+ * 
+ * 
+ */
+export function getUserListByPart(data) {
+	return request({
+		url: `/manager/user/list/${data.organizationId}/${data.partId}`,
+		method: 'post'
+	})
+}
+/* 
+ * 判断用户手机号是否存在
+ * 
+ * 
+ */
+export function testPhone(phone) {
+	return request({
+		url: `/manager/user/exist/${phone}`,
+		method: 'get'
+	})
+}
+/* 
+ * 新增用户
+ * 
+ * 
+ */
+export function insertUser(organizationId, roleIds, data) {
+	return request({
+		url: `/manager/user/add/${organizationId}/${roleIds}`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 更新运营用户权限和菜单
+ * 
+ * 
+ */
+export function updateUserRole(data) {
+	return request({
+		url: `/manager/user/userRole`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 删除用户
+ * 
+ * 
+ */
+export function deleteUser(organizationId, userId) {
+	return request({
+		url: `/manager/user/delete/${organizationId}/${userId}`,
+		method: 'delete'
+	})
+}
+/* 
+ * 获取组织印章列表
+ * 
+ * 
+ */
+export function getSealList(organizationId) {
+	return request({
+		url: `/file/seal/${organizationId}`,
+		method: 'get'
+	})
+}
+/* 
+ * 获取组织印章详情
+ * 
+ * 
+ */
+export function getSealDetailById(id) {
+	return request({
+		url: `/file/seal/detail/${id}`,
+		method: 'get',
+	})
+}
+/* 
+ * 新增组织印章
+ * 
+ * 
+ */
+export function insertSeal(data) {
+	return request({
+		url: `/file/seal`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 编辑组织印章
+ * 
+ * 
+ */
+export function updateSeal(data) {
+	return request({
+		url: `/file/seal`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 删除组织印章
+ * 
+ * 
+ */
+export function deleteSeal(id) {
+	return request({
+		url: `/file/seal/${id}`,
+		method: 'delete'
+	})
+}
+/* 
+ * 获取申请印章列表
+ * 
+ * 
+ */
+export function getSealRequestListByQuery(data) {
+	return request({
+		url: `/file/sealRequest/${data.sealId}/${data.currPage}/${data.pageSize}`,
+		method: 'get'
+	})
+}
+/* 
+ * 申请使用公章
+ * @param {Object} data = {}
+ * 
+ */
+export function requestSeal(data) {
+	return request({
+		url: `/file/sealRequest`,
+		method: 'post',
+		data: data
+	})
+}
+/*
+ * 申请公章详情
+ * @param {Object} data = {}
+ * 
+ */
+export function getRequestSealDetail(id) {
+	return request({
+		url: `/file/sealRequest/${id}`,
+		method: 'get',
+	})
+}
+/*
+ * 通过印章申请
+ * @param {Object} data = {}
+ * 
+ */
+export function requestUseSeal(id, data) {
+	return request({
+		url: `/file/sealRequest/${id}`,
+		method: 'post',
+		data: data
+	})
+}
+/*
+ * 判断文档中申请印章痛没通过
+ * @param {Object} data = {}
+ * 
+ */
+export function isPassRequestUseSeal(documentIds) {
+	return request({
+		url: `/file/sealRequest/document/${documentIds}`,
+		method: 'get'
+	})
+}

+ 165 - 0
virgo.wzfrontend/workark/src/api/system.js

@@ -0,0 +1,165 @@
+import request from '@/axios'
+import config from '@/config'
+/* 
+ * 获取菜单列表
+ * 
+ */
+export function getMenuList() {
+	return request({
+		url: `/manager/resource/list`,
+		method: 'get'
+	})
+}
+/* 
+ * 新增菜单
+ * 
+ */
+export function insertMenu(data) {
+	return request({
+		url: `/manager/resource`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 编辑菜单
+ * 
+ */
+export function updateMenu(data) {
+	return request({
+		url: `/manager/resource/update`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 获取身份
+ * 
+ */
+export function getIdentityList() {
+	return request({
+		url: `/manager/identity`,
+		method: 'get',
+	})
+}
+/* 
+ * 新增身份
+ * 
+ */
+export function insertIdentity(data) {
+	return request({
+		url: `/manager/identity`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 编辑身份
+ * 
+ */
+export function updateIdentity(data) {
+	return request({
+		url: `/manager/identity/update`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 身份资源的获取
+ * 
+ */
+export function getIdentityResource(data) {
+	return request({
+		url: `/manager/identity/resource/query`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 新增身份资源
+ * 
+ */
+export function insertIdentityResource(data) {
+	return request({
+		url: `/manager/identity/resource`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 更新身份资源
+ * 
+ */
+export function updateIdentityResource(data) {
+	return request({
+		url: `/manager/identity/resource/update`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 下载文件
+ * 
+ * 
+ */
+export function downloadFile(fileId) {
+	console.log(config.baseURL + '/file/filenode/' + fileId);
+	return window.location.href = config.baseURL + '/file/filenode/' + fileId;
+}
+/* 
+ * 获取设备层级
+ * 
+ * 
+ */
+export function getDeviceModel() {
+	return request({
+		url: `/api/operateDeviceLevelModel`,
+		method: 'get'
+	})
+}
+/* 
+ * 获取通过id设备层级
+ * 
+ * 
+ */
+export function getDeviceModelById(id) {
+	return request({
+		url: `/api/operateDeviceLevelModel/get/${id}`,
+		method: 'get'
+	})
+}
+/* 
+ * 新增设备层级
+ * 
+ * 
+ */
+export function insertDeviceModel(data) {
+	return request({
+		url: `/api/operateDeviceLevelModel`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 编辑设备层级
+ * 
+ * 
+ */
+export function updateDeviceModel(data) {
+	return request({
+		url: `/api/operateDeviceLevelModel`,
+		method: 'put',
+		data: data
+	})
+}
+/* 
+ * 删除设备层级
+ * 
+ * 
+ */
+export function deleteDeviceModel(id) {
+	return request({
+		url: `/api/operateDeviceLevelModel/${id}`,
+		method: 'delete'
+	})
+}

+ 411 - 0
virgo.wzfrontend/workark/src/assets/css/common.scss

@@ -0,0 +1,411 @@
+#app{
+	color: $--color-text-primary;
+	font-size: $--size-base;
+}
+.hui-flex {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+
+	.hui-flex-box {
+		flex: 1;
+		height: 0;
+		overflow-y: auto;
+	}
+}
+
+.hui-content {
+	.hui-content-title {
+		display: flex;
+		height: 48px;
+		border-bottom: 1px solid $--border-color-base;
+		padding: 0 20px;
+
+		.hui-title-item {
+			padding: 0 14px;
+			cursor: pointer;
+			position: relative;
+			height: 48px;
+			line-height: 48px;
+		}
+
+		.hui-title-item.active,
+		.hui-title-item:hover {
+			color: $--color-primary;
+		}
+
+		.hui-title-item.active::before,
+		.hui-title-item:hover::before {
+			content: '';
+			background: $--color-primary;
+			height: 2px;
+			left: 14px;
+			right: 14px;
+			bottom: 0;
+			position: absolute;
+		}
+	}
+	
+	.hui-nav{
+		height: 48px;
+		border-bottom: 1px solid #374156;
+		padding: 0 20px;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		.el-page-header__content{
+			font-size: 16px;
+		}
+	}
+	
+	.hui-content-insert {
+		padding-bottom: 12px;
+	}
+	
+	.hui-content-select{
+		display: flex;
+		padding-bottom: 10px;
+		
+		.select-item{
+			display: flex;
+			flex: 1;
+		}
+		
+		.select-input{
+			width: 240px;
+		}
+	}
+	
+	.hui-table {
+		padding: 12px 20px;
+		
+		.cell{
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		
+		.tr-center{
+			display: flex;
+			align-items: center;
+			
+			.label{
+				margin-right: 8px;
+			}
+		}
+		
+		.hui-table-operation {
+			.table-operation {
+				padding-left: 10px;
+				cursor: pointer;
+				
+				&:hover{
+					color: $--color-primary;
+				}
+			}
+
+			.table-operation:first-child {
+				padding-left: 0;
+			}
+		}
+
+		.hui-table-tag {
+			display: flex;
+			align-items: center;
+		}
+
+		.hui-table-user {
+			display: flex;
+			align-items: center;
+
+			.hui-table-avatar {
+				width: 32px;
+				height: 32px;
+				overflow: hidden;
+				border-radius: 50%;
+				margin-right: 8px;
+			}
+		}
+		
+		.el-table{
+			border: 1px solid $--border-color-base;
+		}
+		
+		.el-table__header{
+			
+			th{
+				color: #606266;
+				background: #f4f4f5;
+				
+			}
+			
+			.cell{
+				font-weight: 700;
+			}
+		}
+		
+		.el-table__expand-icon {
+			.el-icon-arrow-right {
+				font-family: 'iconfont' !important;
+				font-size: 8px;
+				position: relative;
+				top: -2px;
+			}
+
+			.el-icon-arrow-right::before {
+				content: '\e738';
+			}
+		}
+	}
+
+	.hui-content-pagination {
+		padding-top: 12px;
+		text-align: right;
+	}
+
+	.hui-table-box-foot {
+		display: flex;
+		padding-top: 12px;
+		justify-content: space-between;
+		align-items: flex-start;
+
+		.tips {
+			font-size: 12px;
+		}
+	}
+}
+//form
+.el-form {
+	display: flex;
+	flex-wrap: wrap;
+
+	.el-form-item__content {
+		line-height: 32px;
+	}
+	
+	.el-form-item {
+		width: 50%;
+		box-sizing: border-box;
+	}
+
+	.el-form-item:nth-child(2n-1) {
+		padding-right: 10px;
+	}
+
+	.el-form-item:nth-child(2n) {
+		padding-left: 10px;
+	}
+
+	.el-form-item__label {
+		line-height: normal;
+		padding-bottom: 5px;
+	}
+	
+	.hui-textarea {
+		width: 100% !important;
+		padding: 0 !important;
+	}
+	
+	.hui-document {
+		padding: 0 !important;
+	}
+}
+//dialog
+.el-dialog {
+	height: 80vh;
+	margin-top: 10vh !important;
+	display: flex;
+	flex-direction: column;
+
+	.el-dialog__header {
+		padding: 15px 20px;
+		border-bottom: 1px solid $--border-color-base;
+
+		.el-icon-close {
+			font-family: "iconfont" !important;
+			font-size: 18px;
+		}
+
+		.el-icon-close::before {
+			content: "\e72f";
+		}
+	}
+
+	.el-dialog__title {
+		font-size: 16px;
+	}
+
+	.el-dialog__body {
+		padding: 0;
+		flex: 1;
+		height: 0;
+	}
+}
+
+.hui-dialog {
+	.hui-dialog-content {
+		padding: 20px;
+		overflow-y: auto;
+	}
+	.hui-dialog-submit {
+		border-top: 1px solid $--border-color-base;
+		text-align: right;
+		padding: 10px;
+	}
+}
+
+.color-red {
+	color: $--color-danger;
+}
+.yui-tree-box {
+	display: flex;
+	width: 100%;
+	height: 100%;
+
+	.hui-left-tree {
+		width: 250px;
+		box-sizing: border-box;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+
+		.hui-left-tree-title {
+			height: 42px;
+			border-radius: 2px;
+			display: flex;
+			align-items: center;
+			padding: 0 12px;
+			border-bottom: 1px solid $--border-color-base;
+		}
+
+		.hui-left-tree-content {
+			flex: 1;
+			height: 0;
+			overflow-y: auto;
+
+			.collapse-title {
+				flex: 1;
+				width: 0;
+				display: flex;
+				align-items: center;
+				
+				&.active{
+					color: $--color-primary;
+					.iconfont {
+						color: $--color-primary;
+					}
+				}
+			}
+		}
+
+		.hui-left-tree-sub {
+			font-weight: 600;
+			margin-left: 8px;
+			flex: 1;
+			width: 0;
+		}
+	}
+
+	.hui-tree-content {
+		width: 0;
+		flex: 1;
+		height: 100%;
+		padding-left: 10px;
+	}
+
+	.el-collapse {
+		border: none;
+
+		.el-collapse-item__header {
+			border-color: $--border-color-base;
+			height: 32px;
+			line-height: 32px;
+			font-size: 12px;
+			padding: 0 14px;
+
+			.el-collapse-name {
+				flex: 1;
+				width: 0;
+				overflow: hidden;
+			}
+
+			.iconfont {
+				margin-right: 6px;
+			}
+
+			.huifont-bumen {
+				font-size: 16px;
+			}
+
+			.huifont-shuzhuangcaidantubiao,
+			.huifont-moxingguanli {
+				font-size: 18px;
+			}
+		}
+
+		.el-collapse-item__wrap {
+			border: none;
+		}
+
+		.el-icon-arrow-right {
+			font-family: 'iconfont' !important;
+			font-size: 9px;
+			margin: 0;
+		}
+
+		.el-icon-arrow-right::before {
+			content: '\e738';
+		}
+
+		.el-collapse-item__content {
+			padding-bottom: 0;
+			font-size: 12px;
+		}
+	}
+}
+.el-tree {
+	.el-tree-node__content {
+		height: 32px;
+		overflow: hidden;
+	}
+
+	.el-tree-node__label {
+		font-size: 12px;
+		position: relative;
+		left: -10px;
+	}
+
+	.el-icon-caret-right {
+		font-family: 'iconfont' !important;
+		font-size: 8px;
+		margin: 0;
+
+	}
+
+	.el-icon-caret-right::before {
+		content: '\e738';
+	}
+	
+	.el-tree-node__content>.el-tree-node__expand-icon {
+		padding: 10px;
+	}
+	
+	.tree-node {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		width: 100%;
+		position: relative;
+		left: -10px;
+
+		.tree-node-label {
+			font-size: 12px;
+		}
+
+		.btn .el-button {
+			padding: 5px 10px;
+		}
+	}
+
+}

+ 2 - 2
virgo.wzfrontend/workark/src/assets/css/element-variables.scss

@@ -14,13 +14,13 @@ $--color-text-secondary: #909399 !default;// 次要文字
 $--color-text-placeholder: #C0C4CC !default; // 占位符
 
 // 边框颜色
-$--border-color-base: #DCDFE6 !default;  // 基础边框
+$--border-color-base: #e4e4e7 !default;  // 基础边框
 $--border-color-light: #E4E7ED !default; // 浅色边框
 $--border-color-lighter: #EBEEF5 !default;// 更浅边框
 $--border-color-extra-light: #F2F6FC !default;
 
 // 背景色
-$--background-color-base: #F5F7FA !default; // 基础背景
+$--background-color-base: #f1f3f6 !default; // 基础背景
 $--background-color-light: #fafafa !default; // 浅色背景
 
 $--font-path: 'fonts' !default;          // 字体文件路径

+ 74 - 0
virgo.wzfrontend/workark/src/components/common/avatar.vue

@@ -0,0 +1,74 @@
+<template>
+	<div class="hui-avatar">
+		<el-image v-if="user.portrait" :src="user.portrait" fit="cover"></el-image>
+		<div class="el-image" v-else>
+			<div class="avatar-slot">
+				<div :style="`font-size:${size}px;line-height:${lineHeight}px`">
+					{{name}}
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+	export default {
+		name: 'avatar',
+		props: {
+			user: {
+				type: Object,
+				default: () => {
+					return {
+						name: ''
+					}
+				}
+			},
+			size: {
+				type: Number,
+				default: 14
+			}
+		},
+		data() {
+			return {
+				lineHeight: 16
+			}
+		},
+		computed: {
+			name() {
+				if (!this.user.name) return '';
+				let name = this.user.name.substring(0, 1);
+				let strCode = name.charCodeAt();
+				if (strCode >= 97 && strCode <= 122) {
+					this.lineHeight = this.size + 2
+				} else {
+					this.lineHeight = this.size;
+				}
+				return name
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.hui-avatar {
+		width: 100%;
+		height: 100%;
+
+		.el-image {
+			width: 100%;
+			height: 100%;
+			display: block;
+
+			.avatar-slot {
+				background: $--color-primary;
+				width: 100%;
+				height: 100%;
+				display: flex;
+				flex-direction: column;
+				justify-content: center;
+				align-items: center;
+				color: #fff;
+			}
+		}
+	}
+</style>

+ 101 - 0
virgo.wzfrontend/workark/src/components/common/cc.vue

@@ -0,0 +1,101 @@
+<template>
+	<div class="cc-user-list">
+		<el-button size="small" @click="visible = true" v-if="type === 'insert'">
+			<i class="iconfont huifont-xinzeng"></i>{{'添加'+(!!label ? label:'抄送人')}}
+		</el-button>
+		<div class="cc-user-ivewer">
+			<div class="cc-user-item" v-for="(item,index) in list" :key="index">
+				<div class="cc-user-avatar">
+					<avatar :user="item" :size="10"></avatar>
+				</div>
+				<div class="cc-user-name">{{item.name}}</div>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" :title="(!!label ? label:'抄送人')+'选择'" :visible.sync="visible" width="880px" :append-to-body="true">
+			<select-user @callback="callBack" v-if="visible" :list="list" :maxLen="maxLen"></select-user>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+	import selectUser from '@/components/common/selectUser'
+	export default {
+		props: ['type', 'ccList', 'label', 'maxLen'],
+		data() {
+			return {
+				list: [],
+				visible: false
+			}
+		},
+		mounted() {
+			if (!!this.ccList) this.list = this.ccList;
+		},
+		methods: {
+			callBack(data) {
+				this.visible = false;
+				if (data) this.list = data;
+			}
+		},
+		watch: {
+			ccList() {
+				if (!!this.ccList) this.list = this.ccList;
+			}
+		},
+		components: {
+			selectUser
+		}
+	}
+</script>
+
+<style lang="scss">
+	.cc-user-list {
+		width: 100%;
+
+		.el-button {
+			padding-left: 12px;
+			margin-bottom: 10px;
+
+			span {
+				display: flex;
+				align-items: center;
+			}
+
+			.huifont-xinzeng {
+				padding-right: 10px;
+			}
+		}
+
+		.cc-user-ivewer {
+			display: flex;
+			flex-wrap: wrap;
+		}
+
+		.cc-user-item {
+			display: flex;
+			min-width: 76px;
+			height: 28px;
+			align-items: center;
+			margin-right: 15px;
+			background: #363f51;
+			border-radius: 15px;
+			padding: 0 4px;
+
+			.cc-user-avatar {
+				width: 20px;
+				height: 20px;
+				border-radius: 50%;
+				overflow: hidden;
+				margin-right: 4px;
+			}
+
+			.cc-user-name {
+				overflow: hidden;
+			}
+		}
+
+		.cc-user-item:first-child {
+			margin-top: 0;
+		}
+
+	}
+</style>

+ 63 - 0
virgo.wzfrontend/workark/src/components/common/city.vue

@@ -0,0 +1,63 @@
+<template>
+	<div class="city">
+		<el-cascader ref="cascader" :props="propsOption" :options="mapDatas" separator="-" v-model="address"
+			:placeholder="'请选择地址'" @change="changeInput" :disabled="disabled">
+		</el-cascader>
+	</div>
+</template>
+
+<script>
+	//区域代码
+	import mapData from '@/uitls/map.js';
+	export default {
+		model: {
+			prop: 'value',
+			event: 'change'
+		},
+		props: {
+			value: [Array],
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				propsOption: {
+					label: 'value'
+				},
+				mapDatas: [],
+				address: []
+			}
+		},
+		mounted() {
+			this.address = this.value;
+			this.mapDatas = mapData.data;
+		},
+		methods: {
+			changeInput() {
+				this.$emit('change', this.address)
+			},
+			addressCode() {
+				if (!this.$refs.cascader.getCheckedNodes()[0]) return '';
+				return this.$refs.cascader.getCheckedNodes()[0].parent.data.label;
+			}
+		},
+		watch: {
+			value(val) {
+				this.address = val;
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.city {
+		display: flex;
+		width: 100%;
+
+		.el-cascader {
+			width: 100%;
+		}
+	}
+</style>

+ 378 - 0
virgo.wzfrontend/workark/src/components/common/selectUser.vue

@@ -0,0 +1,378 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<div class="change-user-box">
+				<div class="change-part">
+					<div class="part-breadcrumb">
+						<div class="part-breadcrumb-item" v-for="(item,index) in breadcrumb" :key="index"
+							@click="breadcrumbClick(item,index)">
+							{{item.name}}
+							<i class="iconfont huifont-xiala-right" v-if="index < breadcrumb.length-1"></i>
+						</div>
+					</div>
+					<el-checkbox class="check-all" v-model="checkAll" @change="checkChange" v-if="!maxLen">
+						全选
+					</el-checkbox>
+					<div class="part-user no-box" v-if="partList.length === 0 && userList.length === 0">
+						<empty width="100"></empty>
+					</div>
+					<div class="part-user" v-else>
+						<el-checkbox-group v-model="checkedBox" @change="testCheckedAll">
+							<el-checkbox v-for="(item,index) in partList" :label="item.id" :key="-item.id" disabled>
+								<div class="user-box">
+									<div class="user-avatar">
+										<i class="iconfont huifont-bumen"></i>
+									</div>
+									<div class="user-name">{{item.name}}</div>
+									<div class="part-button" @click="openPart(item)">展开</div>
+								</div>
+							</el-checkbox>
+							<el-checkbox v-for="(item,index) in userList" :label="item.id" :key="item.id"
+								@change="selectUser" :disabled="returnDisabled(item)">
+								<div class="user-box">
+									<div class="user-avatar">
+										<avatar :user="item" :size="12"></avatar>
+									</div>
+									<div class="user-name">{{item.name}}</div>
+								</div>
+							</el-checkbox>
+						</el-checkbox-group>
+					</div>
+				</div>
+				<div class="change-user">
+					<div class="change-user-operation">
+						<div class="change-user-count">
+							已选择:{{checkedUserList.length}}名用户
+						</div>
+						<div class="change-user-clear" @click="clearAll">清空</div>
+					</div>
+					<div class="change-user-list no-box" v-if="checkedUserList.length === 0">
+						<empty width="100"></empty>
+					</div>
+					<div class="change-user-list" v-else>
+						<div class="change-user-item user-box" v-for="(item,index) in checkedUserList" :label="item.id"
+							:key="item.id">
+							<div class="user-avatar">
+								<avatar :user="item" :size="12"></avatar>
+							</div>
+							<div class="user-name">
+								<div>{{item.name}}</div>
+								<p>{{item.partName}}</p>
+							</div>
+							<div class="user-delete" @click="deleteUser(item)">
+								<i class="iconfont huifont-guanbi"></i>
+							</div>
+							<div class="user-line" v-if="index < checkedUserList.length-1"></div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="submit">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		getPartList
+	} from '@/api/organization'
+	export default {
+		props: ['list', 'type', 'maxLen'],
+		data() {
+			return {
+				checkAll: false,
+				partList: [],
+				allUserList: [],
+				userList: [],
+				checkedBox: [],
+				breadcrumb: [{
+					id: 0,
+					name: '部门'
+				}],
+				checkedUserList: [],
+				partId: ''
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				getPartList(this.$store.getters.organization.id, -1).then(res => {
+					if (res.state) {
+						this.returnChildren(res.data); //获取总员工数列表
+						this.partList = res.data;
+						this.breadcrumb[0]['node'] = this.partList;
+						if (this.list && this.list.length > 0) {
+							this.checkedBox = this.list.map(node => node.id);
+							this.checkedUserList = this.list;
+						}
+					}
+				})
+			},
+			returnChildren(data) {
+				data.forEach(item => {
+					if (item.children && item.users) {
+						let obj = item.users.map(res => {
+							return {
+								id: res.id,
+								name: res.name,
+								partName: item.name,
+								partId: item.id,
+								portrait: res.portrait
+							};
+						})
+						this.allUserList = this.allUserList.concat(obj);
+					}
+					if (item.children && item.children.length > 0) this.returnChildren(item.children);
+				});
+			},
+			returnDisabled(item) {
+				if (this.maxLen === this.checkedBox.length) {
+					if (this.checkedBox.filter(node => node === item.id).length > 0) return false;
+					return true;
+				}
+				return false;
+			},
+			checkChange(val) {
+				if (this.userList.length === 0) return;
+				if (val) {
+					let data = this.checkedBox;
+					this.checkedBox = Array.from(new Set(data.concat(this.userList.map(node => node.id))));
+				} else {
+					let ids = this.userList.map(node => node.id);
+					for (var i = 0; i < ids.length; i++) {
+						let boxIndex = this.checkedBox.findIndex(node => node === ids[i]);
+						this.checkedBox.splice(boxIndex, 1);
+					}
+				}
+				this.selectUser();
+			},
+			selectUser() {
+				this.checkedUserList = this.checkedBox.map(node => {
+					return this.allUserList.filter(user => user.id === node)[0];
+				})
+			},
+			deleteUser(item) {
+				let boxIndex = this.checkedBox.findIndex(node => node === item.id);
+				this.checkedBox.splice(boxIndex, 1);
+				this.selectUser();
+				this.testCheckedAll();
+			},
+			clearAll() {
+				this.checkedBox = [];
+				this.selectUser();
+				this.testCheckedAll();
+			},
+			openPart(data) {
+				this.breadcrumb.push({
+					id: data.id,
+					name: data.name,
+					node: data
+				});
+				this.filterList(data);
+			},
+			filterList(data) {
+				this.partList = data.children;
+				this.partId = data.id;
+				this.userList = this.allUserList.filter(node => node.partId === data.id);
+				this.testCheckedAll();
+			},
+			testCheckedAll() {
+				if (this.userList.length === 0) return this.checkAll = false;
+				let checkedCount = this.checkedBox.filter(node => this.userList.filter(res => res.id === node).length > 0)
+					.length;
+				this.checkAll = checkedCount === this.userList.length;
+			},
+			breadcrumbClick(item, index) {
+				if (index === this.breadcrumb.length - 1) return;
+				this.breadcrumb = this.breadcrumb.slice(0, index + 1);
+				if (item.id === 0) {
+					this.userList = [];
+					return this.partList = item.node;
+				}
+				this.filterList(this.breadcrumb[this.breadcrumb.length - 1].node);
+			},
+			submit() {
+				this.$emit('callback', this.checkedUserList);
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.change-user-box {
+		width: 100%;
+		height: 100%;
+		border: 1px solid $--border-color-base;
+		border-radius: 4px;
+		display: flex;
+
+		.no-box {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			border-top: 1px solid $--border-color-base;
+		}
+
+		.change-part {
+			flex: 1;
+			border-right: 1px solid $--border-color-base;
+			height: 100%;
+			display: flex;
+			flex-direction: column;
+
+			.part-breadcrumb {
+				display: flex;
+				flex-wrap: wrap;
+				padding: 20px;
+
+				.part-breadcrumb-item {
+					display: flex;
+					align-items: center;
+					opacity: 0.5;
+					cursor: pointer;
+
+					i {
+						font-size: 14px;
+						padding: 0 5px;
+					}
+				}
+
+				.part-breadcrumb-item:last-child {
+					opacity: 1;
+				}
+			}
+
+			.check-all {
+				padding: 0px 20px 10px 20px;
+			}
+
+			.part-user {
+				flex: 1;
+				overflow-y: auto;
+				padding: 0 20px 10px 20px;
+
+				.el-checkbox-group {
+					.el-checkbox {
+						display: flex;
+						align-items: center;
+						height: 48px;
+
+					}
+
+					.el-checkbox__input.is-disabled+span.el-checkbox__label {
+						cursor: pointer;
+					}
+
+					.el-checkbox {
+						margin-right: 0;
+					}
+
+					.el-checkbox__label {
+						flex: 1;
+					}
+				}
+			}
+		}
+
+		.user-box {
+			display: flex;
+			align-items: center;
+
+			.user-avatar {
+				width: 32px;
+				height: 32px;
+				background: #4958de;
+				border-radius: 50%;
+				margin-right: 10px;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				overflow: hidden;
+
+				i {
+					font-size: 20px;
+				}
+			}
+
+
+			.user-name {
+				flex: 1;
+				width: 0;
+
+				p {
+					font-size: 12px;
+					opacity: 0.6;
+				}
+			}
+
+			.part-button {
+				color: $--color-primary;
+			}
+
+			.user-delete {
+
+				width: 24px;
+				height: 24px;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				cursor: pointer;
+				border-radius: 2px;
+
+				i {
+					margin-top: 2px;
+					opacity: 0.5;
+					font-size: 14px;
+				}
+			}
+
+			.user-delete:hover {
+				background: #31353f;
+			}
+		}
+
+		.change-user {
+			flex: 1;
+			height: 100%;
+			display: flex;
+			flex-direction: column;
+
+			.change-user-operation {
+				padding: 20px;
+				display: flex;
+				justify-content: space-between;
+
+				.change-user-clear {
+					color: $--color-primary;
+					cursor: pointer;
+				}
+			}
+
+			.change-user-list {
+				flex: 1;
+				height: 0;
+				overflow-y: auto;
+				padding: 0 20px 14px 20px;
+
+				.change-user-item {
+					height: 48px;
+					position: relative;
+
+					.user-line {
+						width: 2px;
+						background-color: rgba(255, 255, 255, 0.2);
+						position: absolute;
+						top: 40px;
+						bottom: -8px;
+						left: 15px;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 359 - 0
virgo.wzfrontend/workark/src/components/common/upload.vue

@@ -0,0 +1,359 @@
+<template>
+	<div class="common-update">
+		<div v-if="type === 'preview'">
+			<div class="no-tips" v-if="fileList.length === 0">暂无附件</div>
+			<div class="common-perview-image" v-else>
+				<div class="common-update-image-box" v-for="(item,index) in fileList" :key="item.id">
+					<div class="el-image video-image" v-if="item.type === 'mp4'">
+						<div class="video-icon">
+							<i class="el-icon-video-play" @click="openVideo(item)"></i>
+						</div>
+						<img :src="videoPreviewList[item.id]" class="el-image__inner">
+					</div>
+					<el-image :src="item.url" :preview-src-list="[item.url]" v-else>
+						<div slot="error" class="image-slot">
+							<el-tooltip class="item" effect="dark" :content="item.name" placement="bottom">
+								<div class="image-text">
+									<span>{{item.type.toUpperCase()}}</span>
+									<div class="file-download">
+										<i class="el-icon-download" @click="download(item)"></i>
+									</div>
+								</div>
+							</el-tooltip>
+						</div>
+					</el-image>
+				</div>
+			</div>
+		</div>
+		<div v-else>
+			<div class="common-update-image">
+				<div class="common-update-image-box" v-for="(item,index) in fileList" :key="item.id">
+					<div class="el-image video-image" v-if="item.type === 'mp4'">
+						<div class="video-icon">
+							<i class="el-icon-video-play" @click="openVideo(item)"></i>
+						</div>
+						<img :src="videoPreviewList[item.id]" class="el-image__inner">
+					</div>
+					<el-image :src="item.url" :preview-src-list="[item.url]" v-else>
+						<div slot="error" class="image-slot">
+							<el-tooltip class="item" effect="dark" :content="item.name" placement="bottom">
+								<div class="image-text">{{item.type.toUpperCase()}}</div>
+							</el-tooltip>
+						</div>
+					</el-image>
+					<div class="common-update-close" @click="removeFile(index)">
+						<i class="el-icon-close"></i>
+					</div>
+				</div>
+				<el-upload :action="action" :headers="headers" :show-file-list="false" name="uploadFile"
+					:before-upload="beforeUpload" :on-success="successFile" :on-error="errorFile" :accept="accept"
+					v-if="fileList.length < maxLen" :on-progress="progress" :limit="maxLen" :on-exceed="handleExceed"
+					:multiple="maxLen === 1 ? false : true">
+					<div class="common-update-button">
+						<i class="iconfont huifont-xinzeng"></i>
+						<div class="common-update-button-label">{{text}}</div>
+					</div>
+				</el-upload>
+			</div>
+			<div class="update-image-tips"><span class="color-red">*</span> 请上传小于10M的文件</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" :title="video.name" custom-class="monitor-dialog"
+			:visible.sync="visible" width="900px" height="500px" :append-to-body="true">
+			<div class="video-box">
+				<video v-if="visible" width="900" height="445" controls>
+					<source :src="video.url" type="video/mp4" />
+				</video>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+	import config from '@/config';
+	import {
+		getToken
+	} from '@/uitls/auth';
+	export default {
+		props: {
+			list: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			type: {
+				type: String,
+				default: 'preview'
+			},
+			maxLen: {
+				type: Number,
+				default: 5
+			},
+			text: {
+				type: String,
+				default: '上传图片'
+			},
+			accept: {
+				type: String,
+				default: '.png, .jpg, .jpeg'
+			},
+		},
+		data() {
+			return {
+				action: config.baseURL + '/file/filenode/-1', //上传地址
+				headers: {
+					token: ''
+				},
+				url: '',
+				srcList: [],
+				fileList: [],
+				videoPreviewList: {},
+				visible: false,
+				video: {}
+			};
+		},
+		mounted() {
+			this.headers.token = getToken();
+			this.fileList = JSON.parse(JSON.stringify(this.list));
+		},
+		methods: {
+			beforeUpload(file) {
+				//上传前
+				if ((parseInt(file.size) / 1024 / 1024) > 10) {
+					//判断上传的文件不大于30M
+					this.$message.warning('请上传小于10M的文件');
+					return false;
+				}
+			},
+			progress(e) {
+				let percent = e.percent >= 100 ? 99 : parseInt(e.percent)
+				this.$loading({
+					percent: (percent + '%')
+				});
+			},
+			successFile(response, file, fileList) {
+				//上传成功
+				if (!response.data) return this.errorFile();
+				this.$message.success('上传成功');
+				let data = response.data;
+				let typeList = data.name.split('.');
+				this.fileList.push({
+					id: data.id,
+					name: data.name,
+					url: data.node.url,
+					type: typeList[typeList.length - 1]
+				});
+				this.$loading.close();
+			},
+			errorFile() {
+				//上传失败
+				this.$message.error('上传失败');
+				this.$loading.close();
+			},
+			removeFile(index) {
+				this.$confirm('确定要删除该文件?', () => {
+					this.fileList.splice(index, 1);
+				});
+			},
+			download(item) {
+				window.location.href = config.baseURL + '/file/filenode/' + item.id;
+			},
+			openVideo(item) {
+				this.video = item;
+				this.visible = true;
+			},
+			handleExceed(files, fileList) {
+				this.$message.warning('最多上传' + this.maxLen + '个文件,当前已上传' + fileList.length + '个文件,请重新选择');
+			},
+			mp4Preview(item) {
+				const video = document.createElement('video') // 也可以自己创建video
+				video.src = item.url // url地址 url跟 视频流是一样的
+				video.crossOrigin = '*' // 解决跨域问题,也就是提示污染资源无法转换视频
+				video.currentTime = 1 // 第一帧
+				video.oncanplay = () => {
+					let canvas = document.createElement('canvas') // 获取 canvas 对象
+					const ctx = canvas.getContext('2d') // 绘制2d
+					canvas.width = video.clientWidth ? video.clientWidth : 320 // 获取视频宽度
+					canvas.height = video.clientHeight ? video.clientHeight : 320 //获取视频高度
+					// 利用canvas对象方法绘图
+					ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
+					// 转换成base64形式
+					const videoFirstImgsrc = canvas.toDataURL('image/png') // 截取后的视频封面
+					this.videoPreviewList[item.id] = videoFirstImgsrc;
+					this.$forceUpdate();
+					video.remove()
+					canvas.remove();
+				}
+			}
+		},
+		watch: {
+			list(val) {
+				this.fileList = JSON.parse(JSON.stringify(val));
+			},
+			fileList() {
+				for (let i = 0; i < this.fileList.length; i++) {
+					if (this.fileList[i].type === 'mp4') {
+						this.mp4Preview(this.fileList[i])
+					}
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.el-dialog.monitor-dialog {
+		height: 500px;
+		margin-top: 20vh !important;
+	}
+
+	.common-update {
+		display: flex;
+		flex-wrap: wrap;
+
+		.update-image-tips {
+			font-size: 12px;
+			opacity: 0.7;
+		}
+
+		.video-image {
+			cursor: pointer;
+			position: relative;
+
+			&:hover {
+				.video-icon {
+					opacity: 1;
+				}
+			}
+
+			.video-icon {
+				position: absolute;
+				top: 0;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				background: rgba(0, 0, 0, 0.6);
+				opacity: 0;
+				transition: opacity 200ms;
+
+				i {
+					font-size: 24px;
+				}
+			}
+		}
+
+		.common-update-button {
+			width: 100px;
+			height: 100px;
+			border: 1px dashed $--border-color-base;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			margin: 4px 0;
+
+			.huifont-xinzeng {
+				line-height: 20px;
+				font-size: 20px;
+			}
+
+			.common-update-button-label {
+				font-size: 14px;
+				line-height: 20px;
+				padding-top: 10px;
+			}
+		}
+
+		.common-perview-image {
+			display: flex;
+			flex-wrap: wrap;
+
+			.el-image {
+				width: 64px;
+				height: 64px;
+				border-radius: 2px;
+				background: transparent;
+			}
+		}
+
+		.image-slot {
+			width: 100%;
+			height: 100%;
+			border-radius: 4px;
+			background: #253642;
+			position: relative;
+
+			.file-download {
+				position: absolute;
+				display: none;
+				align-items: center;
+				justify-content: center;
+				font-size: 20px;
+				background: rgba(0, 0, 0, 0.8);
+				top: 0;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				color: #fff;
+				cursor: pointer;
+			}
+
+			.image-text {
+				font-size: 20px;
+				width: 100%;
+				height: 100%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				color: #fff;
+				font-weight: bold;
+
+				&:hover {
+					.file-download {
+						display: flex;
+					}
+				}
+			}
+
+		}
+
+		.common-update-image-box {
+			display: flex;
+			position: relative;
+			margin-right: 4px;
+		}
+
+		.common-update-image {
+			display: flex;
+			flex-wrap: wrap;
+
+			.common-update-image-box {
+				margin: 4px 8px 4px 0;
+			}
+
+			.el-image {
+				width: 100px;
+				height: 100px;
+				border-radius: 2px;
+				background: transparent;
+			}
+
+			.common-update-close {
+				position: absolute;
+				right: -7px;
+				top: -7px;
+				background: $--color-danger;
+				width: 14px;
+				height: 14px;
+				line-height: 14px;
+				text-align: center;
+				border-radius: 50%;
+				font-size: 10px;
+				cursor: pointer;
+				color: #fff;
+			}
+		}
+	}
+</style>

+ 18 - 0
virgo.wzfrontend/workark/src/components/work/organization/depart/detail.vue

@@ -0,0 +1,18 @@
+<template>
+	<div class="hui-detail">
+		detail
+	</div>
+</template>
+
+<script>
+	export default {
+		props: ['detail'],
+		data() {
+			return {}
+		},
+		mounted() {},
+	}
+</script>
+<style lang="scss">
+
+</style>

+ 93 - 0
virgo.wzfrontend/workark/src/components/work/organization/depart/edit.vue

@@ -0,0 +1,93 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-form ref="departmentForm" :model="departmentForm" label-position="top">
+				<el-form-item label="部门名称">
+					<el-input type="text" v-model="departmentForm.name" placeholder="请输入部门名称"></el-input>
+				</el-form-item>
+				<el-form-item label="部门描述" class="hui-textarea">
+					<el-input type="textarea" v-model="departmentForm.remark" placeholder="请输入部门描述" resize="none">
+					</el-input>
+				</el-form-item>
+				<el-form-item label="部门负责人" class="hui-textarea">
+					<cc ref="cc" type="insert" :ccList="ccList" label="负责人"></cc>
+				</el-form-item>
+				<el-form-item label="部门职责" class="hui-textarea">
+					<upload ref="upload" :list="responsibility" type="insert"></upload>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="submit" :loading="loading">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		insertDepartment,
+		updateDepartment
+	} from '@/api/organization'
+	import upload from '@/components/common/upload'
+	import cc from '@/components/common/cc'
+	export default {
+		props: ['isUpdate', 'part', 'projectId'],
+		data() {
+			return {
+				departmentForm: {
+					name: '',
+					remark: '',
+					organizationId: '',
+					projectId: '',
+					parentId: -1,
+					isp: 0
+				},
+				departmentRuler: {},
+				responsibility: [],
+				ccList: [],
+				loading: false
+			}
+		},
+		mounted() {
+			if (this.isUpdate) {
+				this.departmentForm = this.part;
+				this.responsibility = this.part.responsibility ? JSON.parse(this.part.responsibility) : [];
+				this.ccList = this.part.responsible ? JSON.parse(this.part.responsible) : [];
+			} else {
+				this.departmentForm['organizationId'] = this.$store.getters.organization.id;
+				this.departmentForm['parentId'] = this.part.id || -1;
+				this.departmentForm['projectId'] = this.$store.getters.project.id;
+			}
+		},
+		methods: {
+			submit() {
+				this.loading = true;
+				this.departmentForm['responsibility'] = JSON.stringify(this.$refs.upload.fileList);
+				this.departmentForm['responsible'] = JSON.stringify(this.$refs.cc.list);
+				if (this.isUpdate) {
+					updateDepartment(this.departmentForm).then(this.successFunc)
+				} else {
+					insertDepartment(this.departmentForm).then(this.successFunc)
+				}
+			},
+			successFunc(res) {
+				this.loading = false;
+				if (res.state) {
+					this.$message.success('操作成功');
+					this.$emit('callback', 'init');
+				}
+			}
+		},
+		components: {
+			upload,
+			cc
+		},
+	}
+</script>
+
+<style lang="scss">
+	.department-form {
+		padding: 10px;
+	}
+</style>

+ 125 - 0
virgo.wzfrontend/workark/src/components/work/organization/manager/edit.vue

@@ -0,0 +1,125 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-form ref="informationForm" label-position="top" :model="informationForm">
+				<el-form-item label="组织代码">
+					<el-input type="text" :value="informationForm.organizationCode" disabled></el-input>
+				</el-form-item>
+				<el-form-item label="组织名称" prop="name">
+					<el-input type="text" v-model="informationForm.name"></el-input>
+				</el-form-item>
+				<el-form-item label="联系人" prop="contact">
+					<el-input type="text" v-model="informationForm.contact"></el-input>
+				</el-form-item>
+				<el-form-item label="联系方式" prop="contactTel">
+					<el-input type="text" v-model="informationForm.contactTel" disabled></el-input>
+				</el-form-item>
+				<el-form-item label="组织地址" prop="address">
+					<city v-model="informationForm.address"></city>
+				</el-form-item>
+				<el-form-item label="详细地址">
+					<el-input type="text" v-model="informationForm.detailAddress"></el-input>
+				</el-form-item>
+				<el-form-item label="公司简介" class="hui-textarea">
+					<el-input type="textarea" v-model="informationForm.introduction" placeholder="请输入公司简介"
+						resize="none">
+					</el-input>
+				</el-form-item>
+				<el-form-item label="LOGO" class="hui-textarea">
+					<upload ref="uploadLogo" :list="informationForm.logo" :maxLen="1" type="insert" text="上传LOGO">
+					</upload>
+				</el-form-item>
+				<el-form-item label="附件" class="hui-textarea">
+					<upload ref="upload" :list="informationForm.businessLicense" type="insert">
+					</upload>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" :loading="loading" type="primary" @click="submit">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import upload from '@/components/common/upload'
+	import city from '@/components/common/city'
+	import {
+		updateOrganization,
+	} from '@/api/organization'
+	export default {
+		data() {
+			return {
+				informationForm: {
+					organizationCode: '',
+					name: '',
+					contact: '',
+					contactTel: '',
+					address: [],
+					businessLicense: [],
+					logo: [],
+					detailAddress: '',
+					introduction: ''
+				},
+				isUpdate: true,
+				loading: false
+			}
+		},
+		mounted() {
+			let obj = JSON.parse(JSON.stringify(this.$store.getters.organization));
+			obj.address = obj['address'] ? JSON.parse(obj.address) : [];
+			obj.businessLicense = obj['businessLicense'] ? JSON.parse(obj.businessLicense) : [];
+			obj.logo = obj['logo'] ? [JSON.parse(obj.logo)] : [];
+			this.informationForm = Object.assign(this.informationForm, obj);
+		},
+		methods: {
+			submit() {
+				let postData = JSON.parse(JSON.stringify(this.informationForm));
+				postData['businessLicense'] = JSON.stringify(this.$refs.upload.fileList);
+				postData['address'] = JSON.stringify(postData.address);
+				postData['logo'] = this.$refs.uploadLogo.fileList.length === 0 ? '{}' : JSON.stringify(this.$refs
+					.uploadLogo.fileList[0]);
+				this.loading = true;
+				updateOrganization(postData).then(res => {
+					if (res.state) {
+						this.$message.success('操作成功');
+						this.$store.dispatch('app/changeOrganization', postData);
+						this.$emit('callback', 'init');
+					}
+					this.loading = false;
+				})
+			}
+		},
+		components: {
+			upload,
+			city
+		},
+	}
+</script>
+
+<style lang="scss">
+	.organization-information {
+		width: 100%;
+		height: 100%;
+
+		.hui-title {
+			.el-icon-edit-outline {
+				font-size: 18px;
+				cursor: pointer;
+				color: $--color-primary;
+				padding: 0 8px;
+			}
+		}
+
+		.el-form {
+			padding-top: 10px;
+			padding-right: 10px;
+		}
+
+		.work-people-user-information-submit {
+			padding: 10px;
+			text-align: right;
+		}
+	}
+</style>

+ 64 - 0
virgo.wzfrontend/workark/src/components/work/organization/seal/detail.vue

@@ -0,0 +1,64 @@
+<template>
+	<div class="hui-detail">
+		<div class="hui-detail-title">基础信息</div>
+		<div class="hui-detail-content">
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">印章名称</div>
+				<div class="hui-detail-value">{{detail.name}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">印章描述</div>
+				<div class="hui-detail-value"> {{detail.comment}}</div>
+			</div>
+		</div>
+		<div class="hui-detail-title">印章图片</div>
+		<div class="hui-detail-content hui-detail-image">
+			<upload ref="upload" :list="responsibility" type="preview">
+			</upload>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		getSealDetailById
+	} from '@/api/organization'
+	import upload from '@/components/common/upload'
+	export default {
+		props: ['detailId'],
+		data() {
+			return {
+				detail: {},
+				responsibility: []
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				getSealDetailById(this.detailId).then(res => {
+					if (res.state) {
+						let data = res.data;
+						this.detail['name'] = data.name;
+						this.detail['comment'] = data.comment;
+						let fileNode = data.fileNode;
+						let typeList = fileNode.name.split('.');
+						this.responsibility = [{
+							id: fileNode.id,
+							name: fileNode.name,
+							url: fileNode.node.url,
+							type: typeList[typeList.length - 1]
+						}];
+					}
+				})
+			}
+		},
+		components: {
+			upload
+		},
+	}
+</script>
+<style lang="scss">
+
+</style>

+ 107 - 0
virgo.wzfrontend/workark/src/components/work/organization/seal/edit.vue

@@ -0,0 +1,107 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-form ref="sealForm" :model="sealForm" label-position="top">
+				<el-form-item label="印章名称">
+					<el-input type="text" v-model="sealForm.name" placeholder="请输入印章名称"></el-input>
+				</el-form-item>
+				<el-form-item label="印章类型" prop="type" :rules="[{required: true, message: '请选择印章类型'}]">
+					<el-select v-model="sealForm.type" placeholder="请选择印章类型">
+						<el-option :label="item.name" :value="item.id" v-for="(item,index) in $field.field.sealType"
+							:key="item.id">
+						</el-option>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="印章描述" class="hui-textarea">
+					<el-input type="textarea" v-model="sealForm.comment" placeholder="请输入印章描述" resize="none">
+					</el-input>
+				</el-form-item>
+				<el-form-item label="印章图片" class="hui-textarea">
+					<upload ref="upload" :list="responsibility" type="insert" :maxLen="1"></upload>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="submit" :loading="loading">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		insertSeal,
+		getSealDetailById,
+		updateSeal
+	} from '@/api/organization'
+	import upload from '@/components/common/upload'
+	export default {
+		props: ['isUpdate', 'detailId'],
+		data() {
+			return {
+				sealForm: {
+					name: '',
+					type: 1,
+					comment: ''
+				},
+				responsibility: [],
+				loading: false
+			}
+		},
+		mounted() {
+			if (this.isUpdate) {
+				getSealDetailById(this.detailId).then(res => {
+					if (res.state) {
+						let data = res.data;
+						this.sealForm['id'] = data.id;
+						this.sealForm['name'] = data.name;
+						this.sealForm['comment'] = data.comment;
+						this.sealForm['type'] = data.type;
+						let fileNode = data.fileNode;
+						let typeList = fileNode.name.split('.');
+						this.responsibility = [{
+							id: fileNode.id,
+							name: fileNode.name,
+							url: fileNode.node.url,
+							type: typeList[typeList.length - 1]
+						}];
+					}
+				})
+			} else {
+				this.sealForm['organizationId'] = this.$store.getters.organization.id;
+			}
+		},
+		methods: {
+			submit() {
+				this.loading = true;
+				let fileList = this.$refs.upload.fileList;
+				if (fileList.length < 1) {
+					this.loading = false;
+					this.$message.warning('请上传印章');
+				}
+				this.sealForm['fileNodeId'] = fileList[0].id;
+				if (this.isUpdate) {
+					updateSeal(this.sealForm).then(this.successFunc)
+				} else {
+					insertSeal(this.sealForm).then(this.successFunc)
+				}
+			},
+			successFunc(res) {
+				this.loading = false;
+				if (res.state) {
+					this.$message.success('操作成功');
+					this.$emit('callback', 'init');
+				}
+			}
+		},
+		components: {
+			upload
+		},
+	}
+</script>
+
+<style lang="scss">
+	.seal-form {
+		padding: 10px;
+	}
+</style>

+ 96 - 0
virgo.wzfrontend/workark/src/components/work/organization/user/detail.vue

@@ -0,0 +1,96 @@
+<template>
+	<div class="hui-detail">
+		<div class="hui-detail-title">基础信息</div>
+		<div class="hui-detail-content">
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">头像</div>
+				<div class="hui-detail-value">
+					<div style="width: 40px;height: 40px;border-radius: 4px;overflow: hidden;">
+						<avatar :user="user" :size="18"></avatar>
+					</div>
+				</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">名称</div>
+				<div class="hui-detail-value">{{user.name}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">性别</div>
+				<div class="hui-detail-value">{{user.sex == 'M'?'男':'女'}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">电话</div>
+				<div class="hui-detail-value">{{user.phone}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">部门</div>
+				<div class="hui-detail-value">{{user.partName}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">岗位</div>
+				<div class="hui-detail-value">{{operateUserInfo.employeePosition || '-'}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">职务</div>
+				<div class="hui-detail-value">{{operateUserInfo.position || '-'}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">入职时间</div>
+				<div class="hui-detail-value">{{operateUserInfo.entryDate || '-'}}</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">工作状态</div>
+				<div class="hui-detail-value">
+					<div class="hui-tag" v-if="operateUserInfo.state === 1">在职</div>
+					<div class="hui-tag hui-tag-warning" v-else-if="operateUserInfo.state === 2">请假</div>
+					<div class="hui-tag hui-tag-error" v-else-if="operateUserInfo.state === 3">离职</div>
+					<span v-else>-</span>
+				</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">空间位置</div>
+				<div class="hui-detail-value">
+					{{operateUserInfo.projectItemTargetRoomName || operateUserInfo.projectItemTargetName || operateUserInfo.projectItemName || '-'}}
+				</div>
+			</div>
+			<div class="hui-detail-item">
+				<div class="hui-detail-label">工作业务</div>
+				<div class="hui-detail-value">{{operateUserInfo.workBusiness || '-'}}</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		getOperationUserInfo
+	} from '@/api/loginRegister'
+	export default {
+		props: ['user'],
+		data() {
+			return {
+				type: 1,
+				operateUserInfo: {}
+			}
+		},
+		mounted() {
+			if (this.user.id) this.init();
+		},
+		methods: {
+			init() {
+				getOperationUserInfo({
+					projectId: this.$store.getters.project.id,
+					operateOrganizationId: this.$store.getters.organization.id,
+					userId: this.user.id
+				}).then(res => {
+					if (res.state) {
+						this.operateUserInfo = res.data || {}
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 103 - 0
virgo.wzfrontend/workark/src/components/work/organization/user/edit.vue

@@ -0,0 +1,103 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-form :model="userForm" :rules="userRulers" ref="userForm" label-position="top">
+				<el-alert title="该账号已存在,请直接保存" show-icon type="success" :closable="false" v-if="alertShow"
+					style="margin-bottom: 10px;"></el-alert>
+				<el-form-item label="手机号" prop="phone">
+					<el-input v-model="userForm.phone" autocomplete="off"></el-input>
+				</el-form-item>
+				<el-form-item label="姓名" prop="name" v-if="show">
+					<el-input v-model="userForm.name" autocomplete="off"></el-input>
+				</el-form-item>
+				<el-form-item label="性别" v-if="show">
+					<el-radio-group v-model="userForm.sex">
+						<el-radio label="M">男</el-radio>
+						<el-radio label="W">女</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="邮箱" v-if="show">
+					<el-input v-model="userForm.email"></el-input>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="submit" :loading="loading">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		insertUser,
+		testPhone,
+		bindProject
+	} from '@/api/organization'
+	export default {
+		props: ['part'],
+		data() {
+			return {
+				userForm: {
+					name: '',
+					sex: 'M',
+					phone: '',
+					email: ''
+				},
+				userRulers: {
+					name: [{
+						required: true,
+						message: '请输入姓名',
+						trigger: 'blur'
+					}],
+					phone: [{
+						required: true,
+						message: '请输入手机号',
+						trigger: 'blur'
+					}, {
+						validator: (rule, value, callback) => {
+							if (!/^1[123456789]\d{9}$/.test(value)) {
+								callback(new Error("请输入正确的手机号"));
+							} else if (value == '18888888888') {
+								callback(new Error("不能添加该号码"));
+							} else {
+								testPhone(value).then(data => {
+									this.show = !data.data;
+									this.alertShow = !this.show;
+									callback()
+								})
+							}
+						},
+						trigger: 'blur'
+					}],
+				},
+				show: false,
+				alertShow: false,
+				loading: false
+			}
+		},
+		mounted() {},
+		methods: {
+			submit() {
+				this.loading = true;
+				this.$nextTick(() => {
+					this.$refs.userForm.validate(valid => {
+						if (valid) {
+							insertUser(this.$store.getters.organization.id, this.part.id, this.userForm)
+								.then(res => {
+									this.loading = false;
+									if (res.state) {
+										this.$emit('callback', 'init');
+										this.$message.success('操作成功');
+									}
+								})
+						} else {
+							this.loading = false;
+							return false;
+						}
+					});
+				});
+			}
+		}
+	}
+</script>

+ 147 - 0
virgo.wzfrontend/workark/src/components/work/organization/user/role.vue

@@ -0,0 +1,147 @@
+<template>
+	<div class="hui-flex hui-dialog role-box">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-tree :data="roleData" :props="defaultProps" show-checkbox node-key="id"
+				:default-checked-keys="checkedKeys" ref="tree" :render-after-expand="false">
+				<div :class="classNode(node)? 'tree-node especially' : 'tree-node'" slot-scope="{ node, data }">
+					<div class="tree-node-label">
+						<span>{{ node.label }}</span>
+					</div>
+				</div>
+			</el-tree>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="updateMenu">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		updateUserRole
+	} from '@/api/organization'
+	export default {
+		props: ['projectId', 'user'],
+		data() {
+			return {
+				roleData: [],
+				defaultProps: {
+					label: 'title'
+				},
+				checkedKeys: []
+			}
+		},
+		mounted() {
+			this.roleData = JSON.parse(JSON.stringify(this.$store.getters.menuData));
+			this.$nextTick(() => {
+				this.cssTree();
+			})
+			if (this.user.id) {
+				this.checkedKeys = [];
+				this.testCheck(this.user.workResources ? JSON.parse(this.user.workResources) : []);
+			}
+		},
+		methods: {
+			classNode(node) {
+				return node.data.isMenu === '2';
+			},
+			cssTree() {
+				let classDomList = document.getElementsByClassName('especially')
+				// 改变这几个样式
+				if (classDomList.length == 0) return;
+				let paddingleft = parseInt(classDomList[0].parentNode.style.paddingLeft);
+				for (let i = 0; i < classDomList.length; i++) {
+					let node = classDomList[i].parentNode.parentNode;
+					node.parentNode.style.paddingLeft = (paddingleft + 24) + 'px';
+					node.parentNode.classList.add('tree-children-list')
+				}
+			},
+			filterArr(opArr, partId) {
+				function circle(opArr) {
+					for (let i = 0; i < opArr.length; i++) {
+						const isAccord = partId.find(r => r === opArr[i].id)
+						if (!isAccord) {
+							opArr.splice(i, 1)
+							i--
+						} else if (opArr[i].children && opArr[i].children.length) {
+							circle(opArr[i].children)
+						}
+					}
+				}
+				circle(opArr)
+				return opArr
+			},
+			testCheck(data) {
+				for (var i = 0; i < data.length; i++) {
+					if (data[i].children && data[i].children.length > 0) {
+						this.testCheck(data[i].children);
+					} else {
+						this.checkedKeys.push(data[i].id);
+					}
+				}
+			},
+			updateMenu() {
+				let resultData = JSON.parse(JSON.stringify(this.$store.getters.menuData));
+				let data = this.$refs.tree.getCheckedNodes(false, true);
+				let partId = data.map(node => node.id);
+				let obj = {
+					resource: JSON.stringify(this.filterArr(resultData, partId)),
+					comment: JSON.stringify(data.filter(item => !!item.index).map(item => item.index))
+				}
+				updateUserRole({
+					workMenus: obj.comment,
+					organizationId: this.$store.getters.organization.id,
+					workResources: obj.resource,
+					userId: this.user.id,
+					projectId: this.$store.getters.project.id
+				}).then(res => {
+					if (res.state) {
+						this.$emit('callback', 'init');
+						this.$message.success('操作成功')
+					}
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.role-box {
+		width: 100%;
+		height: 100%;
+
+		.el-tree {
+			.tree-node {
+				left: 0;
+			}
+
+			.el-tree-node__content>.el-tree-node__expand-icon {
+				padding: 6px;
+			}
+
+			.tree-children-list {
+				.el-tree-node {
+					float: left;
+				}
+
+				.el-tree-node__content {
+					padding: 0 !important;
+				}
+
+				.el-tree-node__expand-icon.el-icon-caret-right {
+					display: none !important;
+				}
+
+				.especially {
+					padding-right: 15px;
+				}
+
+				.el-tree-node__content:hover,
+				.el-tree .el-tree-node:focus>.el-tree-node__content {
+					background: transparent !important;
+				}
+			}
+		}
+	}
+</style>

+ 59 - 0
virgo.wzfrontend/workark/src/components/work/system/main/edit.vue

@@ -0,0 +1,59 @@
+<template>
+	<div class="hui-flex hui-dialog">
+		<div class="hui-flex-box hui-dialog-content">
+			<el-form ref="menuForm" :model="menuForm" label-position="top">
+				<el-form-item label="菜单名称">
+					<el-input v-model="menuForm.title"></el-input>
+				</el-form-item>
+				<el-form-item label="菜单URL">
+					<el-input v-model="menuForm.index"></el-input>
+				</el-form-item>
+				<el-form-item label="菜单图标">
+					<el-input v-model="menuForm.iconClass"></el-input>
+				</el-form-item>
+				<el-form-item label="是否菜单">
+					<el-radio v-model="menuForm.isMenu" label="1">是</el-radio>
+					<el-radio v-model="menuForm.isMenu" label="2">否</el-radio>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="hui-dialog-submit">
+			<el-button size="small" @click="$emit('callback')">取 消</el-button>
+			<el-button size="small" type="primary" @click="submit">保 存</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	export default {
+		props: ['updateData'],
+		data() {
+			return {
+				menuForm: {
+					title: '',
+					index: '',
+					iconClass: '',
+					isMenu: '1'
+				}
+			}
+		},
+		mounted() {
+			let _self = this;
+			if (_self.updateData.id) _self.menuForm = _self.updateData;
+		},
+		methods: {
+			sureIncrease() { //返回新增数据
+				let _self = this;
+				if (_self.menuForm.index == '') delete _self.menuForm.index;
+				return _self.menuForm;
+			},
+			submit() {
+				this.$emit('sure')
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 419 - 0
virgo.wzfrontend/workark/src/config/field.js

@@ -0,0 +1,419 @@
+const projectType = [{
+	id: 1,
+	name: '房屋租赁'
+}, {
+	id: 2,
+	name: '知识产权'
+}];
+const projectTypes = [{
+	id: 1,
+	name: '科技园区'
+}, {
+	id: 2,
+	name: '医院'
+}, {
+	id: 3,
+	name: '商超'
+}, {
+	id: 4,
+	name: '酒店'
+}];
+
+const houseType = [{
+	id: 1,
+	name: '纯写字楼'
+}, {
+	id: 2,
+	name: '商住两用'
+}, {
+	id: 3,
+	name: '商业裙楼'
+}, {
+	id: 4,
+	name: '厂房改造'
+}];
+
+const payWay = [{
+	id: 1,
+	name: '公对公转账'
+}, {
+	id: 2,
+	name: '微信小程序支付'
+}]
+
+const customerType = [{
+	id: 1,
+	name: '潜在客户'
+}, {
+	id: 2,
+	name: '新增客户'
+}, {
+	id: 3,
+	name: '跟进中客户'
+}, {
+	id: 4,
+	name: '成交客户'
+}, {
+	id: 5,
+	name: '流失客户'
+}, {
+	id: 6,
+	name: '洽商客户'
+}];
+
+const followUpState = [{
+	id: 1,
+	name: '初次接触'
+}, {
+	id: 2,
+	name: '沟通中'
+}, {
+	id: 3,
+	name: '已成交'
+}, {
+	id: 4,
+	name: '失败/放弃'
+}, {
+	id: 5,
+	name: '暂不考虑'
+}, {
+	id: 6,
+	name: '待定'
+}, {
+	id: 7,
+	name: '未联系上'
+}];
+
+const fieldType = [{
+	id: 1,
+	name: '输入类型'
+}, {
+	id: 2,
+	name: '选择类型'
+}, {
+	id: 3,
+	name: '日期类型'
+}, {
+	id: 4,
+	name: '单选类型'
+}];
+
+const industryType = [{
+	id: 1,
+	name: '机构组织'
+}, {
+	id: 2,
+	name: '农林牧渔'
+}, {
+	id: 3,
+	name: '建筑建材'
+}, {
+	id: 4,
+	name: '冶金矿产'
+}, {
+	id: 5,
+	name: '交通运输'
+}, {
+	id: 6,
+	name: '信息产业'
+}, {
+	id: 7,
+	name: '机械机电'
+}, {
+	id: 8,
+	name: '轻工食品'
+}, {
+	id: 9,
+	name: '服装纺织'
+}, {
+	id: 10,
+	name: '专业服务'
+}, {
+	id: 11,
+	name: '旅游休闲'
+}];
+
+const rentWay = [{
+	id: 1,
+	name: '固定租金'
+}];
+
+const chargingType = [{
+	id: 1,
+	name: '按月计费'
+}, {
+	id: 2,
+	name: '按季计费'
+}, {
+	id: 3,
+	name: '按年计费'
+}];
+
+const payTime = [{
+	id: 1,
+	name: '当天提醒'
+}, {
+	id: 2,
+	name: '提前一天提醒'
+}, {
+	id: 3,
+	name: '提前三天提醒'
+}, {
+	id: 4,
+	name: '提前五天提醒'
+}];
+
+const unnaturalMonthChargingWay = [{
+	id: 1,
+	name: '按天单价计算'
+}];
+
+const leaseTermWay = [{
+	id: 1,
+	name: '按起始日划分'
+}, {
+	id: 2,
+	name: '延迟一日划分'
+}];
+
+const earnestMoneyType = [{
+	id: 1,
+	name: '租金保证金'
+}, {
+	id: 2,
+	name: '装修保证金'
+}];
+
+const currencyType = [{
+	id: 1,
+	name: '人民币CNY'
+}];
+
+const preferentialType = [{
+	id: 1,
+	name: '免租期'
+}];
+
+const preferentialRentFreeWay = [{
+	id: 1,
+	name: '免租期划入租期'
+}];
+
+const billType = [{
+	id: 1,
+	name: '物业费'
+}, {
+	id: 2,
+	name: '水电费'
+}, {
+	id: 3,
+	name: '车位费'
+}, {
+	id: 4,
+	name: '其他费用'
+}];
+
+const invoiceType = [{
+	id: 1,
+	name: '增值税普通发票'
+}, {
+	id: 2,
+	name: '增值税电子普通发票'
+}];
+
+const serviceWorkWay = [{
+	id: 1,
+	name: '上门修理'
+}, {
+	id: 2,
+	name: '事后维修'
+}, {
+	id: 3,
+	name: '紧急抢修'
+}, {
+	id: 4,
+	name: '预防性维修'
+}, {
+	id: 5,
+	name: '专项修理'
+}, {
+	id: 6,
+	name: '网络维修'
+}];
+
+const clearWorkWay = [{
+	id: 1,
+	name: '日常保洁'
+}, {
+	id: 2,
+	name: '垃圾处理'
+}];
+
+const patrolWorkWay = [{
+	id: 1,
+	name: '日常巡查'
+}];
+
+const supportingFacilities = [{
+	id: 1,
+	name: '交通便利',
+	icon: 'huifont-jiaotong'
+}, {
+	id: 2,
+	name: '免费wifi',
+	icon: 'huifont-wifi'
+}, {
+	id: 3,
+	name: '幼儿教育',
+	icon: 'huifont-zhishiku'
+}, {
+	id: 4,
+	name: '医疗完善',
+	icon: 'huifont-tubiao_-'
+}, {
+	id: 5,
+	name: '靠近公园',
+	icon: 'huifont-gongyuan'
+}, {
+	id: 6,
+	name: '便捷购物',
+	icon: 'huifont-gouwu'
+}];
+
+const modelType = [{
+	id: 1,
+	name: 'GLTF/GLB',
+	accept: '.gltf, .GLTF, .glb, .GLB'
+}, {
+	id: 2,
+	name: 'FBX',
+	accept: '.fbx, .FBX'
+}];
+
+const sealType = [{
+	id: 1,
+	name: '公章'
+}, {
+	id: 2,
+	name: '财务章'
+}, {
+	id: 3,
+	name: '法人章'
+}, {
+	id: 4,
+	name: '项目章'
+}, {
+	id: 5,
+	name: '合同章'
+}, {
+	id: 6,
+	name: '资料章'
+}];
+
+const contractType = [{
+	id: 1,
+	name: '房租租赁'
+}, {
+	id: 2,
+	name: '设备租赁'
+}];
+
+const remindType = [{
+	id: 1,
+	name: '重要提醒'
+}, {
+	id: 2,
+	name: '会议提醒'
+}, {
+	id: 3,
+	name: '收费提醒'
+}, {
+	id: 4,
+	name: '看房提醒'
+}];
+
+const organizationTagList = [{
+	id: 1,
+	name: '国家科技型中小企业',
+	icon: 'iconfont huifont-V',
+	size: '20'
+}, {
+	id: 2,
+	name: '国家高新技术企业',
+	icon: 'iconfont huifont-guojiagaoxinqiye',
+	size: '18'
+}, {
+	id: 3,
+	name: '专精特新企业',
+	icon: 'iconfont huifont-zhuanjingtexinqiye',
+	size: '20'
+}, {
+	id: 4,
+	name: '院士工作站',
+	icon: 'iconfont huifont-yuanshigongzuozhan',
+	size: '16'
+}]
+const floorPlanType = [{
+	id: 1,
+	name: 'BIM模型'
+}, {
+	id: 2,
+	name: '平面图'
+}, {
+	id: 3,
+	name: '系统列表'
+}]
+const findItem = (type, id) => {
+	return eval(type).find(node => node.id == id) || {
+		id: 0,
+		name: '-'
+	};
+};
+
+export default {
+	field: {
+		projectType,
+		projectTypes,
+		houseType,
+		payWay,
+		customerType,
+		followUpState,
+		fieldType,
+		industryType,
+		rentWay,
+		chargingType,
+		payTime,
+		unnaturalMonthChargingWay,
+		leaseTermWay,
+		earnestMoneyType,
+		currencyType,
+		preferentialType,
+		preferentialRentFreeWay,
+		billType,
+		invoiceType,
+		serviceWorkWay,
+		clearWorkWay,
+		patrolWorkWay,
+		supportingFacilities,
+		modelType,
+		sealType,
+		contractType,
+		remindType,
+		organizationTagList,
+		floorPlanType
+	},
+	findTypeName(type, id) {
+		return findItem(type, id).name;
+	},
+	findTypeNameByList(type, ids) {
+		console.log(ids);
+		return ids.split(',').map(id => {
+			return findItem(type, id)
+		});
+	},
+	findTypeNameByListArray(type, ids) {
+		return JSON.parse(ids).map(id => {
+			return findItem(type, id)
+		});
+	}
+}

+ 99 - 0
virgo.wzfrontend/workark/src/layout/components/breadCrumb.vue

@@ -0,0 +1,99 @@
+<template>
+	<div class="bread-crumb">
+		<div class="crumb-box">
+			<div class="crumb-item" v-for="(item,index) in levelList" :key="index">
+				<span>{{item}}</span>
+				<span class="tips" v-if="index !== levelList.length - 1">/</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+	export default {
+		name: 'breadCrumb',
+		data() {
+			return {
+				levelList: []
+			}
+		},
+		watch: {
+			$route() {
+				this.getBreadcrumb();
+			}
+		},
+		mounted() {
+			this.menuList = this.$store.getters.menuData;
+			this.getBreadcrumb();
+		},
+		methods: {
+			getBreadcrumb() {
+				this.levelList = this.findParent(this.menuList, this.$route.path);
+				if (this.levelList.length === 0) this.levelList.push(this.$route.name);
+			},
+			findParent(dataSource, path) {
+				const parentIds = []; // 用于存储所有父节点ID的数组
+				// 定义一个递归函数,用于遍历整棵树并查找子节点的所有父节点
+				function traverse(node, path) {
+					if (node.index === path) { // 如果当前节点的ID等于子节点的ID,则表示已经找到了子节点,可以开始向上查找父节点
+						parentIds.push(node.title);
+						return true; // 返回true表示已经找到了子节点
+					}
+					if (node.children) { // 如果当前节点有子节点,则继续遍历子节点
+						for (const childNode of node.children) {
+							if (traverse(childNode, path)) { // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点
+								parentIds.unshift(node.title);
+								return true;
+							}
+						}
+					}
+					return false; // 如果当前节点不是子节点的父节点,则返回false
+				}
+				// 从根节点开始遍历整棵树,并调用递归函数查找子节点的所有父节点
+				for (const node of dataSource) {
+					if (traverse(node, path)) { // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环
+						break;
+					}
+				}
+				return parentIds; // 返回所有父节点ID的数组
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.bread-crumb {
+		display: flex;
+		justify-content: space-between;
+		position: absolute;
+		top: 60px;
+		left: 176px;
+		right: 12px;
+		transition: left 0.4s;
+		z-index: 8;
+
+		&.small-crumb {
+			left: 76px;
+		}
+
+		.crumb-box {
+			position: relative;
+			padding-top: 1px;
+			display: flex;
+			align-items: center;
+
+			.crumb-item {
+				opacity: 0.5;
+				font-weight: 400;
+			}
+
+			.crumb-item:last-child {
+				opacity: 1;
+			}
+
+			.tips {
+				margin: 0px 3px;
+			}
+		}
+	}
+</style>

+ 35 - 0
virgo.wzfrontend/workark/src/layout/components/subMenu.vue

@@ -0,0 +1,35 @@
+<template>
+	<div>
+		<template v-for="(item,index) in menuData">
+			<div v-if="item.isMenu === '1'" :class="'menu-index-'+menuIndex">
+				<el-submenu :index="item.title"
+					v-if="item.children && item.children.length > 0 && item.children.filter(node=>node.isMenu==='1').length > 0"
+					:key="index">
+					<template slot="title">
+						<i v-if="item.iconClass" :class="item.iconClass + ' icon iconfont'"></i>
+						<span slot="title" class="menu-title">{{item.title}}</span>
+					</template>
+					<sub-menu :menuData="item.children" :menuIndex="menuIndex+1"></sub-menu>
+				</el-submenu>
+				<el-menu-item :index="item.index" v-else :key="index" :name="item.index">
+					<i v-if="item.iconClass" :class="item.iconClass + ' icon'"></i>
+					<span slot="title" class="menu-title">{{item.title}}</span>
+				</el-menu-item>
+			</div>
+		</template>
+	</div>
+</template>
+
+<script>
+	import subMenu from '@/layout/components/subMenu'
+	export default {
+		name: 'sub-menu',
+		props: ['menuData', 'menuIndex'],
+		components: {
+			subMenu
+		}
+	}
+</script>
+
+<style>
+</style>

+ 153 - 0
virgo.wzfrontend/workark/src/layout/components/topNav.vue

@@ -0,0 +1,153 @@
+<template>
+	<div class="work-layout-title">
+		<div class="work-layout-left">
+			<div class="work-layout-image">
+				<img src="https://file-node.oss-cn-shanghai.aliyuncs.com/youji/2f7b4bfaf9e64817bc2cb46f3a632b01"
+					alt="" />
+			</div>
+			<div class="work-layout-label">WORKARK</div>
+		</div>
+		<div class="work-layout-right">
+			<div class="work-layout-badge">
+				<el-badge :is-dot="systemCountStatus">
+					<i class="iconfont huifont-xiaoxi"></i>
+				</el-badge>
+			</div>
+			<el-dropdown @visible-change="value => userDropdown = value" @command="userCommand">
+				<div class="work-layout-user">
+					<div class="work-layout-avatar">
+						<avatar :user="users"></avatar>
+					</div>
+					<i :class="'iconfont huifont-gerenzhongxinxiala'+ (userDropdown ? ' dropdown-icon' : '')"></i>
+				</div>
+				<el-dropdown-menu class="user-dropdown" slot="dropdown">
+					<el-dropdown-item command="user">{{users.name}}</el-dropdown-item>
+					<el-dropdown-item command="detail">个人信息</el-dropdown-item>
+					<el-dropdown-item command="loginout">退出登录</el-dropdown-item>
+				</el-dropdown-menu>
+			</el-dropdown>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		removeToken
+	} from '@/uitls/auth';
+	import {
+		mapGetters
+	} from 'vuex';
+	export default {
+		data() {
+			return {
+				users: {},
+				userDropdown: false,
+				systemCountStatus: false
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				this.users = this.$store.getters.user;
+			},
+			userCommand() {
+
+			}
+		},
+		watch: {
+			user() {
+				this.init();
+			}
+		},
+		computed: {
+			...mapGetters(['user'])
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.work-layout-title {
+		display: flex;
+		height: 52px;
+		position: absolute;
+		left: 0;
+		right: 0;
+		top: 0;
+		justify-content: space-between;
+		background: $--color-white;
+		border-bottom: 1px solid $--border-color-base;
+	}
+
+	.work-layout-left {
+		height: 100%;
+		padding-left: 24px;
+		display: flex;
+		align-items: center;
+	}
+
+	.work-layout-image {
+		width: 36px;
+		height: 36px;
+		border-radius: 4px;
+		overflow: hidden;
+
+		img {
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+	.work-layout-label {
+		padding-left: 10px;
+		font-size: 18px;
+		font-weight: bold;
+		font-weight: 600;
+		margin-right: 77px;
+	}
+
+	.work-layout-right {
+		height: 100%;
+		padding-right: 24px;
+		display: flex;
+		align-items: center;
+	}
+
+	.work-layout-badge {
+		margin-right: 35px;
+		cursor: pointer;
+		margin-left: 20px;
+
+		.iconfont {
+			font-size: 22px;
+		}
+
+		.el-badge__content {
+			border: none;
+		}
+	}
+
+	.work-layout-user {
+		display: flex;
+		align-items: center;
+		cursor: pointer;
+
+		.work-layout-avatar {
+			width: 32px;
+			height: 32px;
+			border-radius: 50%;
+			overflow: hidden;
+			margin-right: 8px;
+		}
+
+		.iconfont {
+			font-size: 14px;
+			transition: all 300ms;
+		}
+
+		.dropdown-icon {
+			transform: rotate(180deg);
+		}
+	}
+</style>

+ 1 - 1
virgo.wzfrontend/workark/src/layout/loginLayout.vue

@@ -121,7 +121,7 @@
 			}
 
 			.content-right {
-				width: 400px;
+				width: 440px;
 				display: flex;
 				align-items: center;
 			}

+ 268 - 0
virgo.wzfrontend/workark/src/layout/workLayout.vue

@@ -0,0 +1,268 @@
+<template>
+	<div class="work-layout">
+		<top-nav></top-nav>
+		<div :class="isCollapse ? 'small-main work-layout-main':'work-layout-main'">
+			<div class="app-main-box">
+				<el-menu router :default-active="$route.path" :collapse="isCollapse" :collapse-transition="false">
+					<sub-menu :menuData="menuList" :menuIndex="1"></sub-menu>
+				</el-menu>
+			</div>
+			<div class="app-main-button" @click="isCollapse = !isCollapse">
+				<i :class="isCollapse?'el-icon-s-unfold':'el-icon-s-fold'"></i>
+				<div class="app-main-icon">收起导航</div>
+			</div>
+		</div>
+		<bread-crumb :class="isCollapse ? 'small-crumb':''"></bread-crumb>
+		<div :class="isCollapse ? 'small-content work-layout-content':'work-layout-content'">
+			<transition name="slide-fade">
+				<router-view :key="key" />
+			</transition>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		mapGetters
+	} from 'vuex';
+	import topNav from './components/topNav'
+	import subMenu from './components/subMenu'
+	import breadCrumb from './components/breadCrumb'
+	export default {
+		data() {
+			return {
+				menuList: [],
+				isCollapse: false
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				this.menuList = this.$store.getters.menuData;
+			}
+		},
+		components: {
+			topNav,
+			subMenu,
+			breadCrumb
+		},
+		computed: {
+			key() {
+				return this.$route.path;
+			},
+			...mapGetters(['menuData'])
+		},
+		watch: {
+			menuData() {
+				this.init();
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.work-layout {
+		width: 100%;
+		height: 100%;
+		background: $--background-color-base;
+
+		.work-layout-main {
+			position: fixed;
+			left: 0;
+			top: 52px;
+			width: 164px;
+			bottom: 0;
+			background: $--color-white;
+			padding: 8px 8px 0px 8px;
+			box-sizing: border-box;
+			display: flex;
+			flex-direction: column;
+			transition: all 0.4s;
+			z-index: 99;
+			border-right: 1px solid $--border-color-base;
+
+			&.small-main {
+				width: 64px;
+				padding: 8px 8px 0px 8px;
+			}
+
+			.app-main-box {
+				flex: 1;
+				height: 0;
+				overflow-y: auto;
+				overflow-x: hidden;
+				-ms-overflow-style: none;
+				/* 隐藏 IE 中的滚动条 */
+				scrollbar-width: none;
+				/* 隐藏 Firefox 中的滚动条 */
+			}
+
+			.app-main-box::-webkit-scrollbar {
+				width: 0;
+				/* 隐藏滚动条 */
+			}
+
+			.el-menu {
+				width: 100%;
+				height: 100%;
+				border-right: none;
+
+				.el-icon-arrow-down {
+					font-family: "iconfont" !important;
+					font-size: 16px;
+				}
+
+				.el-icon-arrow-down::before {
+					content: "\e72d";
+				}
+
+				.el-menu-item,
+				.el-submenu__title {
+					border-radius: 5px;
+					height: 48px;
+					line-height: 46px;
+					font-size: 14px;
+					min-width: auto;
+					margin: 3px 0;
+					padding-right: 10px;
+
+					i.icon {
+						font-size: 24px;
+						margin-right: 8px;
+					}
+
+					&:hover,
+					&.is-active,
+					&:focus {
+						background-color: $--color-primary;
+						color: #fff;
+
+						i,
+						span {
+							color: #fff;
+						}
+					}
+				}
+
+				.el-submenu__title {
+					.el-submenu__icon-arrow {
+						right: 10px;
+					}
+				}
+
+				.menu-index-1 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 12px !important;
+					}
+				}
+
+				.menu-index-2 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 40px !important;
+					}
+				}
+
+				.menu-index-3 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 50px !important;
+					}
+				}
+
+				.menu-index-3 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 60px !important;
+					}
+				}
+
+				.menu-index-4 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 70px !important;
+					}
+				}
+
+				.menu-index-5 {
+
+					.el-menu-item,
+					.el-submenu__title {
+						padding-left: 80px !important;
+					}
+				}
+			}
+
+			.app-main-button {
+				border-top: 1px solid $--table-border-color;
+				padding: 16px 13px;
+				display: flex;
+				cursor: pointer;
+				margin-top: 12px;
+				align-items: center;
+
+				i {
+					font-size: 24px;
+					margin-right: 7px;
+					color: #909399;
+				}
+
+				.app-main-icon {
+					flex: 1;
+					width: 0;
+					overflow: hidden;
+					font-weight: 400;
+					word-spacing: break-all;
+					height: 20px;
+				}
+			}
+		}
+
+		.el-menu--collapse {
+
+			.el-submenu__icon-arrow,
+			.menu-title {
+				display: none;
+			}
+		}
+
+		.work-layout-content {
+			position: absolute;
+			top: 89px;
+			left: 176px;
+			right: 12px;
+			bottom: 12px;
+			background: $--color-white;
+			transition: left 0.4s;
+			border: 1px solid $--border-color-base;
+
+			.slide-fade-enter-active {
+				transition: all 0.1s ease;
+			}
+
+			.slide-fade-leave-active {
+				transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
+			}
+
+			.slide-fade-enter,
+			.slide-fade-leave-to {
+				transform: translateX(10px);
+				opacity: 0;
+			}
+
+			&.small-content {
+				left: 76px;
+			}
+		}
+
+
+	}
+</style>

+ 17 - 2
virgo.wzfrontend/workark/src/main.js

@@ -5,12 +5,27 @@ import store from './store'
 
 //element-ui
 import ElementUI from 'element-ui';
-import './assets/css/element-variables.scss';
 Vue.use(ElementUI);
 
-
 Vue.config.productionTip = false
 
+import avatar from './components/common/avatar'
+Vue.component('avatar', avatar)
+
+import field from './config/field.js'
+Vue.prototype.$field = field;
+
+Vue.prototype.$confirm = (title, callback) => {
+	return ElementUI.MessageBox.confirm(title, 'WORKARK提示', {
+		confirmButtonText: '确 定',
+		cancelButtonClass: 'cancel',
+		confirmButtonClass: 'confirm',
+		cancelButtonText: '取 消',
+		type: 'warning'
+	}).then(callback).catch(() => {});
+};
+
+import './uitls/permission.js'
 import './router/permission.js'
 
 new Vue({

+ 9 - 2
virgo.wzfrontend/workark/src/router/index.js

@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import Router from 'vue-router'
-import loginRouter from './login'
+import loginRouter from './modules/login'
+import workRouter from './modules/work'
 
 Vue.use(Router)
 const originalPush = Router.prototype.push;
@@ -9,8 +10,14 @@ Router.prototype.push = function push(location) {
 };
 
 export default new Router({
-	routes: [loginRouter, { //404
+	routes: [loginRouter, workRouter, { //404
 		path: '/',
 		component: () => import('@/views/index'),
+	}, { //404
+		path: '/404',
+		component: () => import('@/views/error/404'),
+	}, { //401
+		path: '/401',
+		component: () => import('@/views/error/401'),
 	}]
 })

virgo.wzfrontend/workark/src/router/login.js → virgo.wzfrontend/workark/src/router/modules/login.js


+ 25 - 0
virgo.wzfrontend/workark/src/router/modules/work.js

@@ -0,0 +1,25 @@
+import workLayout from '@/layout/workLayout'
+
+const modulesFiles = require.context('./work', true, /\.js$/);
+let child = [{
+	path: '',
+	component: () => import('@/views/work/index'),
+	name: '工作台',
+	meta: {
+		title: '工作台'
+	}
+}];
+
+modulesFiles.keys().reduce((modules, modulePath) => {
+	const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
+	const value = modulesFiles(modulePath);
+	child = child.concat(value.default);
+}, {})
+
+const workRouter = {
+	path: '/work',
+	component: workLayout,
+	children: child
+}
+
+export default workRouter;

+ 30 - 0
virgo.wzfrontend/workark/src/router/modules/work/organization.js

@@ -0,0 +1,30 @@
+const organization = [{
+	path: 'organization/depart',
+	component: () => import('@/views/work/organization/depart'),
+	name: '部门管理',
+	meta: {
+		title: '部门管理'
+	}
+}, {
+	path: 'organization/manager',
+	component: () => import('@/views/work/organization/manager'),
+	name: '公司管理',
+	meta: {
+		title: '公司管理'
+	}
+}, {
+	path: 'organization/user',
+	component: () => import('@/views/work/organization/user'),
+	name: '人员管理',
+	meta: {
+		title: '人员管理'
+	}
+}, {
+	path: 'organization/seal',
+	component: () => import('@/views/work/organization/seal'),
+	name: '印章管理',
+	meta: {
+		title: '印章管理'
+	}
+}]
+export default organization;

+ 9 - 0
virgo.wzfrontend/workark/src/router/modules/work/system.js

@@ -0,0 +1,9 @@
+const system = [{
+	path: 'system/main',
+	component: () => import('@/views/work/system/main'),
+	name: '菜单管理',
+	meta: {
+		title: '菜单管理'
+	}
+}]
+export default system;

+ 3 - 1
virgo.wzfrontend/workark/src/store/modules/app.js

@@ -7,7 +7,9 @@ const state = {
 	menuData: [],
 	user: {},
 	organization: {},
-	project: {},
+	project: {
+		id: -1
+	},
 }
 
 const mutations = {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 13346 - 0
virgo.wzfrontend/workark/src/uitls/map.js


+ 16 - 0
virgo.wzfrontend/workark/src/uitls/permission.js

@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import {
+	auth
+} from '@/uitls/auth'
+Vue.prototype.auth = auth;
+// 注册一个全局自定义指令 `v-permission`
+Vue.directive('permission', {
+	// 当绑定元素挂载到 DOM 上时
+	inserted: function(el, binding, vnode) {
+		// 获取绑定的权限值
+		const permissionValue = binding.value;
+		// 检查用户权限
+		if (auth(permissionValue)) return;
+		el.parentNode.removeChild(el);
+	}
+});

+ 148 - 0
virgo.wzfrontend/workark/src/views/error/401.vue

@@ -0,0 +1,148 @@
+<template>
+	<div class="error">
+		<svg width="380px" height="500px" viewBox="0 0 837 1045" version="1.1" xmlns="http://www.w3.org/2000/svg"
+			xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+			<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+				<path d="M353,9 L626.664028,170 L626.664028,487 L353,642 L79.3359724,487 L79.3359724,170 L353,9 Z"
+					id="Polygon-1" stroke="#007FB2" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M78.5,529 L147,569.186414 L147,648.311216 L78.5,687 L10,648.311216 L10,569.186414 L78.5,529 Z"
+					id="Polygon-2" stroke="#EF4A5B" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M773,186 L827,217.538705 L827,279.636651 L773,310 L719,279.636651 L719,217.538705 L773,186 Z"
+					id="Polygon-3" stroke="#795D9C" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M639,529 L773,607.846761 L773,763.091627 L639,839 L505,763.091627 L505,607.846761 L639,529 Z"
+					id="Polygon-4" stroke="#F2773F" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M281,801 L383,861.025276 L383,979.21169 L281,1037 L179,979.21169 L179,861.025276 L281,801 Z"
+					id="Polygon-5" stroke="#36B455" stroke-width="6" sketch:type="MSShapeGroup"></path>
+			</g>
+		</svg>
+		<div class="message-box">
+			<h1>401</h1>
+			<p>抱歉,您暂时没有访问权限</p>
+			<div class="buttons-con">
+				<div class="action-link-wrap">
+					<a onclick="history.back(-1)" class="link-button link-back-button">返回上一页</a>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+</script>
+
+<style lang="scss">
+	body .error {
+		background-color: #fff;
+
+		svg {
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			margin-top: -250px;
+			margin-left: -400px;
+		}
+
+		.message-box {
+			height: 200px;
+			width: 380px;
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			margin-top: -100px;
+			margin-left: 50px;
+			color: #000;
+			font-family: Roboto;
+			font-weight: 300;
+		}
+
+		.message-box h1 {
+			font-size: 60px;
+			line-height: 46px;
+			margin-bottom: 40px;
+		}
+
+		.buttons-con .action-link-wrap {
+			margin-top: 40px;
+		}
+
+		.buttons-con .action-link-wrap a {
+			background: $--color-primary;
+			padding: 8px 25px;
+			border-radius: 4px;
+			color: #FFF;
+			font-weight: bold;
+			font-size: 14px;
+			transition: all 0.3s linear;
+			cursor: pointer;
+			text-decoration: none;
+			margin-right: 10px
+		}
+
+		.buttons-con .action-link-wrap a:hover {
+			opacity: 0.7;
+			color: #fff;
+		}
+
+		#Polygon-1,
+		#Polygon-2,
+		#Polygon-3,
+		#Polygon-4,
+		#Polygon-4,
+		#Polygon-5 {
+			-webkit-animation: float 1s infinite ease-in-out alternate;
+			animation: float 1s infinite ease-in-out alternate;
+		}
+
+		#Polygon-2 {
+			-webkit-animation-delay: .2s;
+			animation-delay: .2s;
+		}
+
+		#Polygon-3 {
+			-webkit-animation-delay: .4s;
+			animation-delay: .4s;
+		}
+
+		#Polygon-4 {
+			-webkit-animation-delay: .6s;
+			animation-delay: .6s;
+		}
+
+		#Polygon-5 {
+			-webkit-animation-delay: .8s;
+			animation-delay: .8s;
+		}
+
+		@-webkit-keyframes float {
+			100% {
+				-webkit-transform: translateY(20px);
+				transform: translateY(20px);
+			}
+		}
+
+		@keyframes float {
+			100% {
+				-webkit-transform: translateY(20px);
+				transform: translateY(20px);
+			}
+		}
+
+		@media (max-width: 450px) {
+			svg {
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				margin-top: -250px;
+				margin-left: -190px;
+			}
+
+			.message-box {
+				top: 50%;
+				left: 50%;
+				margin-top: -100px;
+				margin-left: -190px;
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 148 - 0
virgo.wzfrontend/workark/src/views/error/404.vue

@@ -0,0 +1,148 @@
+<template>
+	<div class="error">
+		<svg width="380px" height="500px" viewBox="0 0 837 1045" version="1.1" xmlns="http://www.w3.org/2000/svg"
+			xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+			<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+				<path d="M353,9 L626.664028,170 L626.664028,487 L353,642 L79.3359724,487 L79.3359724,170 L353,9 Z"
+					id="Polygon-1" stroke="#007FB2" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M78.5,529 L147,569.186414 L147,648.311216 L78.5,687 L10,648.311216 L10,569.186414 L78.5,529 Z"
+					id="Polygon-2" stroke="#EF4A5B" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M773,186 L827,217.538705 L827,279.636651 L773,310 L719,279.636651 L719,217.538705 L773,186 Z"
+					id="Polygon-3" stroke="#795D9C" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M639,529 L773,607.846761 L773,763.091627 L639,839 L505,763.091627 L505,607.846761 L639,529 Z"
+					id="Polygon-4" stroke="#F2773F" stroke-width="6" sketch:type="MSShapeGroup"></path>
+				<path d="M281,801 L383,861.025276 L383,979.21169 L281,1037 L179,979.21169 L179,861.025276 L281,801 Z"
+					id="Polygon-5" stroke="#36B455" stroke-width="6" sketch:type="MSShapeGroup"></path>
+			</g>
+		</svg>
+		<div class="message-box">
+			<h1>404</h1>
+			<p>找不到页面</p>
+			<div class="buttons-con">
+				<div class="action-link-wrap">
+					<a onclick="history.back(-1)" class="link-button link-back-button">返回上一页</a>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+</script>
+
+<style lang="scss">
+	body .error {
+		background-color: #fff;
+
+		svg {
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			margin-top: -250px;
+			margin-left: -400px;
+		}
+
+		.message-box {
+			height: 200px;
+			width: 380px;
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			margin-top: -100px;
+			margin-left: 50px;
+			color: #000;
+			font-family: Roboto;
+			font-weight: 300;
+		}
+
+		.message-box h1 {
+			font-size: 60px;
+			line-height: 46px;
+			margin-bottom: 40px;
+		}
+
+		.buttons-con .action-link-wrap {
+			margin-top: 40px;
+		}
+
+		.buttons-con .action-link-wrap a {
+			background: $--color-primary;
+			padding: 8px 25px;
+			border-radius: 4px;
+			color: #FFF;
+			font-weight: bold;
+			font-size: 14px;
+			transition: all 0.3s linear;
+			cursor: pointer;
+			text-decoration: none;
+			margin-right: 10px
+		}
+
+		.buttons-con .action-link-wrap a:hover {
+			opacity: 0.7;
+			color: #fff;
+		}
+
+		#Polygon-1,
+		#Polygon-2,
+		#Polygon-3,
+		#Polygon-4,
+		#Polygon-4,
+		#Polygon-5 {
+			-webkit-animation: float 1s infinite ease-in-out alternate;
+			animation: float 1s infinite ease-in-out alternate;
+		}
+
+		#Polygon-2 {
+			-webkit-animation-delay: .2s;
+			animation-delay: .2s;
+		}
+
+		#Polygon-3 {
+			-webkit-animation-delay: .4s;
+			animation-delay: .4s;
+		}
+
+		#Polygon-4 {
+			-webkit-animation-delay: .6s;
+			animation-delay: .6s;
+		}
+
+		#Polygon-5 {
+			-webkit-animation-delay: .8s;
+			animation-delay: .8s;
+		}
+
+		@-webkit-keyframes float {
+			100% {
+				-webkit-transform: translateY(20px);
+				transform: translateY(20px);
+			}
+		}
+
+		@keyframes float {
+			100% {
+				-webkit-transform: translateY(20px);
+				transform: translateY(20px);
+			}
+		}
+
+		@media (max-width: 450px) {
+			svg {
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				margin-top: -250px;
+				margin-left: -190px;
+			}
+
+			.message-box {
+				top: 50%;
+				left: 50%;
+				margin-top: -100px;
+				margin-left: -190px;
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 18 - 75
virgo.wzfrontend/workark/src/views/login/login.vue

@@ -38,7 +38,8 @@
 		login,
 		getImgCode,
 		sendPhoneCode,
-		getUserInfo
+		getUserInfo,
+		selectOrangaized
 	} from '@/api/loginRegister';
 	import {
 		setToken,
@@ -85,7 +86,6 @@
 			};
 		},
 		mounted() {
-			localStorage.removeItem('chatToken')
 			this.imgCodeFunc();
 			if (this.$store.getters.codeNumber != 60) this.codeReset();
 		},
@@ -129,7 +129,7 @@
 				//code操作
 				this.codeName = codeNumber == 0 ? '获取验证码' : '重新获取' + codeNumber;
 				this.isDisabled = codeNumber == 0 ? false : true;
-				this.$store.dispatch('loginRegister/changeCodeNumber', codeNumber == 0 ? 60 : codeNumber);
+				this.$store.dispatch('app/changeCodeNumber', codeNumber == 0 ? 60 : codeNumber);
 			},
 			loginSubmit() {
 				if (this.loginLoading) return;
@@ -150,28 +150,17 @@
 						getUserInfo().then(res => {
 							if (res.state) {
 								let user = res.data;
-								if (user.projectId === -1) {
-									let organziation = user.organizationList.find(node => node
-										.contactTel == user.phone);
-									selectOrangaized(organziation)
-									this.$store.dispatch('app/changeOrganization', organziation);
-									this.$store.dispatch('app/changeUser', user);
-									this.$store.dispatch('projectBase/changeProject', {});
-									this.$store.dispatch('app/changeIdentity', {
-										id: 6,
-										name: '所有者',
-										remark: '所有者'
-									});
-									localStorage.setItem('projectId', 0);
-									return this.successLogin('/work/space/project');
-								} else {
-									this.$store.dispatch('app/changeOrganization', user.organization);
-									this.$store.dispatch('app/changeUser', user);
-									this.$store.dispatch('app/changeMenuData', user.resource ? JSON.parse(
-										user.resource) : []);
-									setComment(user.menu ? user.menu : JSON.stringify([]));
-									this.initProjectData(user.projectId, user.identityId);
+								let organization = user.organization;
+								if (!organization) {
+									organziation = user.organizationList[0];
+									selectOrangaized(organization)
 								}
+								this.$store.dispatch('app/changeOrganization', organization);
+								this.$store.dispatch('app/changeUser', user);
+								this.$store.dispatch('app/changeMenuData', user.workarkResource ? JSON
+									.parse(user.workarkResource) : []);
+								setComment(user.workarkMenu ? user.workarkMenu : JSON.stringify([]));
+								return this.successLogin('/work');
 							} else {
 								this.loginLoading = false;
 							}
@@ -181,57 +170,10 @@
 					}
 				})
 			},
-			initProjectData(projectId, identityId) {
-				getProjectListByIdentity().then(res => {
-					if (res.state) {
-						if (res.data.length === 0) {
-							this.$store.dispatch('projectBase/changeProject', {});
-							this.$store.dispatch('app/changeIdentity', {
-								id: 6,
-								name: '所有者',
-								remark: '所有者'
-							});
-							localStorage.setItem('projectId', 0);
-							return this.successLogin('/work/space/project');
-						}
-						let project = projectId === -1 ? res.data[0] : res.data.find(node => node.id ===
-							projectId);
-						this.$store.dispatch('projectBase/changeProject', project);
-						let identity = identityId === -1 ? project.projectListIdentity[0] : project
-							.projectListIdentity.find(node => node.id === identityId);
-						this.$store.dispatch('app/changeIdentity', identity);
-						localStorage.setItem('projectId', projectId);
-						this.successLogin('/');
-					} else {
-						this.loginLoading = false;
-					}
-				})
-			},
 			successLogin(url) {
-				let item = this.$store.getters.identity;
-				if (item.id === 3 || this.$store.getters.user.userId === 1) {
-					this.loginLoading = false;
-					this.$router.push(url);
-					this.$message.success('登录成功');
-				} else {
-					getIdentityResource({
-						identityId: item.id,
-						type: this.$store.getters.project.type || 1
-					}).then(node => {
-						if (node.state) {
-							let role = node.data[0] || {};
-							this.$store.dispatch('app/changeMenuData', role
-								.resource ? JSON.parse(role.resource) : []);
-							setComment(role.menus ? role.menus : JSON.stringify(
-								[]));
-							this.loginLoading = false;
-							this.$router.push(url);
-							this.$message.success('登录成功');
-						} else {
-							this.loginLoading = false;
-						}
-					})
-				}
+				this.loginLoading = false;
+				this.$router.push(url);
+				this.$message.success('登录成功');
 			}
 		}
 	};
@@ -244,10 +186,11 @@
 		box-shadow: 0px 4px 16px 0px rgba(164, 178, 203, 0.15);
 		border-radius: 8px;
 		border: 0px solid $--color-white;
+		box-sizing: border-box;
 	}
 
 	.login-title {
-		padding: 62px 52px 48px 52px;
+		padding: 52px 52px 48px 52px;
 	}
 
 	.login-title-value {

+ 8 - 0
virgo.wzfrontend/workark/src/views/work/index.vue

@@ -0,0 +1,8 @@
+<template>
+</template>
+
+<script>
+</script>
+
+<style>
+</style>

+ 145 - 0
virgo.wzfrontend/workark/src/views/work/organization/depart.vue

@@ -0,0 +1,145 @@
+<template>
+	<div class="hui-flex hui-content">
+		<div class="hui-content-title">
+			<div class="hui-title-item active">部门管理</div>
+		</div>
+		<div class="hui-flex-box hui-flex hui-table">
+			<div class="hui-content-insert">
+				<el-button v-permission="'/work/organization/depart/add'" type="primary" size="small"
+					@click="insertDepartment({})">新建部门</el-button>
+			</div>
+			<div class="hui-flex-box">
+				<el-table :data="treeData" row-key="id" height="100%">
+					<el-table-column width="50"></el-table-column>
+					<el-table-column label="序号" width="100">
+						<template slot-scope="scope">
+							<span>{{scope.$index + 1}}</span>
+						</template>
+					</el-table-column>
+					<el-table-column label="部门名称" prop="name"></el-table-column>
+					<el-table-column label="部门人数">
+						<template slot-scope="scope">
+							<span>{{!scope.row.users ?0: scope.row.users.length}}</span>
+						</template>
+					</el-table-column>
+					<el-table-column label="部门责任人">
+						<template slot-scope="scope">
+							<div class="hui-table-user" v-if="scope.row.responsible && scope.row.responsible!='[]'">
+								<div class="hui-table-avatar">
+									<avatar :user="JSON.parse(scope.row.responsible)[0]"></avatar>
+								</div>
+								<div>{{JSON.parse(scope.row.responsible)[0].name}}</div>
+							</div>
+							<div v-else>无</div>
+						</template>
+					</el-table-column>
+					<el-table-column label="操作" width="240">
+						<template slot-scope="scope">
+							<div class="hui-table-operation">
+								<span v-permission="'/work/organization/depart/detail'" class="table-operation"
+									@click="lookFlow(scope.row)">
+									详情
+								</span>
+								<span v-permission="'/work/organization/depart/add'" class="table-operation"
+									@click="insertDepartment(scope.row)">
+									添加子部门
+								</span>
+								<span v-permission="'/work/organization/depart/update'" class="table-operation"
+									@click="updateDepartment(scope.row)">
+									编辑
+								</span>
+								<span v-permission="'/work/organization/depart/delete'" class="table-operation"
+									@click="deleteDepartment(scope.row)">
+									删除
+								</span>
+							</div>
+						</template>
+					</el-table-column>
+					<template slot="empty">
+						<el-empty description="暂无数据"></el-empty>
+					</template>
+				</el-table>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" :title="isUpdate?'编辑':'新增'" :visible.sync="visible" width="900px"
+			:append-to-body="true">
+			<edit v-if="visible" @callback="callback" :isUpdate="isUpdate" :part="part">
+			</edit>
+		</el-dialog>
+		<el-drawer title="部门详情" :visible.sync="drawer" :size="400" :append-to-body="true">
+			<detail v-if="drawer" :detail="part"></detail>
+		</el-drawer>
+	</div>
+</template>
+
+<script>
+	import {
+		getPartList,
+		deleteDepartment
+	} from '@/api/organization'
+	import edit from '@/components/work/organization/depart/edit'
+	import detail from '@/components/work/organization/depart/detail'
+	export default {
+		data() {
+			return {
+				treeData: [],
+				visible: false,
+				isUpdate: false,
+				part: {},
+				drawer: false
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				if (!this.auth('/work/organization/depart/list')) return;
+				getPartList(this.$store.getters.organization.id, this.$store.getters.project.id).then(res => {
+					if (res.state) {
+						this.treeData = res.data;
+					}
+				})
+			},
+			currentChange(currPage) {
+				this.currPage = currPage;
+				this.init();
+			},
+			insertDepartment(val) {
+				this.part = JSON.parse(JSON.stringify(val));
+				this.visible = true;
+				this.isUpdate = false;
+			},
+			lookFlow(val) {
+				this.part = JSON.parse(JSON.stringify(val));
+				this.drawer = true;
+			},
+			updateDepartment(val) {
+				this.part = JSON.parse(JSON.stringify(val));
+				this.isUpdate = true;
+				this.visible = true;
+			},
+			deleteDepartment(val) {
+				this.$confirm('确定要删除该部门?', () => {
+					deleteDepartment(val.id).then(res => {
+						if (res.state) {
+							this.$message.success('操作成功');
+							this.init();
+						}
+					})
+				});
+			},
+			callback(type) {
+				this.visible = false;
+				if (type === 'init') this.init();
+			}
+		},
+		components: {
+			edit,
+			detail
+		},
+	}
+</script>
+
+<style lang="scss">
+</style>

+ 199 - 0
virgo.wzfrontend/workark/src/views/work/organization/manager.vue

@@ -0,0 +1,199 @@
+<template>
+	<div class="hui-flex hui-content">
+		<div class="hui-content-title">
+			<div class="hui-title-item active">公司管理</div>
+		</div>
+		<div class="hui-flex-box">
+			<div class="manager-index hui-flex hui-table">
+				<div class="hui-content-insert" style="text-align: right;">
+					<el-button v-permission="'/work/organization/manager/update'" type="primary" size="small" @click="visible = true">修改</el-button>
+				</div>
+				<div class="hui-flex-box">
+					<div class="manager-content">
+						<div class="manager-title">
+							<div class="manager-logo">
+								<el-image v-if="logoUrl" :src="logoUrl" fit="cover"></el-image>
+								<div class="el-image" v-else>
+									<div class="image-slot"><i class="el-icon-picture-outline"></i></div>
+								</div>
+							</div>
+							<div class="manager-name">{{organization.name}}</div>
+						</div>
+						<div class="manager-box">
+							<div class="manager-item">
+								<div class="manager-label">公司代码</div>
+								<div class="manager-value">
+									{{organization.organizationCode}}
+									<i class="el-icon-copy-document color-blue" title="点击复制"
+										@click="copyText(organization.organizationCode)"></i>
+								</div>
+							</div>
+							<div class="manager-item">
+								<div class="manager-label">联系人</div>
+								<div class="manager-value">{{organization.contact}}</div>
+							</div>
+							<div class="manager-item">
+								<div class="manager-label">公司地址</div>
+								<div class="manager-value">{{address}}</div>
+							</div>
+							<div class="manager-item">
+								<div class="manager-label">联系方式</div>
+								<div class="manager-value">{{organization.contactTel}}</div>
+							</div>
+							<div class="manager-item" style="width: 100%;">
+								<div class="manager-label">公司简介</div>
+								<div class="manager-value">{{organization.introduction}}</div>
+							</div>
+							<div class="manager-item" style="width: 100%;">
+								<div class="manager-label">附件</div>
+								<div class="manager-value">
+									<upload
+										:list="organization.businessLicense ? JSON.parse(organization.businessLicense):[]">
+									</upload>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" title="编辑" :visible.sync="visible" width="900px"
+			:append-to-body="true">
+			<edit v-if="visible" @callback="callback"></edit>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+	import upload from '@/components/common/upload'
+	import edit from '@/components/work/organization/manager/edit'
+	export default {
+		data() {
+			return {
+				organization: {},
+				logoUrl: '',
+				address: '',
+				visible: false
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				if (!this.auth('/work/organization/manager/information')) return;
+				this.organization = this.$store.getters.organization;
+				if (this.organization.address) {
+					let address = JSON.parse(this.organization.address);
+					this.address = address.join('-') + '-' + (this.organization.detailAddress || '');
+				}
+				let logo = !this.organization.logo ? {} : JSON.parse(this.organization.logo);
+				this.logoUrl = logo.url;
+
+			},
+			async copyText(code) {
+				try {
+					await navigator.clipboard.writeText(code);
+					this.$message.success('复制成功')
+				} catch (error) {
+					this.$message.success('复制失败')
+				}
+			},
+			callback(type) {
+				this.visible = false;
+				if (type === 'init') this.init();
+			}
+		},
+		components: {
+			upload,
+			edit
+		},
+	}
+</script>
+
+<style lang="scss">
+	.manager-index {
+		width: 70%;
+		max-width: 1200px;
+		margin: 0 auto;
+
+		.manager-update {
+			padding: 12px 0;
+			text-align: right;
+		}
+
+		.manager-content {
+			background: $--background-color-base;
+			border-radius: 2px;
+			height: auto;
+		}
+
+		.manager-title {
+			display: flex;
+			align-items: center;
+			padding: 32px;
+			position: relative;
+		}
+
+		.manager-title::before {
+			content: '';
+			position: absolute;
+			left: 32px;
+			right: 32px;
+			bottom: 0;
+			height: 1px;
+			background-color: $--border-color-base;
+		}
+
+		.manager-logo {
+			width: 48px;
+			height: 48px;
+			border-radius: 4px;
+			overflow: hidden;
+			margin-right: 18px;
+			border: 1px solid $--border-color-base;
+
+			.el-image {
+				width: 100%;
+				height: 100%;
+
+				.image-slot {
+					width: 100%;
+					height: 100%;
+					text-align: center;
+					line-height: 48px;
+				}
+			}
+		}
+
+		.manager-name {
+			font-size: 18px;
+		}
+
+		.manager-box {
+			padding: 32px 32px 16px 32px;
+			display: flex;
+			flex-wrap: wrap;
+
+			.manager-item {
+				width: 50%;
+				margin-bottom: 32px;
+
+				.manager-label {
+					opacity: 0.6;
+					line-height: 20px;
+					margin-bottom: 8px;
+				}
+
+				.manager-value {
+					line-height: 20px;
+				}
+
+				.el-icon-copy-document {
+					margin-left: 5px;
+					cursor: pointer;
+				}
+			}
+		}
+	}
+</style>

+ 123 - 0
virgo.wzfrontend/workark/src/views/work/organization/seal.vue

@@ -0,0 +1,123 @@
+<template>
+	<div class="hui-flex hui-content">
+		<div class="hui-content-title">
+			<div class="hui-title-item active">印章列表</div>
+		</div>
+		<div class="hui-flex-box hui-flex hui-table">
+			<div class="hui-content-insert">
+				<el-button v-permission="'/work/organization/seal/add'" type="primary" size="small"
+					@click="insertSeal">新增印章</el-button>
+			</div>
+			<div class="hui-flex-box">
+				<el-table :data="tableData" row-key="id" height="100%">
+					<el-table-column label="序号" width="50">
+						<template slot-scope="scope">
+							<div style="text-align: center;">{{scope.$index + 1}}</div>
+						</template>
+					</el-table-column>
+					<el-table-column label="印章名称" prop="name"></el-table-column>
+					<el-table-column label="印章类型" prop="name">
+						<template slot-scope="scope">
+							<span>{{$field.findTypeName('sealType',scope.row.type)}}</span>
+						</template>
+					</el-table-column>
+					<el-table-column label="印章描述" prop="comment"></el-table-column>
+					<el-table-column label="操作" width="150" align="center">
+						<template slot-scope="scope">
+							<div class="hui-table-operation">
+								<span class="table-operation" v-permission="'/work/organization/seal/detail'"
+									@click="lookSeal(scope.row)">
+									详情
+								</span>
+								<span class="table-operation" v-permission="'/work/organization/seal/update'"
+									@click="updateSeal(scope.row)">
+									编辑
+								</span>
+								<span class="table-operation" v-permission="'/work/organization/seal/delete'"
+									@click="deleteSeal(scope.row)">
+									删除
+								</span>
+							</div>
+						</template>
+					</el-table-column>
+					<template slot="empty">
+						<el-empty description="暂无数据"></el-empty>
+					</template>
+				</el-table>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" :title="isUpdate?'编辑':'新增'" :visible.sync="visible" width="900px"
+			:append-to-body="true">
+			<edit v-if="visible" @callback="callback" :isUpdate="isUpdate" :detailId="detailId"></edit>
+		</el-dialog>
+		<el-drawer title="项目详情" :visible.sync="drawer" :size="400" :append-to-body="true">
+			<detail v-if="drawer" :detailId="detailId"></detail>
+		</el-drawer>
+	</div>
+</template>
+
+<script>
+	import {
+		getSealList,
+		deleteSeal
+	} from '@/api/organization'
+	import edit from '@/components/work/organization/seal/edit'
+	import detail from '@/components/work/organization/seal/detail'
+	export default {
+		data() {
+			return {
+				tableData: [],
+				visible: false,
+				detailId: '',
+				isUpdate: false,
+				drawer: false
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				if (!this.auth('/work/organization/seal/list')) return;
+				getSealList(this.$store.getters.organization.id).then(res => {
+					if (res.state) {
+						this.tableData = res.data;
+					}
+				})
+			},
+			insertSeal() {
+				this.isUpdate = false;
+				this.visible = true;
+			},
+			updateSeal(item) {
+				this.detailId = item.id;
+				this.isUpdate = true;
+				this.visible = true;
+			},
+			lookSeal(item) {
+				this.detailId = item.id;
+				this.drawer = true;
+			},
+			deleteSeal(item) {
+				this.$confirm('确定要删除该印章?', () => {
+					deleteSeal(item.id).then(res => {
+						if (res.state) {
+							this.init();
+							this.$message.success('操作成功');
+						}
+					})
+				});
+			},
+			callback(type) {
+				if (type === 'init') this.init();
+				this.visible = false;
+			}
+		},
+		components: {
+			edit,
+			detail
+		},
+	}
+</script>
+
+<style lang="scss"></style>

+ 253 - 0
virgo.wzfrontend/workark/src/views/work/organization/user.vue

@@ -0,0 +1,253 @@
+<template>
+	<div class="hui-flex hui-content work-user">
+		<div class="hui-content-title">
+			<div class="hui-title-item active">成员管理</div>
+		</div>
+		<div class="hui-flex-box yui-tree-box">
+			<div class="hui-left-tree">
+				<div class="hui-left-tree-title">
+					<div class="tree-logo">
+						<el-image v-if="organization.logoUrl" :src="organization.logoUrl" fit="cover"></el-image>
+						<div class="el-image" v-else>
+							<div class="image-slot"><i class="el-icon-picture-outline"></i></div>
+						</div>
+					</div>
+					<span class="hui-left-tree-sub hui-ellipsis">{{organization.name}}</span>
+				</div>
+				<div class="hui-left-tree-content">
+					<el-collapse>
+						<el-collapse-item v-for="item in treeData" :key="item.id" :name="item.id">
+							<template slot="title">
+								<div class="collapse-title" @click="changeCollapse(item)">
+									<i class="iconfont huifont-bumen"></i>
+									<span class="el-collapse-name">{{item.name}}</span>
+								</div>
+							</template>
+							<div>
+								<el-tree :data="item.children" :props="defaultProps" :expand-on-click-node="false"
+									@node-click="selectPart">
+								</el-tree>
+							</div>
+						</el-collapse-item>
+					</el-collapse>
+				</div>
+			</div>
+			<div class="hui-tree-content">
+				<div class="hui-flex hui-table">
+					<div class="hui-content-insert">
+						<el-button v-permission="'/work/organization/user/add'" type="primary" size="small"
+							@click="insertUser" :disabled="!part.id">
+							新增成员
+						</el-button>
+					</div>
+					<div class="hui-flex-box">
+						<el-table :data="tableData" row-key="id" height="100%">
+							<el-table-column label="序号" width="50">
+								<template slot-scope="scope">
+									<div style="text-align: center;">{{scope.$index + 1}}</div>
+								</template>
+							</el-table-column>
+							<el-table-column label="姓名">
+								<template slot-scope="scope">
+									<div class="hui-table-user">
+										<div class="hui-table-avatar">
+											<avatar :user="scope.row"></avatar>
+										</div>
+										<div>{{scope.row.name}}</div>
+									</div>
+								</template>
+							</el-table-column>
+							<el-table-column label="电话" prop="phone"></el-table-column>
+							<el-table-column label="部门">
+								<template slot-scope="scope">
+									<span>{{part.name}}</span>
+								</template>
+							</el-table-column>
+							<el-table-column label="权限">
+								<template slot-scope="scope">
+									<el-button v-permission="'/work/organization/user/set'" size="mini" type="primary"
+										@click="updateUser(scope.row,2,'设置权限')">
+										设置
+									</el-button>
+								</template>
+							</el-table-column>
+							<el-table-column label="操作" width="150" align="center">
+								<template slot-scope="scope">
+									<div class="hui-table-operation">
+										<span v-permission="'/work/organization/user/detail'" class="table-operation"
+											@click="lookUser(scope.row)">
+											详情
+										</span>
+										<span v-permission="'/work/organization/user/update'" class="table-operation"
+											v-if="!scope.row.projectFlowId" @click="updateUser(scope.row,3,'修改用户')">
+											编辑
+										</span>
+										<span v-permission="'/work/organization/user/delete'" class="table-operation"
+											v-if="!scope.row.projectFlowId" @click="deleteUser(scope.row)">
+											删除
+										</span>
+									</div>
+								</template>
+							</el-table-column>
+							<template slot="empty">
+								<el-empty description="当前部门没有直属成员"></el-empty>
+							</template>
+						</el-table>
+					</div>
+				</div>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="visible" width="900px"
+			:append-to-body="true">
+			<edit v-if="visible && type === 1" :part="part" @callback="callback"></edit>
+			<role v-if="visible && type === 2" :user="user" @callback="callback"></role>
+		</el-dialog>
+		<el-drawer title="用户详情" :visible.sync="drawer" :size="400" :append-to-body="true">
+			<detail v-if="drawer" :user="user"></detail>
+		</el-drawer>
+	</div>
+</template>
+
+<script>
+	import {
+		getPartList,
+		getUserListByPart,
+		deleteUser
+	} from '@/api/organization'
+	import role from '@/components/work/organization/user/role'
+	import detail from '@/components/work/organization/user/detail'
+	import edit from '@/components/work/organization/user/edit'
+	export default {
+		data() {
+			return {
+				treeData: [],
+				organization: {},
+				project: {},
+				tableData: [],
+				defaultProps: {
+					children: 'children',
+					label: 'name'
+				},
+				part: {},
+				visible: false,
+				user: {},
+				drawer: false,
+				type: 1,
+				title: '',
+				oldCollapse: []
+			}
+		},
+		mounted() {
+			this.organization = this.$store.getters.organization;
+			this.project = this.$store.getters.project;
+			let logo = !this.organization.logo ? {} : JSON.parse(this.organization.logo);
+			this.organization['logoUrl'] = logo.url;
+			this.init()
+		},
+		methods: {
+			init() {
+				if (!this.auth('/work/organization/user/list')) return;
+				getPartList(this.organization.id, this.project.id).then(res => {
+					if (res.state) {
+						this.treeData = res.data;
+					}
+				})
+			},
+			changeCollapse(part) {
+				setTimeout(() => {
+					this.selectPart(part)
+				}, 300)
+			},
+			selectPart(part) {
+				this.part = part;
+				getUserListByPart({
+					projectId: this.project.id,
+					organizationId: this.organization.id,
+					partId: part.id
+				}).then(
+					res => {
+						if (res.state) {
+							this.tableData = res.data;
+						}
+					})
+			},
+			insertUser() {
+				this.type = 1;
+				this.title = '新增成员';
+				this.visible = true;
+			},
+			updateUser(user, type, title) {
+				this.user = user;
+				this.type = type;
+				this.title = title;
+				this.visible = true;
+			},
+			lookUser(user) {
+				this.user = user;
+				this.user['partName'] = this.part.name;
+				this.drawer = true;
+			},
+			deleteUser(user) {
+				this.$confirm('确定要删除该员工?', () => {
+					deleteUser(this.organization.id, user.id).then(res => {
+						if (res.state) {
+							this.selectPart(this.part);
+							this.$message.success('操作成功');
+						}
+					})
+				});
+			},
+			callback(type) {
+				this.visible = false;
+				if (type === 'init') this.selectPart(this.part);
+			}
+		},
+		components: {
+			edit,
+			role,
+			detail
+		}
+	}
+</script>
+
+<style lang="scss">
+	.work-user {
+		.yui-tree-box {
+			background: transparent;
+
+			.hui-left-tree {
+				border-right: 1px solid $--border-color-base;
+			}
+
+			.hui-tree-content {
+				padding: 0;
+			}
+		}
+
+		.hui-left-tree-title {
+			.tree-logo {
+				width: 24px;
+				height: 24px;
+				border-radius: 4px;
+				overflow: hidden;
+
+
+				.el-image {
+					width: 100%;
+					height: 100%;
+
+					.image-slot {
+						width: 100%;
+						height: 100%;
+						text-align: center;
+						line-height: 24px;
+					}
+				}
+			}
+
+			.hui-left-tree-sub {
+				font-weight: 400;
+			}
+		}
+	}
+</style>

+ 270 - 0
virgo.wzfrontend/workark/src/views/work/system/main.vue

@@ -0,0 +1,270 @@
+<template>
+	<div class="hui-flex hui-content">
+		<div class="hui-flex-box hui-flex hui-table">
+			<div class="hui-content-insert">
+				<el-button type="primary" size="small" @click="insert(-1)">新增主菜单</el-button>
+			</div>
+			<div class="hui-flex-box">
+				<el-table ref="elTable" :data="tableData" row-key="id" border height="100%">
+					<el-table-column prop="title" label="菜单名称" width="180">
+					</el-table-column>
+					<el-table-column prop="iconClass" label="菜单图标" width="180">
+					</el-table-column>
+					<el-table-column prop="index" label="菜单URL">
+					</el-table-column>
+					<el-table-column label="操作" width="180" align="center">
+						<template slot-scope="scope">
+							<div class="hui-table-operation">
+								<span class="table-operation" v-if="scope.row.isMenu === '1'"
+									@click="insert(scope.row.id)">新增</span>
+								<span class="table-operation" @click="update(scope.row)">编辑</span>
+								<span class="table-operation" @click="deleteItem(scope.row.id)">删除</span>
+							</div>
+						</template>
+					</el-table-column>
+					<el-table-column label="排序" align="center" width="50">
+						<template>
+							<i class="el-icon-rank handle"></i>
+						</template>
+					</el-table-column>
+					<template slot="empty">
+						<el-empty description="暂无数据"></el-empty>
+					</template>
+				</el-table>
+			</div>
+		</div>
+		<el-dialog :close-on-click-modal="false" title="菜单" :visible.sync="dialogVisible" width="900px"
+			:append-to-body="true">
+			<edit v-if="dialogVisible" :updateData="updateData" ref="mainForm" @callback="dialogVisible = false"
+				@sure="sure">
+			</edit>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+	import {
+		getMenuList,
+		insertMenu,
+		updateMenu
+	} from '@/api/system'
+	import edit from '@/components/work/system/main/edit'
+	import {
+		setComment
+	} from '@/uitls/auth';
+	import Sortable from 'sortablejs'
+	export default {
+		data() {
+			return {
+				tableData: [],
+				comment: [],
+				dialogVisible: false,
+				tableId: 1,
+				nowDataId: -1,
+				updateData: {},
+				menuData: {},
+				type: ''
+			}
+		},
+		mounted() {
+			this.init();
+			this.initRowDrop();
+		},
+		methods: {
+			init(type) {
+				getMenuList().then(res => {
+					if (res.state) {
+						if (!res.data || res.data.length === 0) return;
+						this.menuData = res.data[1];
+						this.tableData = JSON.parse(this.menuData.data);
+						this.comment = [];
+						this.testIndex(this.tableData);
+						this.testComment(this.tableData);
+						if (type === 'reload') {
+							this.$store.dispatch('app/changeMenuData', this.tableData);
+							setComment(JSON.stringify(this.comment));
+						}
+					}
+				})
+			},
+			initRowDrop() {
+				const elTable = this.$refs.elTable.$el.querySelector('.el-table__body-wrapper tbody');
+				Sortable.create(elTable, {
+					animation: 150,
+					handle: '.handle', // 拖拽手柄(整行可拖拽)
+					ghostClass: 'sortable-ghost', // 拖拽占位符样式
+					onEnd: (evt) => {
+						const {
+							oldIndex,
+							newIndex,
+							item
+						} = evt;
+						// 获取被拖拽行的原始数据
+						const draggedElement = item.__vue__?.row;
+						if (!draggedElement) return;
+						// 递归查找节点及其父级
+						const findNode = (data, id, parent = null) => {
+							for (let i = 0; i < data.length; i++) {
+								if (data[i].id === id) return {
+									node: data[i],
+									parent,
+									index: i
+								};
+								if (data[i].children) {
+									const result = findNode(data[i].children, id, data[i]);
+									if (result) return result;
+								}
+							}
+							return null;
+						};
+						// 获取被拖拽节点的信息
+						const draggedInfo = findNode(this.tableData, draggedElement.id);
+						if (!draggedInfo) return;
+
+						// 获取目标位置(通过 newIndex 直接定位)
+						const getTargetParentAndIndex = (data, targetIndex) => {
+							let flatData = [];
+							const flatten = (nodes, parent = null) => {
+								nodes.forEach(node => {
+									flatData.push({
+										node,
+										parent
+									});
+									if (node.children) flatten(node.children, node);
+								});
+							};
+							flatten(data);
+							return flatData[targetIndex];
+						};
+
+						const targetInfo = getTargetParentAndIndex(this.tableData, newIndex);
+						if (!targetInfo) return;
+
+						// 从原位置移除
+						if (!draggedInfo.parent) {
+							this.tableData.splice(draggedInfo.index, 1);
+						} else {
+							draggedInfo.parent.children.splice(draggedInfo.index, 1);
+						}
+
+
+						// 插入到新位置(作为同级或子级)
+						const targetParent = targetInfo.parent || {
+							children: this.tableData
+						}; // 根节点的父级为 null
+						const targetSiblingIndex = targetParent.children.indexOf(targetInfo.node);
+
+						if (targetSiblingIndex !== -1) {
+							targetParent.children.splice(targetSiblingIndex, 0, draggedInfo.node);
+						} else {
+							if (!targetInfo.node.children) {
+								this.$set(targetInfo.node, 'children', []);
+							}
+							targetInfo.node.children.push(draggedInfo.node);
+						}
+
+						// 强制更新视图
+						this.$set(this, 'tableData', [...this.tableData]);
+						this.dropSuccess();
+					}
+				});
+			},
+			testIndex(tableData) {
+				for (let i = 0; i < tableData.length; i++) {
+					if (tableData[i].id + 1 > this.tableId) this.tableId = tableData[i].id + 1;
+					if (tableData[i].children) this.testIndex(tableData[i].children);
+				}
+			},
+			testComment(item, id, form) {
+				if (id == -1) return item.push(form);
+				if (this.type == 'delete') {
+					let index = item.findIndex(node => node.id == id);
+					if (index > -1) item.splice(index, 1);
+				}
+				for (let i = 0; i < item.length; i++) {
+					if (item[i].id == id) {
+						if (this.type == 'insert') {
+							if (!item[i].children) item[i].children = [];
+							item[i].children.push(form);
+						} else if (this.type == 'update') {
+							item[i] = form;
+						}
+					}
+					if (item[i].index && this.comment.filter(node => node == item[i].index).length == 0) this.comment.push(
+						item[i].index);
+					if (item[i].children) this.testComment(item[i].children, id, form);
+				}
+			},
+			insert(id) {
+				this.type = 'insert';
+				this.nowDataId = id;
+				this.updateData = {};
+				this.dialogVisible = true;
+			},
+			update(item) {
+				this.type = 'update';
+				this.nowDataId = item.id;
+				this.updateData = JSON.parse(JSON.stringify(item));
+				this.dialogVisible = true;
+			},
+			deleteItem(id) {
+				this.type = 'delete';
+				this.nowDataId = id;
+				this.updateData = {};
+				this.sure();
+			},
+			sure() {
+				let obj = JSON.parse(JSON.stringify(this.tableData));
+				this.comment = [];
+				this.testComment(obj, this.nowDataId, this.returnForm());
+				this.menuData.id ? updateMenu({
+					id: this.menuData.id,
+					data: JSON.stringify(obj),
+					comment: JSON.stringify(this.comment)
+				}).then(this.successFunc) : insertMenu({
+					data: JSON.stringify(obj),
+					comment: JSON.stringify(this.comment)
+				}).then(this.successFunc);
+			},
+			dropSuccess() {
+				let obj = JSON.parse(JSON.stringify(this.tableData));
+				this.comment = [];
+				this.testComments(obj);
+				updateMenu({
+					id: this.menuData.id,
+					data: JSON.stringify(obj),
+					comment: JSON.stringify(this.comment)
+				}).then(this.successFunc)
+			},
+			testComments(item) {
+				for (let i = 0; i < item.length; i++) {
+					if (item[i].index && this.comment.filter(node => node == item[i].index).length == 0) this.comment.push(
+						item[i].index);
+					if (item[i].children) this.testComment(item[i].children);
+				}
+			},
+			returnForm() {
+				if (this.type == 'delete') return {};
+				if (!this.$refs.mainForm) return {};
+				let form = this.$refs.mainForm.sureIncrease();
+				if (this.type == 'insert') form['id'] = this.tableId;
+				return form;
+			},
+			successFunc(res) {
+				if (res.state) {
+					this.$message({
+						type: 'success',
+						message: '操作成功'
+					})
+					this.init('reload');
+					this.dialogVisible = false;
+				}
+			}
+		},
+		components: {
+			edit
+		},
+	}
+</script>
+
+<style lang="scss"></style>