From a48cc614d400c3e4a07d8cdc3f8bb3c719c26bb4 Mon Sep 17 00:00:00 2001 From: pnoker Date: Wed, 29 Jan 2020 11:23:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 +- .../images/common}/avatar.png | Bin public/{img => images}/error/403.svg | 0 public/{img => images}/error/404.svg | 0 public/{img => images}/error/500.svg | 0 public/images/logo/logo-white.png | Bin 0 -> 5372 bytes {src/assets => public/images/logo}/logo.png | Bin public/index.html | 2 +- src/api/user.js | 12 + src/assets/styles/login.scss | 185 +++++++ src/components/error/403.vue | 2 +- src/components/error/404.vue | 2 +- src/components/error/500.vue | 2 +- src/components/error/style.scss | 1 - src/components/particles/particles.vue | 465 ++++++++++++++++++ src/config/axios.js | 54 ++ src/config/permission.js | 36 ++ src/config/website.js | 3 +- src/main.js | 9 +- src/plugins/element/element.js | 5 +- src/router/page/index.js | 42 ++ src/router/router.js | 73 +-- src/router/views/index.js | 121 +++-- src/store/getters.js | 2 +- src/store/modules/common.js | 14 +- src/store/modules/user.js | 51 ++ src/store/store.js | 3 +- src/util/auth.js | 16 + src/util/date.js | 59 +++ src/util/store.js | 2 +- src/util/util.js | 345 +++++++++++++ src/util/validate.js | 8 +- src/views/index/index.vue | 164 ++++++ src/views/login/index.vue | 105 ++++ vue.config.js | 16 +- 35 files changed, 1660 insertions(+), 144 deletions(-) rename {src/assets => public/images/common}/avatar.png (100%) rename public/{img => images}/error/403.svg (100%) rename public/{img => images}/error/404.svg (100%) rename public/{img => images}/error/500.svg (100%) create mode 100644 public/images/logo/logo-white.png rename {src/assets => public/images/logo}/logo.png (100%) create mode 100644 src/api/user.js create mode 100644 src/assets/styles/login.scss create mode 100644 src/components/particles/particles.vue create mode 100644 src/config/axios.js create mode 100644 src/config/permission.js create mode 100644 src/router/page/index.js create mode 100644 src/store/modules/user.js create mode 100644 src/util/auth.js create mode 100644 src/util/date.js create mode 100644 src/util/util.js create mode 100644 src/views/index/index.vue create mode 100644 src/views/login/index.vue diff --git a/package.json b/package.json index 50abf17..1d65f3d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,10 @@ "vue": "^2.6.11", "vue-axios": "^2.1.5", "vue-router": "^3.1.3", - "vuex": "^3.1.2" + "vuex": "^3.1.2", + "nprogress": "latest", + "js-base64": "latest", + "js-cookie": "latest" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.1.2", diff --git a/src/assets/avatar.png b/public/images/common/avatar.png similarity index 100% rename from src/assets/avatar.png rename to public/images/common/avatar.png diff --git a/public/img/error/403.svg b/public/images/error/403.svg similarity index 100% rename from public/img/error/403.svg rename to public/images/error/403.svg diff --git a/public/img/error/404.svg b/public/images/error/404.svg similarity index 100% rename from public/img/error/404.svg rename to public/images/error/404.svg diff --git a/public/img/error/500.svg b/public/images/error/500.svg similarity index 100% rename from public/img/error/500.svg rename to public/images/error/500.svg diff --git a/public/images/logo/logo-white.png b/public/images/logo/logo-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3b20cea1f7bceb3673c1aad1300b773c6a52cb09 GIT binary patch literal 5372 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 literal 0 HcmV?d00001 diff --git a/src/assets/logo.png b/public/images/logo/logo.png similarity index 100% rename from src/assets/logo.png rename to public/images/logo/logo.png diff --git a/public/index.html b/public/index.html index 7ec8c82..2e14ebe 100644 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,7 @@ - IOT | DC3 + DC3 Web UI diff --git a/src/config/axios.js b/src/config/axios.js new file mode 100644 index 0000000..77c48ae --- /dev/null +++ b/src/config/axios.js @@ -0,0 +1,54 @@ +import axios from 'axios' +import router from '@/router/router' +import store from '@/store/store'; +import {Message} from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import {getToken} from '@/util/auth' + +//返回其他状态码 +axios.defaults.validateStatus = function (status) { + return status >= 200 && status <= 500; // 默认的 +}; +//跨域请求,允许保存cookie +axios.defaults.withCredentials = true; + +//NProgress Configuration +NProgress.configure({ + showSpinner: false +}); + +//HTTP Request拦截 +axios.interceptors.request.use(config => { + NProgress.start(); + config.headers['Content-Type'] = 'application/json'; + config.headers['Auth-Token'] = getToken(); + return config; +}, error => { + return Promise.reject(error) +}); + +//HTTP Response拦截 +axios.interceptors.response.use(res => { + NProgress.done(); + const ok = res.data.ok || false; + const status = res.data.code || 200; + const message = res.data.message || '未知错误'; + //如果是401则跳转到登录页面 + if (status === 401) store.dispatch('FedLogOut').then(() => router.push({path: '/login'})); + // 如果请求为 !ok 默认统一处理 + if (!ok) { + Message({ + type: 'error', + showClose: true, + message: message + }); + return Promise.reject(new Error(message)); + } + return res; +}, error => { + NProgress.done(); + return Promise.reject(new Error(error)); +}); + +export default axios diff --git a/src/config/permission.js b/src/config/permission.js new file mode 100644 index 0000000..b3a6c2f --- /dev/null +++ b/src/config/permission.js @@ -0,0 +1,36 @@ +import router from '@/router/router' +import store from '@/store/store' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import {getToken} from '@/util/auth' + +router.beforeEach((to, from, next) => { + NProgress.start(); + const meta = to.meta || {}; + if (getToken()) { + if (to.path === '/login') { //如果登录成功访问登录页跳转到主页 + next({path: '/'}) + } else { + //如果用户信息为空则获取用户信息,获取用户信息失败,跳转到登录页 + console.log(store.getters.token); + if (store.getters.token.length === 0) { + store.dispatch('FedLogOut').then(() => { + next({path: '/login'}) + }) + } else { + next() + } + } + } else { + //判断是否需要认证,没有登录访问去登录页 + if (meta.isAuth === false) { + next() + } else { + next('/login') + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/src/config/website.js b/src/config/website.js index 50c9609..188b0f5 100644 --- a/src/config/website.js +++ b/src/config/website.js @@ -2,6 +2,5 @@ * 全局配置文件 */ export default { - key: 'dc3', - nav: '/' + key: 'dc3' } diff --git a/src/main.js b/src/main.js index ba35388..0c74a25 100644 --- a/src/main.js +++ b/src/main.js @@ -1,16 +1,13 @@ import Vue from 'vue' +import axios from './config/axios' +import VueAxios from 'vue-axios' import App from './App' import router from './router/router' import store from './store/store' -import baseCard from './components/card/base-card' -//Element UI import './plugins/element/element.js' -//Axios -import axios from 'axios' -import VueAxios from 'vue-axios' +import './config/permission'; Vue.use(VueAxios, axios); -Vue.component('base-card', baseCard); Vue.config.productionTip = false; new Vue({ diff --git a/src/plugins/element/element.js b/src/plugins/element/element.js index 7de55b1..b09723a 100644 --- a/src/plugins/element/element.js +++ b/src/plugins/element/element.js @@ -5,4 +5,7 @@ import locale from 'element-ui/lib/locale/lang/zh-CN' import 'element-ui/lib/theme-chalk/index.css'; import './element-variables.scss' -Vue.use(Element, {locale}) \ No newline at end of file +import baseCard from '@/components/card/base-card' + +Vue.use(Element, {locale}); +Vue.component('base-card', baseCard); \ No newline at end of file diff --git a/src/router/page/index.js b/src/router/page/index.js new file mode 100644 index 0000000..c47a410 --- /dev/null +++ b/src/router/page/index.js @@ -0,0 +1,42 @@ +export default [ + { + name: 'login', + path: '/login', + meta: { + isAuth: false + }, + component: () => import('@/views/login/index') + }, { + name: 'index', + path: '/', + redirect: '/home', + meta: { + isAuth: true + } + }, { + name: '404', + path: '/404', + meta: { + isAuth: false + }, + component: () => import('@/components/error/404') + + }, { + name: '403', + path: '/403', + meta: { + isAuth: false + }, + component: () => import('@/components/error/403') + }, { + name: '500', + path: '/500', + meta: { + isAuth: false + }, + component: () => import('@/components/error/500') + }, { + path: '*', + redirect: '/404' + } +] \ No newline at end of file diff --git a/src/router/router.js b/src/router/router.js index 9da1318..f8e7b7d 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -1,72 +1,9 @@ import Vue from 'vue' import VueRouter from 'vue-router' -import ViewsRouter from './views/' +import PageRouter from './page/index' +import ViewsRouter from './views/index' -import Home from '../views/Home' -import Things from '../views/Things' -import Template from '../views/Template' -import Now from '../views/Now' -import History from '../views/History' -import Alarm from '../views/Alarm' -import Label from '../views/Label' -import Picture from '../views/Picture' -import Video from '../views/Video' -import About from '../views/About' - -Vue.use(VueRouter) - -const routes = [ - { - path: '/', - name: 'home', - component: Home - }, - { - path: '/things', - name: 'things', - component: Things - }, - { - path: '/template', - name: 'template', - component: Template - }, - { - path: '/now', - name: 'now', - component: Now - }, - { - path: '/history', - name: 'history', - component: History - }, - { - path: '/alarm', - name: 'alarm', - component: Alarm - }, - { - path: '/label', - name: 'label', - component: Label - }, - { - path: '/picture', - name: 'picture', - component: Picture - }, - { - path: '/video', - name: 'video', - component: Video - }, - { - path: '/about', - name: 'about', - component: About - } -]; +Vue.use(VueRouter); const router = new VueRouter({ scrollBehavior(to, from, savedPosition) { @@ -82,9 +19,9 @@ const router = new VueRouter({ } } }, - routes: routes + routes: [] }); -router.addRoutes([...ViewsRouter]); +router.addRoutes([...PageRouter, ...ViewsRouter]); export default router diff --git a/src/router/views/index.js b/src/router/views/index.js index d780b87..4e6033d 100644 --- a/src/router/views/index.js +++ b/src/router/views/index.js @@ -1,43 +1,86 @@ -export default [ - /*{ - path: '/login', - name: '登录页', - component: () => import('@/page/login/index'), - meta: { - keepAlive: true, - isTab: false, - isAuth: false - } - },*/ { - path: '/404', - component: () => import('@/components/error/404'), - name: '404', - meta: { - keepAlive: true, - isTab: false, - isAuth: false - } +import Index from '@/views/index/index' - }, { - path: '/403', - component: () => import('@/components/error/403'), - name: '403', - meta: { - keepAlive: true, - isTab: false, - isAuth: false - } - }, { - path: '/500', - component: () => import('@/components/error/500'), - name: '500', +export default [ + { + path: '/', + redirect: '/home', meta: { - keepAlive: true, - isTab: false, - isAuth: false - } - }, { - path: '*', - redirect: '/404' + isAuth: true + }, + component: Index, + children: [ + { + name: 'home', + path: '/home', + meta: { + isAuth: true + }, + component: () => import('@/views/Home') + }, + { + name: 'things', + path: '/things', + meta: { + isAuth: true + }, + component: () => import('@/views/Things') + }, { + name: 'template', + path: '/template', + meta: { + isAuth: true + }, + component: () => import('@/views/Template') + }, { + name: 'now', + path: '/now', + meta: { + isAuth: true + }, + component: () => import('@/views/Now') + }, { + name: 'history', + path: '/history', + meta: { + isAuth: true + }, + component: () => import('@/views/History') + }, { + name: 'alarm', + path: '/alarm', + meta: { + isAuth: true + }, + component: () => import('@/views/Alarm') + }, { + name: 'label', + path: '/label', + meta: { + isAuth: true + }, + component: () => import('@/views/Label') + }, { + name: 'picture', + path: '/picture', + meta: { + isAuth: true + }, + component: () => import('@/views/Picture') + }, { + name: 'video', + path: '/video', + meta: { + isAuth: true + }, + component: () => import('@/views/Video') + }, { + name: 'about', + path: '/about', + meta: { + isAuth: true + }, + component: () => import('@/views/About') + } + ] } ] \ No newline at end of file diff --git a/src/store/getters.js b/src/store/getters.js index e0e0d59..4440aa9 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -1,5 +1,5 @@ const getters = { - currentNav: state => state.common.currentNav + token: state => state.user.token, }; export default getters \ No newline at end of file diff --git a/src/store/modules/common.js b/src/store/modules/common.js index d739efe..b3c4190 100644 --- a/src/store/modules/common.js +++ b/src/store/modules/common.js @@ -1,20 +1,10 @@ -import website from '../../config/website' -import {getStore, setStore} from '../../util/store' +import website from '@/config/website' const common = { state: { website: website, - currentNav: getStore({name: 'currentNav'}) || '/', }, - mutations: { - SET_CURRENT_PATH: (state, currentNav) => { - state.currentNav = currentNav; - setStore({ - name: 'currentNav', - content: state.currentNav, - }) - } - } + mutations: {} }; export default common \ No newline at end of file diff --git a/src/store/modules/user.js b/src/store/modules/user.js new file mode 100644 index 0000000..0461c2e --- /dev/null +++ b/src/store/modules/user.js @@ -0,0 +1,51 @@ +import {getStore, setStore} from '@/util/store' +import {setToken} from '@/util/auth' +import {checkTokenValid, generateToken} from '@/api/user' + +const user = { + state: { + userInfo: getStore({name: 'userInfo'}) || [], + token: getStore({name: 'token'}) || '', + }, + actions: { + GenerateToken({commit}, user) { + return new Promise((resolve, reject) => { + generateToken(user).then(res => { + const data = res.data; + if (data.ok) { + commit('SET_USER', user); + commit('SET_TOKEN', data.message); + resolve(); + } + }).catch(error => { + reject(error); + }) + }) + }, + CheckTokenValid(body) { + return new Promise((resolve, reject) => { + checkTokenValid(body).then(res => { + const data = res.data; + if (data.ok) { + resolve(); + } + }).catch(error => { + reject(error) + }) + }) + } + }, + mutations: { + SET_TOKEN: (state, token) => { + setToken(token); + state.token = token; + setStore({name: 'token', content: state.token, type: 'session'}) + }, + SET_USER: (state, userInfo) => { + state.userInfo = userInfo; + setStore({name: 'userInfo', content: state.userInfo}) + } + } +}; + +export default user diff --git a/src/store/store.js b/src/store/store.js index 023018c..4119dcb 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -1,5 +1,6 @@ import Vue from 'vue' import Vuex from 'vuex' +import user from './modules/user' import common from './modules/common' import getters from './getters' @@ -7,7 +8,7 @@ Vue.use(Vuex); const store = new Vuex.Store({ modules: { - common + common, user }, getters }); diff --git a/src/util/auth.js b/src/util/auth.js new file mode 100644 index 0000000..c39993f --- /dev/null +++ b/src/util/auth.js @@ -0,0 +1,16 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'x-access-token'; +const inFifteenMinutes = new Date(new Date().getTime() + 120 * 60 * 1000); + +export function getToken() { + return Cookies.get(TokenKey); +} + +export function setToken(token) { + return Cookies.set(TokenKey, token, {expires: inFifteenMinutes}); +} + +export function removeToken() { + return Cookies.remove(TokenKey); +} \ No newline at end of file diff --git a/src/util/date.js b/src/util/date.js new file mode 100644 index 0000000..b3f6de6 --- /dev/null +++ b/src/util/date.js @@ -0,0 +1,59 @@ +/** + * + * @param date1 + * @param date2 + * @returns {{leave1: number, hours: number, seconds: number, leave2: number, leave3: number, minutes: number, days: number}} + */ +export const calcDate = (date1, date2) => { + let date3 = date2 - date1; + + let days = Math.floor(date3 / (24 * 3600 * 1000)); + + let leave1 = date3 % (24 * 3600 * 1000);//计算天数后剩余的毫秒数 + let hours = Math.floor(leave1 / (3600 * 1000)); + + let leave2 = leave1 % (3600 * 1000);//计算小时数后剩余的毫秒数 + let minutes = Math.floor(leave2 / (60 * 1000)); + + let leave3 = leave2 % (60 * 1000);//计算分钟数后剩余的毫秒数 + let seconds = Math.round(date3 / 1000); + return { + leave1, + leave2, + leave3, + days: days, + hours: hours, + minutes: minutes, + seconds: seconds, + } +}; + +/** + * 日期格式化 + * + * @param date + * @returns {string} + */ +export function dateFormat(date) { + let format = 'yyyy-MM-dd hh:mm:ss'; + if (date !== 'Invalid Date') { + let o = { + "M+": date.getMonth() + 1, //month + "d+": date.getDate(), //day + "h+": date.getHours(), //hour + "m+": date.getMinutes(), //minute + "s+": date.getSeconds(), //second + "q+": Math.floor((date.getMonth() + 3) / 3), //quarter + "S": date.getMilliseconds() //millisecond + }; + if (/(y+)/.test(format)) format = format.replace(RegExp.$1, + (date.getFullYear() + "").substr(4 - RegExp.$1.length)); + for (let k in o) + if (new RegExp("(" + k + ")").test(format)) + format = format.replace(RegExp.$1, + RegExp.$1.length === 1 ? o[k] : + ("00" + o[k]).substr(("" + o[k]).length)); + return format; + } + return ''; +} \ No newline at end of file diff --git a/src/util/store.js b/src/util/store.js index 8d3d070..544eeb2 100644 --- a/src/util/store.js +++ b/src/util/store.js @@ -1,5 +1,5 @@ import {isNull} from './validate'; -import website from '../config/website' +import website from '@/config/website' const keyName = website.key + '-'; diff --git a/src/util/util.js b/src/util/util.js new file mode 100644 index 0000000..df3be0b --- /dev/null +++ b/src/util/util.js @@ -0,0 +1,345 @@ +import {isNull} from './validate' + +/** + * 表单序列化 + * + * @param data + * @returns {string} + */ +export const serialize = data => { + let list = []; + Object.keys(data).forEach(ele => { + list.push(`${ele}=${data[ele]}`) + }); + return list.join('&'); +}; + +/** + * 获取对象类型 + * + * @param obj + * @returns {string|*} + */ +export const getObjType = obj => { + let toString = Object.prototype.toString; + let map = { + '[object Boolean]': 'boolean', + '[object Number]': 'number', + '[object String]': 'string', + '[object Function]': 'function', + '[object Array]': 'array', + '[object Date]': 'date', + '[object RegExp]': 'regExp', + '[object Undefined]': 'undefined', + '[object Null]': 'null', + '[object Object]': 'object' + }; + if (obj instanceof Element) { + return 'element'; + } + return map[toString.call(obj)]; +}; + +/** + * 对象深拷贝 + * + * @param data + * @returns {{}|*} + */ +export const deepClone = data => { + let type = getObjType(data); + let obj; + if (type === 'array') { + obj = []; + } else if (type === 'object') { + obj = {}; + } else { + //不再具有下一层次 + return data; + } + if (type === 'array') { + for (let i = 0, len = data.length; i < len; i++) { + obj.push(deepClone(data[i])); + } + } else if (type === 'object') { + for (let key in data) { + obj[key] = deepClone(data[key]); + } + } + return obj; +}; + +/** + * 设置灰度模式 + * + * @param status + */ +export const toggleGrayMode = (status) => { + if (status) { + document.body.className = document.body.className + ' grayMode'; + } else { + document.body.className = document.body.className.replace(' grayMode', ''); + } +}; + +/** + * 设置主题 + * + * @param name + */ +export const setTheme = (name) => { + document.body.className = name; +}; + +/** + * 加密处理 + * + * @param params + * @returns {any} + */ +export const encryption = (params) => { + let { + data, + type, + param, + key + } = params; + let result = JSON.parse(JSON.stringify(data)); + if (type === 'Base64') { + param.forEach(ele => { + result[ele] = btoa(result[ele]); + }) + } else if (type === 'Aes') { + param.forEach(ele => { + result[ele] = window.CryptoJS.AES.encrypt(result[ele], key).toString(); + }) + } + return result; +}; + + +/** + * 递归寻找子类的父类 + * + * @param menu + * @param id + * @returns {undefined|*} + */ +export const findParent = (menu, id) => { + for (let i = 0; i < menu.length; i++) { + if (menu[i].children.length !== 0) { + for (let j = 0; j < menu[i].children.length; j++) { + if (menu[i].children[j].id === id) { + return menu[i]; + } else { + if (menu[i].children[j].children.length !== 0) { + return findParent(menu[i].children[j].children, id); + } + } + } + } + } +}; + +/** + * 动态插入css + * + * @param url + */ +export const loadStyle = url => { + const link = document.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = url; + const head = document.getElementsByTagName('head')[0]; + head.appendChild(link); +}; + +/** + * 判断2个对象属性和值是否相等 + * + * @param obj1 + * @param obj2 + * @returns {boolean} + */ +export const diff = (obj1, obj2) => { + delete obj1.close; + let o1 = obj1 instanceof Object; + let o2 = obj2 instanceof Object; + if (!o1 || !o2) { + return obj1 === obj2; + } + + if (Object.keys(obj1).length !== Object.keys(obj2).length) { + return false; + } + + for (let attr in obj1) { + let t1 = obj1[attr] instanceof Object; + let t2 = obj2[attr] instanceof Object; + if (t1 && t2) { + return diff(obj1[attr], obj2[attr]); + } else if (obj1[attr] !== obj2[attr]) { + return false; + } + } + return true; +}; + +/** + * 根据字典的value显示label + * + * @param dic + * @param value + * @returns {string|*} + */ +export const findDicLabel = (dic, value) => { + let result = ''; + if (isNull(dic)) return value; + if (typeof (value) == 'string' || typeof (value) == 'number' || typeof (value) == 'boolean') { + let index = 0; + index = findDicIndex(dic, value); + if (index !== -1) { + result = dic[index].label; + } else { + result = value; + } + } else if (value instanceof Array) { + result = []; + let index = 0; + value.forEach(ele => { + index = findDicIndex(dic, ele); + if (index !== -1) { + result.push(dic[index].label); + } else { + result.push(value); + } + }); + result = result.toString(); + } + return result; +}; + +/** + * 根据字典的value查找对应的index + * + * @param dic + * @param value + * @returns {number} + */ +export const findDicIndex = (dic, value) => { + for (let i = 0; i < dic.length; i++) { + if (dic[i].value === value) { + return i; + } + } + return -1; +}; + +/** + * 生成随机len位数字 + * + * @param len + * @param date + * @returns {string} + */ +export const randomLenNum = (len, date) => { + let random = ''; + random = Math.ceil(Math.random() * 100000000000000).toString().substr(0, len ? len : 4); + if (date) random = random + Date.now(); + return random; +}; + +/** + * 打开全屏/关闭全屏 + */ +export const triggerFullscreen = () => { + if (fullscreenEnable()) { + exitFullScreen(); + } else { + reqFullScreen(); + } +}; + +/** + * esc监听全屏 + * + * @param callback + */ +export const listenFullscreen = (callback) => { + function listen() { + callback() + } + + document.addEventListener("fullscreenchange", function () { + listen(); + }); + document.addEventListener("mozfullscreenchange", function () { + listen(); + }); + document.addEventListener("webkitfullscreenchange", function () { + listen(); + }); + document.addEventListener("msfullscreenchange", function () { + listen(); + }); +}; + +/** + * 浏览器判断是否全屏 + */ +export const fullscreenEnable = () => { + return document.isFullScreen || document.mozIsFullScreen || document.webkitIsFullScreen; +}; + +/** + * 浏览器全屏 + */ +export const reqFullScreen = () => { + if (document.documentElement.requestFullScreen) { + document.documentElement.requestFullScreen(); + } else if (document.documentElement.webkitRequestFullScreen) { + document.documentElement.webkitRequestFullScreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } +}; + +/** + * 浏览器退出全屏 + */ +export const exitFullScreen = () => { + if (document.documentElement.requestFullScreen) { + document.exitFullScreen(); + } else if (document.documentElement.webkitRequestFullScreen) { + document.webkitCancelFullScreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.mozCancelFullScreen(); + } +}; + +/** + * 打开小窗口 + * + * @param url + * @param title + * @param w + * @param h + */ +export const openWindow = (url, title, w, h) => { + // Fixes dual-screen position Most browsers Firefox + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; + + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; + + const left = ((width / 2) - (w / 2)) + dualScreenLeft; + const top = ((height / 2) - (h / 2)) + dualScreenTop; + const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left); + + // Puts focus on the newWindow + if (window.focus) { + newWindow.focus() + } +}; \ No newline at end of file diff --git a/src/util/validate.js b/src/util/validate.js index e39fbdb..ee85a5f 100644 --- a/src/util/validate.js +++ b/src/util/validate.js @@ -4,8 +4,7 @@ * @returns {boolean} */ export function isUrl(url) { - const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ - return urlregex.test(url) + return /^http[s]?:\/\/.*/.test(url); } /** @@ -14,8 +13,7 @@ export function isUrl(url) { * @returns {boolean} */ export function isEmail(email) { - const re = /^(([^<>()\\[\]\\.,;:\s@"]+(\.[^<>()\\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - return re.test(email) + return /^([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$/.test(email); } /** @@ -24,7 +22,7 @@ export function isEmail(email) { * @returns {boolean} */ export function isPhone(phone) { - return /^1[0-9]{10}$/.test(phone) + return /^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$/.test(phone); } /** diff --git a/src/views/index/index.vue b/src/views/index/index.vue new file mode 100644 index 0000000..0365b54 --- /dev/null +++ b/src/views/index/index.vue @@ -0,0 +1,164 @@ + + + + + + + diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..85cabae --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,105 @@ + + + + \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 46e9ba7..f2ade37 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,5 +1,17 @@ module.exports = { publicPath: './', lintOnSave: true, - productionSourceMap: false -} \ No newline at end of file + productionSourceMap: false, + devServer: { + proxy: { + '/api': { + target: 'http://localhost:8400', + changeOrigin: true, + ws: true, + pathRewrite: { + '^/api': '' + } + } + } + } +}; \ No newline at end of file