whx пре 2 месеци
родитељ
комит
70af5b1d5e
28 измењених фајлова са 10410 додато и 0 уклоњено
  1. 23 0
      virgo.wzfrontend/workark/.gitignore
  2. 24 0
      virgo.wzfrontend/workark/README.md
  3. 5 0
      virgo.wzfrontend/workark/babel.config.js
  4. 19 0
      virgo.wzfrontend/workark/jsconfig.json
  5. 8406 0
      virgo.wzfrontend/workark/package-lock.json
  6. 51 0
      virgo.wzfrontend/workark/package.json
  7. BIN
      virgo.wzfrontend/workark/public/favicon.ico
  8. 22 0
      virgo.wzfrontend/workark/public/index.html
  9. 167 0
      virgo.wzfrontend/workark/public/reset.css
  10. 14 0
      virgo.wzfrontend/workark/src/App.vue
  11. 154 0
      virgo.wzfrontend/workark/src/api/loginRegister.js
  12. 0 0
      virgo.wzfrontend/workark/src/assets/css/common.scss
  13. 80 0
      virgo.wzfrontend/workark/src/assets/css/element-variables.scss
  14. 63 0
      virgo.wzfrontend/workark/src/axios/index.js
  15. 13 0
      virgo.wzfrontend/workark/src/config/index.js
  16. 162 0
      virgo.wzfrontend/workark/src/layout/loginLayout.vue
  17. 20 0
      virgo.wzfrontend/workark/src/main.js
  18. 16 0
      virgo.wzfrontend/workark/src/router/index.js
  19. 16 0
      virgo.wzfrontend/workark/src/router/login.js
  20. 70 0
      virgo.wzfrontend/workark/src/router/permission.js
  21. 8 0
      virgo.wzfrontend/workark/src/store/getters.js
  22. 21 0
      virgo.wzfrontend/workark/src/store/index.js
  23. 64 0
      virgo.wzfrontend/workark/src/store/modules/app.js
  24. 31 0
      virgo.wzfrontend/workark/src/uitls/auth.js
  25. 512 0
      virgo.wzfrontend/workark/src/uitls/index.js
  26. 16 0
      virgo.wzfrontend/workark/src/views/index.vue
  27. 351 0
      virgo.wzfrontend/workark/src/views/login/login.vue
  28. 82 0
      virgo.wzfrontend/workark/vue.config.js

+ 23 - 0
virgo.wzfrontend/workark/.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 24 - 0
virgo.wzfrontend/workark/README.md

@@ -0,0 +1,24 @@
+# workark
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
virgo.wzfrontend/workark/babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

+ 19 - 0
virgo.wzfrontend/workark/jsconfig.json

@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "esnext",
+    "baseUrl": "./",
+    "moduleResolution": "node",
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  }
+}

Разлика између датотеке није приказан због своје велике величине
+ 8406 - 0
virgo.wzfrontend/workark/package-lock.json


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

@@ -0,0 +1,51 @@
+{
+	"name": "workark",
+	"version": "0.1.0",
+	"private": true,
+	"scripts": {
+		"serve": "vue-cli-service serve",
+		"build": "vue-cli-service build",
+		"lint": "vue-cli-service lint"
+	},
+	"dependencies": {
+		"axios": "^0.19.0",
+		"core-js": "^3.8.3",
+		"element-ui": "^2.15.14",
+		"vue": "^2.6.14",
+		"vue-router": "^3.1.3",
+		"vuex": "^3.0.1"
+	},
+	"devDependencies": {
+		"@babel/core": "^7.12.16",
+		"@babel/eslint-parser": "^7.12.16",
+		"@vue/cli-plugin-babel": "~5.0.0",
+		"@vue/cli-plugin-eslint": "~5.0.0",
+		"@vue/cli-service": "~5.0.0",
+		"eslint": "^7.32.0",
+		"eslint-plugin-vue": "^8.0.3",
+		"happypack": "^5.0.1",
+		"nprogress": "^0.2.0",
+		"sass": "^1.32.6",
+		"sass-loader": "^8.0.0",
+		"vue-template-compiler": "^2.6.14"
+	},
+	"eslintConfig": {
+		"root": true,
+		"env": {
+			"node": true
+		},
+		"extends": [
+			"plugin:vue/essential",
+			"eslint:recommended"
+		],
+		"parserOptions": {
+			"parser": "@babel/eslint-parser"
+		},
+		"rules": {}
+	},
+	"browserslist": [
+		"> 1%",
+		"last 2 versions",
+		"not dead"
+	]
+}

BIN
virgo.wzfrontend/workark/public/favicon.ico


+ 22 - 0
virgo.wzfrontend/workark/public/index.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<meta name="viewport" content="width=device-width,initial-scale=1.0">
+		<link rel="icon" href="https://file-node.oss-cn-shanghai.aliyuncs.com/youji/2f7b4bfaf9e64817bc2cb46f3a632b01">
+		<title>WORKARK</title>
+		<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>reset.css" />
+		<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4358860_u9q17n0qvwc.css">
+	</head>
+	<body>
+		<noscript>
+			<strong>
+				We're sorry but WORKARK doesn't work properly without JavaScript
+				enabled. Please enable it to continue.
+			</strong>
+		</noscript>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+	</body>
+</html>

+ 167 - 0
virgo.wzfrontend/workark/public/reset.css

@@ -0,0 +1,167 @@
+/* 禁用iPhone中Safari的字号自动调整 */
+* {
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	outline: none;
+	font-family: LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, Helvetica Neue, Tahoma, PingFang SC, Microsoft Yahei, Arial, Hiragino Sans GB, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
+	font-weight: 400;
+	-webkit-font-smoothing: antialiased;
+	user-select: none;
+}
+
+html {
+	-webkit-text-size-adjust: 100%;
+	-ms-text-size-adjust: 100%;
+}
+
+/* 去除iPhone中默认的input样式 */
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input,
+button {
+	/* -webkit-appearance:none; */
+	resize: none;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+	-webkit-appearance: none;
+}
+
+button {
+	border: none;
+}
+
+/* 设置HTML5元素为块 */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+	display: block;
+}
+
+/* 图片自适应 */
+img {
+	/*max-width: 100%;*/
+	height: auto;
+	width: auto\9;
+	/* ie8 */
+	-ms-interpolation-mode: bicubic;
+	/*为了照顾ie图片缩放失真*/
+}
+
+/* 初始化 */
+body,
+div,
+ul,
+li,
+ol,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+input,
+textarea,
+select,
+p,
+dl,
+dt,
+dd,
+a,
+img,
+button,
+form,
+table,
+th,
+tr,
+td,
+tbody,
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+	margin: 0;
+	padding: 0;
+	border: none;
+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+	/*取消链接高亮*/
+	box-sizing: border-box;
+}
+
+
+em,
+i {
+	font-style: normal;
+}
+
+strong {
+	font-weight: normal;
+}
+
+.clearfix:after {
+	content: "";
+	display: block;
+	visibility: hidden;
+	height: 0;
+	clear: both;
+}
+
+.clearfix {
+	zoom: 1;
+}
+
+a {
+	text-decoration: none;
+}
+
+a:hover,
+a:active,
+a:visited {
+	text-decoration: none;
+}
+
+ul,
+ol {
+	list-style: none;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+	font-size: 100%;
+}
+
+img {
+	border: none;
+	vertical-align: middle;
+}
+
+body,
+html,
+#app {
+	width: 100%;
+	height: 100%;
+	margin: 0 auto;
+	position: relative;
+}

+ 14 - 0
virgo.wzfrontend/workark/src/App.vue

@@ -0,0 +1,14 @@
+<template>
+	<div id="app">
+		<router-view />
+	</div>
+</template>
+
+<script>
+	export default {
+		name: 'App'
+	}
+</script>
+
+<style>
+</style>

+ 154 - 0
virgo.wzfrontend/workark/src/api/loginRegister.js

@@ -0,0 +1,154 @@
+import request from '@/axios'
+
+/* 
+ * 获取图片验证码
+ * 
+ */
+export function getImgCode() {
+	return request({
+		url: `/manager/pCode`,
+		method: 'get'
+	})
+}
+/* 
+ * 发送手机验证码
+ * 
+ */
+export function sendPhoneCode(phone, vCode) {
+	return request({
+		url: `/manager/send/${phone}/${vCode}`,
+		method: 'get'
+	})
+}
+/* 
+ * 登录
+ * 
+ */
+export function login(data) {
+	return request({
+		url: `/manager/login`,
+		method: 'post',
+		data: data
+	})
+}
+
+/* 
+ * 获取当前登录者信息
+ * @param null
+ * 
+ */
+export function getUserInfo(data) {
+	return request({
+		url: `/manager/userContext`,
+		method: 'get'
+	})
+}
+/* 
+ * 获取用户信息
+ * 
+ * 
+ */
+export function getUserInfoById(userId) {
+	return request({
+		url: `/manager/userInfo/${userId}`,
+		method: 'get'
+	})
+}
+/* 
+ * 切换组织
+ * @param null 
+ */
+export function selectOrangaized(data) {
+	return request({
+		url: `/manager/userContext/organization`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 切换项目
+ * @param null 
+ */
+export function selectProject(projectId) {
+	return request({
+		url: `/manager/userContext/project/${projectId}`,
+		method: 'put',
+	})
+}
+/* 
+ * 切换身份
+ * @param null 
+ */
+export function selectIdentity(identityId) {
+	return request({
+		url: `/manager/userContext/identity/${identityId}`,
+		method: 'put'
+	})
+}
+/* 
+ * 根据组织获取项目列表
+ * @param null 
+ * 
+ */
+export function getOrganizedProjectList(organizationId) {
+	return request({
+		url: `/api/project/getOrganization/${organizationId}/`,
+		method: 'get',
+	})
+}
+/* 
+ * 修改个人信息
+ * @param {Object} data = {loginName用户名|pwd密码|name昵称|sex性别|phone手机号|email邮箱|portrait头像|organizationId组织类型}
+ * 
+ */
+export function updateUserDetails(data) {
+	return request({
+		url: `/manager/userInfo/update`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 刷新上下文
+ * @param null 
+ * 
+ */
+export function refresh() {
+	return request({
+		url: `/manager/userContext/refresh`,
+		method: 'get',
+	})
+}
+/* 
+ * 获取项目组织下个人信息
+ * 
+ */
+export function getOperationUserInfo(data) {
+	return request({
+		url: `/api/info/get`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 新增项目组织下个人信息
+ * 
+ */
+export function insertOperationUserInfo(data) {
+	return request({
+		url: `/api/info`,
+		method: 'post',
+		data: data
+	})
+}
+/* 
+ * 修改项目组织下个人信息
+ * 
+ */
+export function updateOperationUserInfo(data) {
+	return request({
+		url: `/api/info`,
+		method: 'put',
+		data: data
+	})
+}

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


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

@@ -0,0 +1,80 @@
+/* 改变主题色变量 */
+// 主色系
+$--color-primary: #409EFF !default;      // 主色调(蓝)
+$--color-success: #67C23A !default;      // 成功色(绿)
+$--color-warning: #E6A23C !default;      // 警告色(橙)
+$--color-danger: #F56C6C !default;       // 危险色(红)
+$--color-info: #909399 !default;         // 信息色(灰)
+$--color-white:#fff !default;
+
+// 文本颜色
+$--color-text-primary: #303133 !default; // 主要文字
+$--color-text-regular: #606266 !default; // 常规文字
+$--color-text-secondary: #909399 !default;// 次要文字
+$--color-text-placeholder: #C0C4CC !default; // 占位符
+
+// 边框颜色
+$--border-color-base: #DCDFE6 !default;  // 基础边框
+$--border-color-light: #E4E7ED !default; // 浅色边框
+$--border-color-lighter: #EBEEF5 !default;// 更浅边框
+$--border-color-extra-light: #F2F6FC !default;
+
+// 背景色
+$--background-color-base: #F5F7FA !default; // 基础背景
+$--background-color-light: #fafafa !default; // 浅色背景
+
+$--font-path: 'fonts' !default;          // 字体文件路径
+$--font-size-base: 14px !default;        // 基础字号
+$--font-size-small: 13px !default;       // 小号字
+$--font-size-large: 16px !default;       // 大号字
+
+$--font-weight-primary: 500 !default;    // 主要字重
+$--font-line-height-primary: 1.5 !default;// 行高
+
+// 通用尺寸
+$--size-base: 14px !default;             // 基础尺寸单位
+
+// 组件尺寸
+$--button-font-size: $--font-size-base !default;
+$--input-height: 40px !default;          // 输入框高度
+$--menu-item-height: 56px !default;      // 菜单项高度
+
+
+$--border-width-base: 1px !default;      // 边框宽度
+$--border-style-base: solid !default;    // 边框样式
+$--border-radius-base: 4px !default;     // 基础圆角
+$--border-radius-small: 2px !default;    // 小圆角
+$--border-radius-circle: 100% !default;  // 圆形
+
+$--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default;
+$--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default;
+$--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default;
+
+$--index-normal: 1 !default;             // 常规层级
+$--index-top: 1000 !default;             // 顶部层级(如header)
+$--index-popper: 2000 !default;          // 弹出层(下拉/弹窗)
+
+$--transition-all: all .3s !default;      // 通用过渡
+$--transition-fade: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
+$--transition-md-fade: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
+
+// Button 按钮
+$--button-font-weight: $--font-weight-primary !default;
+$--button-border-radius: $--border-radius-base !default;
+
+// Input 输入框
+$--input-border-color: $--border-color-base !default;
+$--input-hover-border-color: mix($--color-white, $--color-primary, 70%) !default;
+
+// Table 表格
+$--table-border-color: $--border-color-light !default;
+$--table-header-background: $--background-color-base !default;
+
+// Dialog 弹窗
+$--dialog-background-color: $--color-white !default;
+$--dialog-box-shadow: $--box-shadow-light !default;
+
+/* 改变 icon 字体路径变量,必需 */
+$--font-path: '~element-ui/lib/theme-chalk/fonts';
+
+@import "~element-ui/packages/theme-chalk/src/index";

+ 63 - 0
virgo.wzfrontend/workark/src/axios/index.js

@@ -0,0 +1,63 @@
+import axios from 'axios'
+import baseConfig from '@/config'
+import {
+	getToken
+} from '@/uitls/auth'
+import {
+	Message
+} from 'element-ui'
+const service = axios.create({
+	baseURL: baseConfig.baseURL,
+	timeout: 6000000, // 请求超时时间
+})
+/* 消息提示 */
+const tip = (msg, type) => {
+	let types = type || 'warning';
+	if (msg == 'RET_INVALID_PASSWORD') {
+		msg = '账号验证码有误'
+	}
+	Message({
+		message: msg,
+		type: types,
+		duration: 2000
+	});
+}
+// request 拦截器
+service.interceptors.request.use(
+	config => {
+		let token = getToken();
+		config.headers['token'] = token;
+		if ((config.url === '/im/message/save')) {
+			config.headers['token'] = localStorage.getItem('systemChatToken');
+		}
+		if (config.url === '/im/upload/image') {
+			config.headers['token'] = localStorage.getItem('chatToken');
+			config.headers['Content-Type'] = 'multipart/form-data';
+		}
+		return config
+	},
+	error => {
+		Promise.reject(error)
+	}
+)
+// response 拦截器
+service.interceptors.response.use(
+	response => {
+		const res = response.data;
+		let url = response.config.url;
+		if (!res.code) return res;
+		if (res.code != '200' && res.code != '20001') tip(res.message || res.msg);
+		return {
+			state: res.code == '200',
+			data: res.data,
+			msg: res.code == '200' ? 'success' : 'error'
+		}
+	},
+	error => { //请求返回错误
+		return {
+			state: false
+		}
+	}
+)
+
+export default service;

+ 13 - 0
virgo.wzfrontend/workark/src/config/index.js

@@ -0,0 +1,13 @@
+const modeUrlObj = {
+	'production': { // 生产环境
+		baseURL: ''
+	},
+	'development': { // 开发环境
+		baseURL: 'v1'
+	},
+	'test': { // 测试环境
+		baseURL: ''
+	}
+}
+
+export default modeUrlObj[process.env.NODE_ENV]

+ 162 - 0
virgo.wzfrontend/workark/src/layout/loginLayout.vue

@@ -0,0 +1,162 @@
+<template>
+	<div class="login-register">
+		<div class="title">
+			<div class="title-logo">
+				<img src="https://file-node.oss-cn-shanghai.aliyuncs.com/youji/2f7b4bfaf9e64817bc2cb46f3a632b01"
+					alt="logo.png">
+				<div class="title-name">WORKARK</div>
+			</div>
+		</div>
+		<div class="content">
+			<div class="content-left">
+				<div class="content-title">
+					<div class="sub-title">WORKARK</div>
+					<div class="small-title">
+						<span>项目申报</span>
+						<span class="line"></span>
+						<span>知识产权</span>
+						<span class="line"></span>
+						<span>网站建设</span>
+						<span class="line"></span>
+						<span>推广宣传</span>
+					</div>
+				</div>
+				<div class="content-image"></div>
+			</div>
+			<div class="content-right">
+				<transition name="slide-fade" mode="out-in">
+					<router-view :key="key" />
+				</transition>
+			</div>
+		</div>
+	</div>
+</template>
+
+
+<script>
+	export default {
+		name: 'loginLayout',
+		data() {
+			return {};
+		},
+		computed: {
+			key() {
+				return this.$route.path;
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.login-register {
+		width: 100%;
+		height: 100%;
+		background: url(https://file-node.oss-cn-shanghai.aliyuncs.com/youji/8ead21d2c7464ae99745e93f5a17e4d3) no-repeat;
+		background-size: 100% 100%;
+		overflow: auto;
+		display: flex;
+		flex-direction: column;
+		min-width: 1300px;
+		min-height: 800px;
+
+		.title {
+			height: 64px;
+			background: rgba(255, 255, 255, 0.36);
+			box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.25);
+			padding-left: 46px;
+			display: flex;
+			align-items: center;
+
+			.title-logo {
+				width: 245px;
+				height: 34px;
+				margin-right: 74px;
+				display: flex;
+				align-items: center;
+				color: #484B50;
+			}
+
+			.title-sub-name {
+				font-size: 12px;
+				transform: scale(0.68) translateX(-66px);
+				width: 282px;
+				opacity: 0.75;
+			}
+
+			.title-name {
+				font-family: hanyiyaku;
+				font-size: 16px;
+				line-height: 22px;
+			}
+
+			.title-logo img {
+				width: 34px;
+				height: 34px;
+				margin-right: 10px;
+				border-radius: 5px;
+			}
+		}
+
+		.content {
+			flex: 1;
+			height: 0;
+			width: 100%;
+			overflow: auto;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+
+			.content-image {
+				width: 640px;
+				height: 440px;
+				background: url(https://file-node.oss-cn-shanghai.aliyuncs.com/youji/2fdedc8660e146b7bb003ce85dedea5f) no-repeat;
+				background-size: 100% 100%;
+			}
+
+			.content-left {
+				width: 910px;
+				display: flex;
+				flex-direction: column;
+				padding-left: 90px;
+			}
+
+			.content-right {
+				width: 400px;
+				display: flex;
+				align-items: center;
+			}
+
+			.content-title {
+				margin-bottom: 64px;
+			}
+
+			.sub-title {
+				font-weight: 600;
+				font-size: 31px;
+				color: #484B50;
+				line-height: 44px;
+				text-align: left;
+				font-style: normal;
+				margin-bottom: 10px;
+			}
+
+			.small-title {
+				font-weight: 400;
+				font-size: 16px;
+				color: #333E4D;
+				line-height: 22px;
+				text-align: left;
+				font-style: normal;
+				display: flex;
+				align-items: center;
+
+				.line {
+					margin: 0 20px;
+					width: 1px;
+					height: 16px;
+					background: #CBE0FF;
+				}
+			}
+		}
+	}
+</style>

+ 20 - 0
virgo.wzfrontend/workark/src/main.js

@@ -0,0 +1,20 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+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 './router/permission.js'
+
+new Vue({
+	router,
+	store,
+	render: h => h(App),
+}).$mount('#app')

+ 16 - 0
virgo.wzfrontend/workark/src/router/index.js

@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+import loginRouter from './login'
+
+Vue.use(Router)
+const originalPush = Router.prototype.push;
+Router.prototype.push = function push(location) {
+	return originalPush.call(this, location).catch(err => err);
+};
+
+export default new Router({
+	routes: [loginRouter, { //404
+		path: '/',
+		component: () => import('@/views/index'),
+	}]
+})

+ 16 - 0
virgo.wzfrontend/workark/src/router/login.js

@@ -0,0 +1,16 @@
+import loginLayout from '@/layout/loginLayout'
+
+const loginRouter = {
+	path: '/loginregister',
+	component: loginLayout,
+	children: [{
+		path: 'login',
+		component: () => import('@/views/login/login'),
+		name: '登录',
+		meta: {
+			title: '登录'
+		}
+	}]
+}
+
+export default loginRouter;

+ 70 - 0
virgo.wzfrontend/workark/src/router/permission.js

@@ -0,0 +1,70 @@
+import router from '../router'
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+import {
+	getToken,
+	getComment,
+	removeToken
+} from '@/uitls/auth'
+import store from '../store';
+import {
+	Message
+} from 'element-ui'
+NProgress.configure({
+	showSpinner: false
+})
+const baseUrl = ['/', '/401', '/404', '/ui', '/loginRegister/login', '/work', '/message', '/work/space/project',
+	'/iot/device/detail', '/work/operation/week/detail', '/work/staging/user'
+];
+
+const testBaseUrl = (path) => { //判断公共路由
+	let nowBaseUrl = ['/loginRegister/login'].filter((item) => {
+		return (item == path && item != '/')
+	})
+	return nowBaseUrl.length == 0;
+}
+/* 消息提示 */
+const tip = (msg, type) => {
+	let types = type || 'warning';
+	Message({
+		message: msg,
+		type: types,
+		duration: 2000
+	});
+}
+const testComment = (path) => { //判断权限路由
+	let comment = getComment() ? JSON.parse(getComment()) : [];
+	let nowComment = comment.concat(baseUrl).filter((item) => {
+		return item == path
+	})
+	return nowComment.length == 0;
+}
+
+router.beforeEach((to, from, next) => {
+	if (getToken() && !(store.getters && store.getters.user && store.getters.user.userId) && !sessionStorage
+		.getItem('store')) removeToken();
+	if (to.path.indexOf('website') === -1) {
+		/* 判断是否登录 */
+		if (!getToken() && testBaseUrl(to.path)) return next('/loginRegister/login');
+		/* 判断路由是否存在 */
+		if (to.matched.length === 0) return next('/404');
+		/* 判断是否有权限 */
+		if (testComment(to.path)) return next('/401');
+		/* 无项目时跳转项目列表页面 */
+		if (getToken() && localStorage.getItem('projectId') == 0 && to.path !== '/work/space/project') {
+			tip('暂无项目,请先添加项目');
+			return next('/work/space/project');
+		}
+	}
+	NProgress.start();
+	next();
+});
+
+router.afterEach(() => {
+	NProgress.done();
+});
+
+router.onError((error) => {
+	NProgress.done();
+	location.reload();
+});

+ 8 - 0
virgo.wzfrontend/workark/src/store/getters.js

@@ -0,0 +1,8 @@
+const getters = {
+	codeNumber: state => state.app.codeNumber, //获取验证码倒计时-防止刷新页面重置倒计时
+	menuData: state => state.app.menuData, //用户菜单
+	user: state => state.app.user, //用户基本信息
+	organization: state => state.app.organization, //所属组织信息
+	project: state => state.app.project, //项目
+}
+export default getters;

+ 21 - 0
virgo.wzfrontend/workark/src/store/index.js

@@ -0,0 +1,21 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import getters from './getters'
+
+Vue.use(Vuex)
+
+const modulesFiles = require.context('./modules', true, /\.js$/)
+
+const modules = modulesFiles.keys().reduce((modules, modulePath) => {
+	const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
+	const value = modulesFiles(modulePath)
+	modules[moduleName] = value.default
+	return modules
+}, {})
+
+const store = new Vuex.Store({
+	modules,
+	getters
+})
+
+export default store

+ 64 - 0
virgo.wzfrontend/workark/src/store/modules/app.js

@@ -0,0 +1,64 @@
+/* 
+ *	app基本信息
+ * 	
+ */
+const state = {
+	codeNumber: 60,
+	menuData: [],
+	user: {},
+	organization: {},
+	project: {},
+}
+
+const mutations = {
+	CHANGE_CODENUMBER: (state, num) => {
+		state.codeNumber = num;
+	},
+	CHANGE_MENU_DATA: (state, menuData) => {
+		state.menuData = menuData;
+	},
+	CHANGE_USER: (state, user) => {
+		state.user = user;
+	},
+	CHANGE_ORGANIZATION: (state, organization) => {
+		state.organization = organization;
+	},
+	CHANGE_PROJECT: (state, project) => {
+		state.project = project;
+	}
+}
+
+const actions = {
+	changeCodeNumber({
+		commit,
+	}, num) {
+		commit('CHANGE_CODENUMBER', num < 0 ? 60 : num);
+	},
+	changeMenuData({
+		commit
+	}, menuData) {
+		commit('CHANGE_MENU_DATA', menuData);
+	},
+	changeUser({
+		commit
+	}, user) {
+		commit('CHANGE_USER', user);
+	},
+	changeOrganization({
+		commit
+	}, organization) {
+		commit('CHANGE_ORGANIZATION', organization);
+	},
+	changeProject({
+		commit,
+	}, project) {
+		commit('CHANGE_PROJECT', project);
+	}
+}
+
+export default {
+	namespaced: true,
+	state,
+	mutations,
+	actions
+}

+ 31 - 0
virgo.wzfrontend/workark/src/uitls/auth.js

@@ -0,0 +1,31 @@
+const TokenKey = 'adminToken'
+
+export function getToken() {
+	return sessionStorage.getItem(TokenKey);
+}
+
+export function setToken(token) {
+	return sessionStorage.setItem(TokenKey, token);
+}
+
+export function removeToken() {
+	return sessionStorage.removeItem(TokenKey);
+}
+
+export function getComment() {
+	return sessionStorage.getItem('comment');
+}
+
+export function setComment(comment) {
+	return sessionStorage.setItem('comment', comment);
+}
+
+export function removeComment() {
+	return sessionStorage.removeItem('comment');
+}
+export function auth(auth) {
+	let commont = sessionStorage.getItem('comment');
+	let arr = commont ? JSON.parse(commont) : [];
+	return arr.filter(node => node == auth).length > 0;
+
+}

+ 512 - 0
virgo.wzfrontend/workark/src/uitls/index.js

@@ -0,0 +1,512 @@
+/**
+ * 解析参数
+ * @param {string} url
+ * @returns {Object}
+ */
+export function getQueryObject(url) {
+	url = url == null ? window.location.href : url
+	const search = url.substring(url.lastIndexOf('?') + 1)
+	const obj = {}
+	const reg = /([^?&=]+)=([^?&=]*)/g
+	search.replace(reg, (rs, $1, $2) => {
+		const name = decodeURIComponent($1)
+		let val = decodeURIComponent($2)
+		val = String(val)
+		obj[name] = val
+		return rs
+	})
+	return obj
+}
+
+/**
+ * 返回字符串的大小byte
+ * @param {string} input value
+ * @returns {number} output value
+ */
+export function byteLength(str) {
+	// returns the byte length of an utf8 string
+	let s = str.length
+	for (var i = str.length - 1; i >= 0; i--) {
+		const code = str.charCodeAt(i)
+		if (code > 0x7f && code <= 0x7ff) s++
+		else if (code > 0x7ff && code <= 0xffff) s += 2
+		if (code >= 0xDC00 && code <= 0xDFFF) i--
+	}
+	return s
+}
+
+/**
+ * @param {Array} actual
+ * @returns {Array}
+ */
+export function cleanArray(actual) {
+	const newArray = []
+	for (let i = 0; i < actual.length; i++) {
+		if (actual[i]) {
+			newArray.push(actual[i])
+		}
+	}
+	return newArray
+}
+
+/**
+ * 将json换成get传参
+ * @param {Object} json
+ * @returns {Array}
+ */
+export function param(json) {
+	if (!json) return ''
+	return cleanArray(
+		Object.keys(json).map(key => {
+			if (json[key] === undefined) return ''
+			return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
+		})
+	).join('&')
+}
+
+/**
+ * 将url参数提取出来
+ * @param {string} url
+ * @returns {Object}
+ */
+export function param2Obj(url) {
+	const search = url.split('?')[1]
+	if (!search) {
+		return {}
+	}
+	return JSON.parse(
+		'{"' +
+		decodeURIComponent(search)
+		.replace(/"/g, '\\"')
+		.replace(/&/g, '","')
+		.replace(/=/g, '":"')
+		.replace(/\+/g, ' ') +
+		'"}'
+	)
+}
+
+/**
+ * @param {string} val
+ * @returns {string}
+ */
+export function html2Text(val) {
+	const div = document.createElement('div')
+	div.innerHTML = val
+	return div.textContent || div.innerText
+}
+
+/**
+ * 对象去重合并
+ * @param {Object} target
+ * @param {(Object|Array)} source
+ * @returns {Object}
+ */
+export function objectMerge(target, source) {
+	if (typeof target !== 'object') {
+		target = {}
+	}
+	if (Array.isArray(source)) {
+		return source.slice()
+	}
+	Object.keys(source).forEach(property => {
+		const sourceProperty = source[property]
+		if (typeof sourceProperty === 'object') {
+			target[property] = objectMerge(target[property], sourceProperty)
+		} else {
+			target[property] = sourceProperty
+		}
+	})
+	return target
+}
+
+/**
+ * 移除和添加className
+ * @param {HTMLElement} element
+ * @param {string} className
+ */
+export function toggleClass(element, className) {
+	if (!element || !className) {
+		return
+	}
+	let classString = element.className
+	const nameIndex = classString.indexOf(className)
+	if (nameIndex === -1) {
+		classString += ' ' + className
+	} else {
+		classString =
+			classString.substr(0, nameIndex) +
+			classString.substr(nameIndex + className.length)
+	}
+	element.className = classString
+}
+
+/**
+ * @param {string} type
+ * @returns {Date}
+ */
+export function getTime(type) {
+	if (type === 'start') {
+		return new Date().getTime() - 3600 * 1000 * 24 * 90
+	} else {
+		return new Date(new Date().toDateString())
+	}
+}
+
+/**
+ * 防抖
+ * @param {Function} func
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {*}
+ */
+export function debounce(func, wait, immediate) {
+	let timeout, args, context, timestamp, result
+	const later = function() {
+		// 据上一次触发时间间隔
+		const last = +new Date() - timestamp
+		// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
+		if (last < wait && last > 0) {
+			timeout = setTimeout(later, wait - last)
+		} else {
+			timeout = null
+			// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+			if (!immediate) {
+				result = func.apply(context, args)
+				if (!timeout) context = args = null
+			}
+		}
+	}
+
+	return function(...args) {
+		context = this
+		timestamp = +new Date()
+		const callNow = immediate && !timeout
+		// 如果延时不存在,重新设定延时
+		if (!timeout) timeout = setTimeout(later, wait)
+		if (callNow) {
+			result = func.apply(context, args)
+			context = args = null
+		}
+		return result
+	}
+}
+
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ * 深拷贝
+ * @param {Object} source
+ * @returns {Object}
+ */
+export function deepClone(source) {
+	if (!source && typeof source !== 'object') {
+		throw new Error('error arguments', 'deepClone')
+	}
+	const targetObj = source.constructor === Array ? [] : {}
+	Object.keys(source).forEach(keys => {
+		if (source[keys] && typeof source[keys] === 'object') {
+			targetObj[keys] = deepClone(source[keys])
+		} else {
+			targetObj[keys] = source[keys]
+		}
+	})
+	return targetObj
+}
+
+/**
+ * 去重
+ * @param {Array} arr
+ * @returns {Array}
+ */
+export function uniqueArr(arr) {
+	return Array.from(new Set(arr))
+}
+
+/**
+ * 创建唯一随机数
+ * @returns {string}
+ */
+export function createUniqueString() {
+	const timestamp = +new Date() + ''
+	const randomNum = parseInt((1 + Math.random()) * 65536) + ''
+	return (+(randomNum + timestamp)).toString(32)
+}
+
+/**
+ * Check if an element has a class
+ * 判断是否含有该className
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ * @returns {boolean}
+ */
+export function hasClass(ele, cls) {
+	return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
+}
+
+/**
+ * Add class to element
+ * 添加className
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ */
+export function addClass(ele, cls) {
+	if (!hasClass(ele, cls)) ele.className += ' ' + cls
+}
+
+/**
+ * Remove class from element
+ * 移除classNmae
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ */
+export function removeClass(ele, cls) {
+	if (hasClass(ele, cls)) {
+		const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
+		ele.className = ele.className.replace(reg, ' ')
+	}
+}
+
+/**
+ * 根据树子节点ID查找所有父节点ID
+ * @param {array} dataSource 树形结构数据源
+ * @param {number} nodeId 子节点ID
+ * @returns {array} 包含所有父节点ID的数组,按照从根节点到直接父节点的顺序排序
+ */
+export function findParentIds(dataSource, nodeId) {
+	const parentIds = []; // 用于存储所有父节点ID的数组
+
+	// 定义一个递归函数,用于遍历整棵树并查找子节点的所有父节点
+	function traverse(node, nodeId) {
+		if (node.id === nodeId) { // 如果当前节点的ID等于子节点的ID,则表示已经找到了子节点,可以开始向上查找父节点
+			return true; // 返回true表示已经找到了子节点
+		}
+		if (node.children) { // 如果当前节点有子节点,则继续遍历子节点
+			for (const childNode of node.children) {
+				if (traverse(childNode, nodeId)) { // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点
+					parentIds.unshift(node.id);
+					return true;
+				}
+			}
+		}
+		return false; // 如果当前节点不是子节点的父节点,则返回false
+	}
+	// 从根节点开始遍历整棵树,并调用递归函数查找子节点的所有父节点
+	for (const node of dataSource) {
+		if (traverse(node, nodeId)) { // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环
+			break;
+		}
+	}
+	parentIds.push(nodeId);
+	return parentIds; // 返回所有父节点ID的数组
+}
+/**
+ * 根据树子节点ID查找所有父节点ID
+ * @param {array} dataSource 树形结构数据源
+ * @param {number} nodeId 子节点ID
+ * @returns {array} 包含所有父节点ID的数组,按照从根节点到直接父节点的顺序排序
+ */
+export function findParentLabel(dataSource, nodeId) {
+	const parentIds = []; // 用于存储所有父节点ID的数组
+	// 定义一个递归函数,用于遍历整棵树并查找子节点的所有父节点
+	function traverse(node, nodeId) {
+		if (node.label === nodeId) { // 如果当前节点的ID等于子节点的ID,则表示已经找到了子节点,可以开始向上查找父节点
+			return true; // 返回true表示已经找到了子节点
+		}
+		if (node.children) { // 如果当前节点有子节点,则继续遍历子节点
+			for (const childNode of node.children) {
+				if (traverse(childNode, nodeId)) { // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点
+					parentIds.unshift(node.label);
+					return true;
+				}
+			}
+		}
+		return false; // 如果当前节点不是子节点的父节点,则返回false
+	}
+	// 从根节点开始遍历整棵树,并调用递归函数查找子节点的所有父节点
+	for (const node of dataSource) {
+		if (traverse(node, nodeId)) { // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环
+			break;
+		}
+	}
+	parentIds.push(nodeId);
+	return parentIds; // 返回所有父节点ID的数组
+}
+
+/**
+ * 获取所有子集
+ * @param {array} dataSource 树形结构数据源
+ * @returns {array} 包含所有父节点ID的数组,按照从根节点到直接父节点的顺序排序
+ */
+export function steamroller(dataSource) {
+	let newArr = []
+	let flat = function(item) {
+		for (var i = 0; i < item.length; i++) {
+			if (Array.isArray(item[i].children)) {
+				flat(item[i].children)
+			} else {
+				newArr.push(item[i])
+			}
+		}
+	}
+	flat(dataSource);
+	return newArr
+}
+/**
+ * 根据树子节点查找所有父节点
+ * @param {array} dataSource 树形结构数据源
+ * @param {number} nodeId 子节点ID
+ * @returns {array} 包含所有父节点ID的数组,按照从根节点到直接父节点的顺序排序
+ */
+export function findParent(dataSource, node) {
+	const parent = []; // 用于存储所有父节点ID的数组
+	// 定义一个递归函数,用于遍历整棵树并查找子节点的所有父节点
+	function traverse(child, node) {
+		if (child.id === node.id) { // 如果当前节点的ID等于子节点的ID,则表示已经找到了子节点,可以开始向上查找父节点
+			return true; // 返回true表示已经找到了子节点
+		}
+		if (child.children) { // 如果当前节点有子节点,则继续遍历子节点
+			for (const childNode of child.children) {
+				if (traverse(childNode, node)) { // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点
+					parent.unshift(child);
+					return true;
+				}
+			}
+		}
+		return false; // 如果当前节点不是子节点的父节点,则返回false
+	}
+	// 从根节点开始遍历整棵树,并调用递归函数查找子节点的所有父节点
+	for (const child of dataSource) {
+		if (traverse(child, node)) { // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环
+			break;
+		}
+	}
+	parent.push(node);
+	return parent; // 返回所有父节点ID的数组
+}
+/**
+ * 返回项目单位建筑房间的层级关系
+ * @param {array} data 树形结构数据源
+ * @returns {array} 
+ */
+export function roomList(data) {
+	let newArr = JSON.parse(JSON.stringify(data));
+	let index = 1;
+	newArr = newArr.map(item => {
+		item['projectItemId'] = item['id'];
+		item['id'] = index;
+		item['projectItem'] = item;
+		item['optionName'] = item['name'];
+		index++;
+		item['children'] = item.projectItemTargetList.map(target => {
+			target['projectItemTargetId'] = target['id'];
+			target['id'] = index;
+			target['projectItem'] = item;
+			target['optionName'] = target['name'];
+			index++;
+			if (target.projectItemTargetRoomList) target['children'] = target.projectItemTargetRoomList
+				.map(room => {
+					room['roomId'] = room['id'];
+					room['id'] = index;
+					room['projectItem'] = item;
+					room['projectItemTarget'] = target;
+					room['optionName'] = room['roomNumber'];
+					index++;
+					return room;
+				})
+			return target
+		})
+		return item;
+	})
+	return newArr;
+}
+/**
+ * 返回项目单位建筑房间选择的层级关系
+ * @param {array} data 树形结构数据源
+ * @returns {array} 
+ */
+export function roomCheckedList(data, roomIds) {
+	let newArr = JSON.parse(JSON.stringify(data));
+	let index = 1;
+	newArr = newArr.map(item => {
+		item['projectItemId'] = item['id'];
+		item['id'] = index;
+		item['projectItem'] = item;
+		item['optionName'] = item['name'];
+		index++;
+		item['children'] = item.projectItemTargetList.map(target => {
+			target['projectItemTargetId'] = target['id'];
+			target['id'] = index;
+			target['projectItem'] = item;
+			target['optionName'] = target['name'];
+			index++;
+			target['children'] = target.projectItemTargetRoomList.map(room => {
+				room['roomId'] = room['id'];
+				room['id'] = index;
+				room['projectItem'] = item;
+				room['projectItemTarget'] = target;
+				room['optionName'] = room['roomNumber'];
+				index++;
+				return room;
+			}).filter(room => roomIds.filter(node => node == room.roomId).length > 0);
+			return target
+		}).filter(target => target.children.length > 0);
+		return item;
+	}).filter(target => target.children.length > 0);
+	return newArr;
+}
+/**
+ * 返回sheet去除data里null数据
+ * @param {array} data 结构数据源
+ * @returns {array} 
+ */
+export function sheetDataRemoveNull(data) {
+	let newArr = JSON.parse(JSON.stringify(data));
+	for (let i = 0; i < newArr.length; i++) {
+		for (let j = 0; j < newArr[i].data.length; j++) {
+			newArr[i].data[j] = newArr[i].data[j].filter(node => node);
+		}
+	}
+	return newArr;
+}
+/**
+ * 返回sheet添加data里null数据
+ * @param {array} data 结构数据源
+ * @returns {array} 
+ */
+export function sheetDataAddNull(data) {
+	let newArr = JSON.parse(JSON.stringify(data));
+	for (let i = 0; i < newArr.length; i++) {
+		for (let j = 0; j < newArr[i].data.length; j++) {
+			for (let k = 0; k < 64; k++) {
+				if (!newArr[i].data[j][k]) newArr[i].data[j].push(null)
+			}
+		}
+	}
+	return newArr;
+}
+/* 
+ * 下载文件
+ * 
+ */
+export function downloadFileDom(data, fileName) {
+	let blob = new Blob([data], {
+		type: 'application/octet-stream'
+	}); // 转化为blob对象
+	if (typeof window.navigator.msSaveBlob !== 'undefined') {
+		window.navigator.msSaveBlob(blob, fileName);
+	} else {
+		var blobURL = window.URL.createObjectURL(blob); // 将blob对象转为一个URL
+		var tempLink = document.createElement('a'); // 创建一个a标签
+		tempLink.style.display = 'none';
+		tempLink.href = blobURL;
+		tempLink.setAttribute('download', fileName); // 给a标签添加下载属性
+		if (typeof tempLink.download === 'undefined') {
+			tempLink.setAttribute('target', '_blank');
+		}
+		document.body.appendChild(tempLink); // 将a标签添加到body当中
+		tempLink.click(); // 启动下载
+		document.body.removeChild(tempLink); // 下载完毕删除a标签
+		window.URL.revokeObjectURL(blobURL);
+	}
+}

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

@@ -0,0 +1,16 @@
+<template>
+	<div>
+		 <el-button type="primary">主要按钮</el-button>
+	</div>
+</template>
+
+<script>
+	export default {
+		mounted() {
+			console.log(this.$store.getters);
+		}
+	}
+</script>
+
+<style>
+</style>

+ 351 - 0
virgo.wzfrontend/workark/src/views/login/login.vue

@@ -0,0 +1,351 @@
+<template>
+	<div class="login-box">
+		<div class="login-title">
+			<div class="login-title-value">登录</div>
+			<div class="login-title-line"></div>
+		</div>
+		<div class="login-form">
+			<el-form :model="loginForm" :rules="loginRules" status-icon ref="loginForm" label-position="left">
+				<el-form-item prop="phone">
+					<el-input type="text" prefix-icon="iconfont huifont-shoujihao" v-model="loginForm.phone"
+						placeholder="手机号" maxlength="11">
+					</el-input>
+				</el-form-item>
+				<el-form-item prop="code" class="image-item">
+					<el-input type="text" prefix-icon="iconfont huifont-tuxingyanzhengma" v-model="loginForm.code"
+						placeholder="图片验证码" maxlength="4"></el-input>
+					<img v-if="codeImg" :src="codeImg" alt="图片验证码" class="code-image" @click="imgCodeFunc">
+				</el-form-item>
+				<el-form-item prop="phoneCode" class="phone-code">
+					<el-input type="number" prefix-icon="iconfont huifont-duanxinyanzhengma"
+						v-model="loginForm.phoneCode" placeholder="短信验证码"
+						oninput="if(value.length > 6) value=value.slice(0, 6)">
+					</el-input>
+					<div class="get-code-btn">
+						<el-button type="primary" size="samll" @click="getPhoneCode" :disabled="codeName !== '获取验证码'">
+							{{codeName}}
+						</el-button>
+					</div>
+				</el-form-item>
+			</el-form>
+			<el-button class="submit" type="primary" @click="loginSubmit" :loading="loginLoading">登录</el-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import {
+		login,
+		getImgCode,
+		sendPhoneCode,
+		getUserInfo
+	} from '@/api/loginRegister';
+	import {
+		setToken,
+		setComment
+	} from '@/uitls/auth';
+	export default {
+		data() {
+			return {
+				loginForm: {
+					phone: '',
+					code: '',
+					phoneCode: ''
+				},
+				loginLoading: false,
+				codeImg: '',
+				codeName: '获取验证码', //获取验证码text
+				isDisabled: false, //获取验证码按钮状态
+				loginRules: {
+					code: [{
+						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 {
+								callback();
+							}
+						},
+						trigger: 'blur'
+					}],
+					phoneCode: [{
+						required: true,
+						message: '请输入短信验证码',
+						trigger: 'blur'
+					}]
+				},
+			};
+		},
+		mounted() {
+			localStorage.removeItem('chatToken')
+			this.imgCodeFunc();
+			if (this.$store.getters.codeNumber != 60) this.codeReset();
+		},
+		methods: {
+			imgCodeFunc() {
+				getImgCode().then(res => {
+					if (res.state) {
+						this.codeImg = res.data.pngBase64;
+					}
+				})
+			},
+			getPhoneCode() {
+				if (this.isDisabled) return;
+				this.$refs.loginForm.validateField('phone', valid => {
+					if (!valid) {
+						this.$refs.loginForm.validateField('code', valid => {
+							if (!valid) {
+								sendPhoneCode(this.loginForm.phone, this.loginForm.code).then(res => {
+									if (res.state) {
+										this.codeReset();
+										this.$message.success('发送成功');
+									}
+								})
+							}
+						})
+					}
+				})
+			},
+			codeReset() {
+				//重置获取验证码倒计时
+				let codeNumber = this.$store.getters.codeNumber;
+				codeNumber--;
+				this.handleCode(codeNumber);
+				let codeRestFn = setInterval(() => {
+					codeNumber--;
+					this.handleCode(codeNumber);
+					if (codeNumber == 0) clearInterval(codeRestFn); //停止
+				}, 1000);
+			},
+			handleCode(codeNumber) {
+				//code操作
+				this.codeName = codeNumber == 0 ? '获取验证码' : '重新获取' + codeNumber;
+				this.isDisabled = codeNumber == 0 ? false : true;
+				this.$store.dispatch('loginRegister/changeCodeNumber', codeNumber == 0 ? 60 : codeNumber);
+			},
+			loginSubmit() {
+				if (this.loginLoading) return;
+				this.loginLoading = true;
+				this.$refs['loginForm'].validate(valid => {
+					if (!valid) return this.loginLoading = false;
+					this.loginFunc();
+				})
+			},
+			loginFunc() {
+				let postData = {
+					phone: this.loginForm.phone,
+					phoneCode: this.loginForm.phoneCode
+				}
+				login(postData).then(res => {
+					if (res.state) {
+						setToken(res.data.token);
+						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);
+								}
+							} else {
+								this.loginLoading = false;
+							}
+						})
+					} else {
+						this.loginLoading = false;
+					}
+				})
+			},
+			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;
+						}
+					})
+				}
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.login-box {
+		width: 440px;
+		background: $--color-white;
+		box-shadow: 0px 4px 16px 0px rgba(164, 178, 203, 0.15);
+		border-radius: 8px;
+		border: 0px solid $--color-white;
+	}
+
+	.login-title {
+		padding: 62px 52px 48px 52px;
+	}
+
+	.login-title-value {
+		font-weight: 600;
+		font-size: 28px;
+		color: #333E4D;
+		line-height: 40px;
+		text-align: left;
+		font-style: normal;
+		margin-bottom: 16px;
+	}
+
+	.login-title-line {
+		width: 80px;
+		height: 5px;
+		background: #E3E7EE;
+	}
+
+	.login-form {
+		padding: 0 52px;
+	}
+
+	::v-deep.el-form {
+		display: block;
+
+		.el-form-item {
+			width: 100%;
+			padding: 0;
+			margin-bottom: 28px;
+			position: relative;
+		}
+
+		.el-input__inner {
+			padding: 15px 15px 15px 63px;
+			height: 52px;
+			line-height: 52px;
+			font-size: 16px;
+		}
+
+		.el-input__icon,
+		.el-range-separator {
+			line-height: 52px;
+			font-size: 16px;
+		}
+
+		.el-input__prefix {
+			left: 16px;
+		}
+
+		.el-input__prefix::before {
+			content: '';
+			top: 16px;
+			right: -16px;
+			height: 21px;
+			width: 1px;
+			background-color: $--input-border-color;
+			position: absolute;
+		}
+
+		.el-input__icon.iconfont {
+			font-size: 20px;
+			color: #596d8e;
+		}
+
+		.el-input__icon.huifont-mima {
+			opacity: 0.95;
+			margin-left: 2px;
+		}
+
+		.image-item {
+			.el-form-item__content {
+				display: flex;
+			}
+
+			.el-input {
+				width: 190px;
+				margin-right: 12px;
+			}
+
+			.code-image {
+				width: 131px;
+				height: 50px;
+				cursor: pointer;
+			}
+		}
+
+		.get-code-btn {
+			position: absolute;
+			top: 6px;
+			right: 30px;
+		}
+	}
+
+	.submit {
+		width: 100%;
+		margin-bottom: 60px;
+		font-weight: 600;
+		font-size: 20px;
+		line-height: 28px;
+	}
+</style>

+ 82 - 0
virgo.wzfrontend/workark/vue.config.js

@@ -0,0 +1,82 @@
+const {
+	defineConfig
+} = require('@vue/cli-service');
+const dev_baseURL = 'https://www.waywish.com';
+const isProduction = process.env.NODE_ENV === 'production'; //是否为生产环境
+const path = require("path");
+const Happypack = require('happypack');
+
+const resolve = (dir) => {
+	return path.join(__dirname, dir);
+}
+
+module.exports = defineConfig({
+	publicPath: './',
+	outputDir: '../src/main/resources/static/workark',
+	assetsDir: 'static',
+	lintOnSave: false,
+	productionSourceMap: false,
+	// webpack配置
+	// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
+	chainWebpack: config => {
+		// 移除 prefetch 插件
+		config.plugins.delete('prefetch')
+		// 移除 preload 插件
+		config.plugins.delete('preload');
+		// 生产环境配置
+		if (isProduction) {
+			// 压缩代码
+			config.optimization.minimize(true);
+			// 分割代码
+			config.optimization.splitChunks({
+				chunks: 'all'
+			})
+		}
+	},
+	configureWebpack: {
+		devtool: process.env.NODE_ENV === 'development' ? 'eval' : false,
+		plugins: [
+			new Happypack({
+				loaders: ['babel-loader', 'vue-loader'],
+				cache: true,
+				threads: 10 // 线程数取决于你电脑性能的好坏,好的电脑建议开更多线程
+			})
+		]
+	},
+	devServer: {
+		port: 8080, // 端口
+		open: false, // 自动开启浏览器
+		compress: false, // 开启压缩
+		hot: true, //  模块热替换
+		client: {
+			overlay: { // 当出现编译错误或警告时,在浏览器中显示全屏覆盖。
+				warnings: true,
+				errors: true
+			},
+			progress: true, // 在浏览器中以百分比形式打印编译进度。
+		},
+		proxy: {
+			'/v1': {
+				target: dev_baseURL,
+				changeOrigin: true,
+				ws: true,
+				pathRewrite: {
+					'^/v1': ''
+				}
+			}
+		}
+	},
+	// css相关配置
+	css: {
+		// css预设器配置项
+		loaderOptions: {
+			// pass options to sass-loader
+			sass: {
+				// 引入全局变量样式
+				prependData: `
+	              @import "@/assets/css/element-variables.scss";
+	          `
+			}
+		},
+	}
+})