-
-
-
-
-
-
+
+
-
-
+
diff --git a/src/api/index.js b/src/api/index.js
new file mode 100644
index 0000000..e07ceeb
--- /dev/null
+++ b/src/api/index.js
@@ -0,0 +1,12 @@
+import request from '../router/axios'
+
+export const getUsualList = (current, size) => {
+ return request({
+ url: '/api/blade-log/usual/list',
+ method: 'get',
+ params: {
+ current,
+ size
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/api/user.js b/src/api/user.js
new file mode 100644
index 0000000..e4d3993
--- /dev/null
+++ b/src/api/user.js
@@ -0,0 +1,49 @@
+import request from '../router/axios';
+import {baseUrl} from '../config/env';
+
+export const loginByUsername = (tenantId, account, password, type) => request({
+ url: '/api/blade-auth/token',
+ method: 'post',
+ params: {
+ tenantId,
+ account,
+ password,
+ type
+ }
+});
+
+export const getButtons = () => request({
+ url: '/api/blade-system/menu/buttons',
+ method: 'get'
+});
+
+export const getUserInfo = () => request({
+ url: baseUrl + '/user/getUserInfo',
+ method: 'get'
+});
+
+export const refeshToken = () => request({
+ url: baseUrl + '/user/refesh',
+ method: 'post'
+});
+
+export const getMenu = () => request({
+ url: '/api/blade-system/menu/routes',
+ method: 'get'
+});
+
+export const getTopMenu = () => request({
+ url: baseUrl + '/user/getTopMenu',
+ method: 'get'
+});
+
+export const sendLogs = (list) => request({
+ url: baseUrl + '/user/logout',
+ method: 'post',
+ data: list
+});
+
+export const logout = () => request({
+ url: baseUrl + '/user/logout',
+ method: 'get'
+});
diff --git a/src/components/basic-container/main.vue b/src/components/basic-container/main.vue
new file mode 100644
index 0000000..d695d9f
--- /dev/null
+++ b/src/components/basic-container/main.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/error-page/403.vue b/src/components/error-page/403.vue
new file mode 100644
index 0000000..6b4bf9c
--- /dev/null
+++ b/src/components/error-page/403.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
403
+
抱歉,你无权访问该页面
+
+
+ 返回首页
+
+
+
+
+
+
+
+
diff --git a/src/components/error-page/404.vue b/src/components/error-page/404.vue
new file mode 100644
index 0000000..11a2be8
--- /dev/null
+++ b/src/components/error-page/404.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
404
+
抱歉,你访问的页面不存在
+
+
+ 返回首页
+
+
+
+
+
+
+
+
diff --git a/src/components/error-page/500.vue b/src/components/error-page/500.vue
new file mode 100644
index 0000000..9ccfda5
--- /dev/null
+++ b/src/components/error-page/500.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
500
+
抱歉,服务器出错了
+
+
+ 返回首页
+
+
+
+
+
+
+
+
diff --git a/src/components/error-page/style.scss b/src/components/error-page/style.scss
new file mode 100644
index 0000000..c41c163
--- /dev/null
+++ b/src/components/error-page/style.scss
@@ -0,0 +1,35 @@
+.error-page {
+ background: #f0f2f5;
+ margin-top: -30px;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .img {
+ margin-right: 80px;
+ height: 360px;
+ width: 100%;
+ max-width: 430px;
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ background-size: contain;
+ }
+
+ .content {
+ h1 {
+ color: #434e59;
+ font-size: 72px;
+ font-weight: 600;
+ line-height: 72px;
+ margin-bottom: 24px;
+ }
+
+ .desc {
+ color: rgba(0, 0, 0, 0.45);
+ font-size: 20px;
+ line-height: 28px;
+ margin-bottom: 16px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/iframe/main.vue b/src/components/iframe/main.vue
new file mode 100644
index 0000000..e675457
--- /dev/null
+++ b/src/components/iframe/main.vue
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/config/env.js b/src/config/env.js
new file mode 100644
index 0000000..e51c2ae
--- /dev/null
+++ b/src/config/env.js
@@ -0,0 +1,21 @@
+// 配置编译环境和线上环境之间的切换
+
+let baseUrl = '';
+let iconfontVersion = ['567566_pwc3oottzol', '1066523_v8rsbcusj5q'];
+let iconfontUrl = `//at.alicdn.com/t/font_$key.css`;
+let codeUrl = `${baseUrl}/code`
+const env = process.env
+if (env.NODE_ENV == 'development') {
+ baseUrl = ``; // 开发环境地址
+} else if (env.NODE_ENV == 'production') {
+ baseUrl = ``; //生产环境地址
+} else if (env.NODE_ENV == 'test') {
+ baseUrl = ``; //测试环境地址
+}
+export {
+ baseUrl,
+ iconfontUrl,
+ iconfontVersion,
+ codeUrl,
+ env
+}
diff --git a/src/config/error.js b/src/config/error.js
new file mode 100644
index 0000000..8c0c75a
--- /dev/null
+++ b/src/config/error.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import store from '../store/store'
+
+Vue.config.errorHandler = function (err, vm, info) {
+
+ Vue.nextTick(() => {
+ store.commit('ADD_LOGS', {
+ type: 'error',
+ message: err.message,
+ stack: err.stack,
+ info
+ })
+ if (process.env.NODE_ENV === 'development') {
+ console.group('>>>>>> 错误信息 >>>>>>')
+ console.log(info)
+ console.groupEnd();
+ console.group('>>>>>> Vue 实例 >>>>>>')
+ console.log(vm)
+ console.groupEnd();
+ console.group('>>>>>> Error >>>>>>')
+ console.log(err)
+ console.groupEnd();
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/config/permission.js b/src/config/permission.js
new file mode 100644
index 0000000..a0e4977
--- /dev/null
+++ b/src/config/permission.js
@@ -0,0 +1,88 @@
+/**
+ * 全站权限配置
+ *
+ */
+import router from '../router/router'
+import store from '../store/store'
+import nprogress from 'nprogress'
+import 'nprogress/nprogress.css'
+import {validatenull} from '../util/validate'
+import {getToken} from '../util/auth'
+
+nprogress.configure({showSpinner: false});
+const lockPage = store.getters.website.lockPage; //锁屏页
+router.beforeEach((to, from, next) => {
+ if (to.matched.length === 0 && to.fullPath.indexOf("?sec") === -1) {
+ next(to.path + "?sec");
+ window.location.reload();
+ } else {
+ next();
+ }
+ //缓冲设置
+ if (to.meta.keepAlive === true && store.state.tags.tagList.some(ele => {
+ return ele.value === to.fullPath;
+ })) {
+ to.meta.$keepAlive = true;
+ } else {
+ nprogress.start();
+ if (to.meta.keepAlive === true && validatenull(to.meta.$keepAlive)) {
+ to.meta.$keepAlive = true;
+ } else {
+ to.meta.$keepAlive = false;
+ }
+ }
+ const meta = to.meta || {};
+ if (getToken()) {
+ if (store.getters.isLock && to.path !== lockPage) { //如果系统激活锁屏,全部跳转到锁屏页
+ next({path: lockPage})
+ } else if (to.path === '/login') { //如果登录成功访问登录页跳转到主页
+ next({path: '/'})
+ } else {
+ //如果用户信息为空则获取用户信息,获取用户信息失败,跳转到登录页
+ if (store.getters.token.length === 0) {
+ store.dispatch('FedLogOut').then(() => {
+ next({path: '/login'})
+ })
+ } else {
+ const value = to.query.src || to.fullPath;
+ const label = to.query.name || to.name;
+ const meta = to.meta || router.$avueRouter.meta || {};
+ const i18n = to.query.i18n;
+ if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
+ store.commit('ADD_TAG', {
+ label: label,
+ value: value,
+ params: to.params,
+ query: to.query,
+ meta: (() => {
+ if (!i18n) {
+ return meta
+ }
+ return {
+ i18n: i18n
+ }
+ })(),
+ group: router.$avueRouter.group || []
+ });
+ }
+ next()
+ }
+ }
+ } else {
+ //判断是否需要认证,没有登录访问去登录页
+ if (meta.isAuth === false) {
+ next()
+ } else {
+ next('/login')
+ }
+ }
+});
+
+router.afterEach(() => {
+ nprogress.done();
+ let title = store.getters.tag.label;
+ let i18n = store.getters.tag.meta.i18n;
+ title = router.$avueRouter.generateTitle(title, i18n)
+ //根据当前的标签也获取label的值动态设置浏览器标题
+ router.$avueRouter.setTitle(title);
+});
diff --git a/src/config/website.js b/src/config/website.js
new file mode 100644
index 0000000..4dfc863
--- /dev/null
+++ b/src/config/website.js
@@ -0,0 +1,39 @@
+/**
+ * 全局配置文件
+ */
+export default {
+ title: "dc3",
+ indexTitle: 'DC3 UI',
+ clientId: 'dc3', // 客户端id
+ clientSecret: 'dc3_secret', // 客户端密钥
+ tenantMode: true, // 是否开启租户模式
+ logo: "D",
+ key: 'dc3',//配置主键,目前用于存储
+ lockPage: '/lock',
+ tokenTime: 6000,
+ //http的status默认放行不才用统一处理的,
+ statusWhiteList: [],
+ //配置首页不可关闭
+ isFirstPage: false,
+ fistPage: {
+ label: "首页",
+ value: "/wel/index",
+ params: {},
+ query: {},
+ meta: {
+ i18n: 'dashboard'
+ },
+ group: [],
+ close: false
+ },
+ //配置菜单的属性
+ menu: {
+ iconDefault: 'iconfont icon-caidan',
+ props: {
+ label: 'name',
+ path: 'path',
+ icon: 'source',
+ children: 'children'
+ }
+ }
+}
diff --git a/src/lang/en.js b/src/lang/en.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/lang/index.js b/src/lang/index.js
new file mode 100644
index 0000000..126efbd
--- /dev/null
+++ b/src/lang/index.js
@@ -0,0 +1,26 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
+import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN' // element-ui lang
+import enLocale from './en'
+import zhLocale from './zh'
+import {getStore} from '../util/store'
+
+Vue.use(VueI18n);
+const messages = {
+ en: {
+ ...enLocale,
+ ...elementEnLocale
+ },
+ zh: {
+ ...zhLocale,
+ ...elementZhLocale
+ }
+}
+
+const i18n = new VueI18n({
+ locale: getStore({name: 'language'}) || 'zh',
+ messages
+});
+
+export default i18n
\ No newline at end of file
diff --git a/src/lang/zh.js b/src/lang/zh.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/main.js b/src/main.js
index ab41fae..a75885d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,18 +1,47 @@
import Vue from 'vue'
-import App from './App.vue'
+import App from './App'
import router from './router/router'
import store from './store/store'
//Element UI
+import Element from 'element-ui'
import './plugins/element/element.js'
//Axios
-import axios from 'axios'
+import axios from './router/axios'
import VueAxios from 'vue-axios'
+//Util
+import './config/permission'
+import './config/error'
+import i18n from './lang'
+import {loadStyle} from './util/util'
+import * as urls from './config/env'
+import {iconfontUrl, iconfontVersion} from './config/env'
+import './styles/common.scss';
+import basicContainer from './components/basic-container/main'
-Vue.config.productionTip = false
+Vue.use(router);
Vue.use(VueAxios, axios);
+Vue.use(Element, {
+ i18n: (key, value) => i18n.t(key, value)
+});
+
+//注册全局容器
+Vue.component('basicContainer', basicContainer);
+
+// 加载相关url地址
+Object.keys(urls).forEach(key => {
+ Vue.prototype[key] = urls[key];
+});
+
+// 动态加载阿里云字体库
+iconfontVersion.forEach(ele => {
+ loadStyle(iconfontUrl.replace('$key', ele));
+});
+
+Vue.config.productionTip = false;
new Vue({
router,
store,
+ i18n,
render: h => h(App)
}).$mount('#app')
diff --git a/src/mixins/color.js b/src/mixins/color.js
new file mode 100644
index 0000000..6d2cee9
--- /dev/null
+++ b/src/mixins/color.js
@@ -0,0 +1,168 @@
+import { mapGetters } from "vuex";
+const version = require("element-ui/package.json").version; // element-ui version from node_modules
+const ORIGINAL_THEME = "#409EFF"; // default color
+export default function () {
+ return {
+ data() {
+ return {
+ themeVal: ORIGINAL_THEME
+ }
+ },
+ created() {
+ this.themeVal = this.colorName;
+ },
+ watch: {
+ themeVal(val, oldVal) {
+ this.$store.commit("SET_COLOR_NAME", val);
+ this.updateTheme(val, oldVal);
+ }
+ },
+ computed: {
+ ...mapGetters(["colorName"])
+ },
+ methods: {
+ updateTheme(val, oldVal) {
+ if (typeof val !== "string") return;
+ const head = document.getElementsByTagName("head")[0];
+ const themeCluster = this.getThemeCluster(val.replace("#", ""));
+ const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
+ const getHandler = (variable, id) => {
+ return () => {
+ const originalCluster = this.getThemeCluster(
+ ORIGINAL_THEME.replace("#", "")
+ );
+ const newStyle = this.updateStyle(
+ this[variable],
+ originalCluster,
+ themeCluster
+ );
+
+ let styleTag = document.getElementById(id);
+ if (!styleTag) {
+ styleTag = document.createElement("style");
+ styleTag.setAttribute("id", id);
+ head.appendChild(styleTag);
+ }
+ styleTag.innerText = newStyle;
+ };
+ };
+
+ const chalkHandler = getHandler("chalk", "chalk-style");
+
+ if (!this.chalk) {
+ const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
+ this.getCSSString(url, chalkHandler, "chalk");
+ } else {
+ chalkHandler();
+ }
+
+ const link = [].slice.call(
+ document.getElementsByTagName("head")[0].getElementsByTagName("link")
+ );
+ for (let i = 0; i < link.length; i++) {
+ const style = link[i];
+ if (style.href.includes('css')) {
+ this.getCSSString(style.href, innerText => {
+ const originalCluster = this.getThemeCluster(
+ ORIGINAL_THEME.replace("#", "")
+ );
+ const newStyle = this.updateStyle(
+ innerText,
+ originalCluster,
+ themeCluster
+ );
+ let styleTag = document.getElementById(i);
+ if (!styleTag) {
+ styleTag = document.createElement("style");
+ styleTag.id = i;
+ styleTag.innerText = newStyle;
+ head.appendChild(styleTag);
+ }
+ });
+ }
+ }
+
+ const styles = [].slice.call(document.querySelectorAll("style"))
+
+ styles.forEach(style => {
+ const {
+ innerText
+ } = style;
+ if (typeof innerText !== "string") return;
+ style.innerText = this.updateStyle(
+ innerText,
+ originalCluster,
+ themeCluster
+ );
+ });
+ },
+ updateStyle(style, oldCluster, newCluster) {
+ let newStyle = style;
+ oldCluster.forEach((color, index) => {
+ newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
+ });
+ return newStyle;
+ },
+
+ getCSSString(url, callback, variable) {
+ const xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = () => {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ if (variable) {
+ this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
+ }
+ callback(xhr.responseText);
+ }
+ };
+ xhr.open("GET", url);
+ xhr.send();
+ },
+
+ getThemeCluster(theme) {
+ const tintColor = (color, tint) => {
+ let red = parseInt(color.slice(0, 2), 16);
+ let green = parseInt(color.slice(2, 4), 16);
+ let blue = parseInt(color.slice(4, 6), 16);
+
+ if (tint === 0) {
+ // when primary color is in its rgb space
+ return [red, green, blue].join(",");
+ } else {
+ red += Math.round(tint * (255 - red));
+ green += Math.round(tint * (255 - green));
+ blue += Math.round(tint * (255 - blue));
+
+ red = red.toString(16);
+ green = green.toString(16);
+ blue = blue.toString(16);
+
+ return `#${red}${green}${blue}`;
+ }
+ };
+
+ const shadeColor = (color, shade) => {
+ let red = parseInt(color.slice(0, 2), 16);
+ let green = parseInt(color.slice(2, 4), 16);
+ let blue = parseInt(color.slice(4, 6), 16);
+
+ red = Math.round((1 - shade) * red);
+ green = Math.round((1 - shade) * green);
+ blue = Math.round((1 - shade) * blue);
+
+ red = red.toString(16);
+ green = green.toString(16);
+ blue = blue.toString(16);
+
+ return `#${red}${green}${blue}`;
+ };
+
+ const clusters = [theme];
+ for (let i = 0; i <= 9; i++) {
+ clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
+ }
+ clusters.push(shadeColor(theme, 0.1));
+ return clusters;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/page/index/top/index.vue b/src/page/index/top/index.vue
new file mode 100644
index 0000000..4067ad1
--- /dev/null
+++ b/src/page/index/top/index.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+ {{userInfo.userName}}
+
+
+
+
+ {{$t('navbar.dashboard')}}
+
+
+ {{$t('navbar.userinfo')}}
+
+ {{$t('navbar.logOut')}}
+
+
+
+
+
+
+
+
diff --git a/src/page/index/top/top-color.vue b/src/page/index/top/top-color.vue
new file mode 100644
index 0000000..ab433c0
--- /dev/null
+++ b/src/page/index/top/top-color.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/page/index/top/top-lang.vue b/src/page/index/top/top-lang.vue
new file mode 100644
index 0000000..e3e4b65
--- /dev/null
+++ b/src/page/index/top/top-lang.vue
@@ -0,0 +1,44 @@
+
+
+
+
+ 中文
+ English
+
+
+
+
+
+
+
diff --git a/src/page/index/top/top-lock.vue b/src/page/index/top/top-lock.vue
new file mode 100644
index 0000000..48ff32c
--- /dev/null
+++ b/src/page/index/top/top-lock.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/page/index/top/top-logs.vue b/src/page/index/top/top-logs.vue
new file mode 100644
index 0000000..d80f2d4
--- /dev/null
+++ b/src/page/index/top/top-logs.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/page/index/top/top-menu.vue b/src/page/index/top/top-menu.vue
new file mode 100644
index 0000000..f92d474
--- /dev/null
+++ b/src/page/index/top/top-menu.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/page/index/top/top-search.vue b/src/page/index/top/top-search.vue
new file mode 100644
index 0000000..40efd97
--- /dev/null
+++ b/src/page/index/top/top-search.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+ {{ item[labelKey] }}
+ {{ item[pathKey] }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/page/index/top/top-theme.vue b/src/page/index/top/top-theme.vue
new file mode 100644
index 0000000..b2713c2
--- /dev/null
+++ b/src/page/index/top/top-theme.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+ {{item.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/page/login/authredirect.vue b/src/page/login/authredirect.vue
new file mode 100644
index 0000000..15cbce1
--- /dev/null
+++ b/src/page/login/authredirect.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
diff --git a/src/page/login/codelogin.vue b/src/page/login/codelogin.vue
new file mode 100644
index 0000000..dfd8061
--- /dev/null
+++ b/src/page/login/codelogin.vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{msgText}}
+
+
+
+
+ {{$t('login.submit')}}
+
+
+
+
+
+
+
diff --git a/src/page/login/index.vue b/src/page/login/index.vue
new file mode 100644
index 0000000..39aa552
--- /dev/null
+++ b/src/page/login/index.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+ {{time}}
+
+

+
{{ $t('login.info') }}
+
+
+
+
+ {{ $t('login.title') }}{{website.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/page/login/thirdlogin.vue b/src/page/login/thirdlogin.vue
new file mode 100644
index 0000000..eda4d96
--- /dev/null
+++ b/src/page/login/thirdlogin.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
{{$t('login.wechat')}}
+
+
+
+
+
+
{{$t('login.qq')}}
+
+
+
+
+
+
+
diff --git a/src/page/login/userlogin.vue b/src/page/login/userlogin.vue
new file mode 100644
index 0000000..61495c2
--- /dev/null
+++ b/src/page/login/userlogin.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{$t('login.submit')}}
+
+
+
+
+
+
+
diff --git a/src/router/axios.js b/src/router/axios.js
new file mode 100644
index 0000000..759ea4a
--- /dev/null
+++ b/src/router/axios.js
@@ -0,0 +1,75 @@
+/**
+ * 全站http配置
+ *
+ * axios参数说明
+ * isSerialize是否开启form表单提交
+ * isToken是否需要token
+ */
+import axios from 'axios'
+import store from '../store/store';
+import router from '../router/router'
+import website from '../config/website';
+
+import nprogress from 'nprogress'
+import 'nprogress/nprogress.css'
+import {Message} from 'element-ui'
+import {serialize} from '../util/util'
+import {getToken} from '../util/auth'
+import {Base64} from 'js-base64';
+
+axios.defaults.timeout = 10000;
+//返回其他状态吗
+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();// start progress bar
+ const meta = (config.meta || {});
+ const isToken = meta.isToken === false;
+ config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;
+ if (getToken() && !isToken) {
+ config.headers['Blade-Auth'] = 'bearer ' + getToken() // 让每个请求携带token--['Authorization']为自定义key 请根据实际情况自行修改
+ }
+ //headers中配置serialize为true开启序列化
+ if (config.method === 'post' && meta.isSerialize === true) {
+ config.data = serialize(config.data);
+ }
+ return config
+}, error => {
+ return Promise.reject(error)
+});
+
+//http response拦截
+axios.interceptors.response.use(res => {
+ nprogress.done();
+ const status = res.data.code || 200;
+ const statusWhiteList = website.statusWhiteList || [];
+ const message = res.data.msg || '未知错误';
+ //如果在白名单里则自行catch逻辑处理
+ if (statusWhiteList.includes(status)) return Promise.reject(res);
+ //如果是401则跳转到登录页面
+ if (status === 401) store.dispatch('FedLogOut').then(() => router.push({path: '/login'}));
+ // 如果请求为非200否者默认统一处理
+ if (status !== 200) {
+ Message({
+ message: message,
+ type: 'error'
+ });
+ return Promise.reject(new Error(message))
+ }
+ return res;
+}, error => {
+ nprogress.done();
+ return Promise.reject(new Error(error));
+});
+
+export default axios;
diff --git a/src/router/page/index.js b/src/router/page/index.js
new file mode 100644
index 0000000..1033358
--- /dev/null
+++ b/src/router/page/index.js
@@ -0,0 +1,50 @@
+export default [{
+ path: '/login',
+ name: '登录页',
+ component: () =>
+ import( /* webpackChunkName: "page" */ '@/page/login/index'),
+ meta: {
+ keepAlive: true,
+ isTab: false,
+ isAuth: false
+ }
+},
+ {
+ path: '/404',
+ component: () =>
+ import( /* webpackChunkName: "page" */ '@/components/error-page/404'),
+ name: '404',
+ meta: {
+ keepAlive: true,
+ isTab: false,
+ isAuth: false
+ }
+
+ },
+ {
+ path: '/403',
+ component: () =>
+ import( /* webpackChunkName: "page" */ '@/components/error-page/403'),
+ name: '403',
+ meta: {
+ keepAlive: true,
+ isTab: false,
+ isAuth: false
+ }
+ },
+ {
+ path: '/500',
+ component: () =>
+ import( /* webpackChunkName: "page" */ '@/components/error-page/500'),
+ name: '500',
+ meta: {
+ keepAlive: true,
+ isTab: false,
+ isAuth: false
+ }
+ },
+ {
+ path: '*',
+ redirect: '/404'
+ }
+]
\ No newline at end of file
diff --git a/src/router/router.js b/src/router/router.js
index b3c5404..7360b5f 100644
--- a/src/router/router.js
+++ b/src/router/router.js
@@ -1,5 +1,5 @@
-import Vue from 'vue'
import VueRouter from 'vue-router'
+import PageRouter from './page/'
import Home from '../views/Home.vue'
import Things from '../views/Things.vue'
import Template from '../views/Template.vue'
@@ -11,8 +11,6 @@ import Picture from '../views/Picture.vue'
import Video from '../views/Video.vue'
import About from '../views/About.vue'
-Vue.use(VueRouter)
-
const routes = [
{
path: '/',
@@ -64,10 +62,24 @@ const routes = [
name: 'about',
component: About
}
-]
+];
-const router = new VueRouter({
+let Router = new VueRouter({
+ scrollBehavior(to, from, savedPosition) {
+ if (savedPosition) {
+ return savedPosition
+ } else {
+ if (from.meta.keepAlive) {
+ from.meta.savedPosition = document.body.scrollTop;
+ }
+ return {
+ x: 0,
+ y: to.meta.savedPosition || 0
+ }
+ }
+ },
routes
-})
+});
-export default router
+Router.addRoutes([...PageRouter]);
+export default Router
diff --git a/src/store/getters.js b/src/store/getters.js
new file mode 100644
index 0000000..b69b8c5
--- /dev/null
+++ b/src/store/getters.js
@@ -0,0 +1,28 @@
+const getters = {
+ nav: state => state.state.nav,
+
+ tag: state => state.tags.tag,
+ language: state => state.common.language,
+ website: state => state.common.website,
+ userInfo: state => state.user.userInfo,
+ colorName: state => state.common.colorName,
+ themeName: state => state.common.themeName,
+ isShade: state => state.common.isShade,
+ isCollapse: state => state.common.isCollapse,
+ keyCollapse: (state, getters) => getters.screen > 1 ? getters.isCollapse : false,
+ screen: state => state.common.screen,
+ isLock: state => state.common.isLock,
+ isFullScren: state => state.common.isFullScren,
+ lockPasswd: state => state.common.lockPasswd,
+ tagList: state => state.tags.tagList,
+ tagWel: state => state.tags.tagWel,
+ token: state => state.user.token,
+ roles: state => state.user.roles,
+ permission: state => state.user.permission,
+ menu: state => state.user.menu,
+ menuAll: state => state.user.menuAll,
+ logsList: state => state.logs.logsList,
+ logsLen: state => state.logs.logsList.length || 0,
+ logsFlag: (state, getters) => getters.logsLen === 0
+};
+export default getters
\ No newline at end of file
diff --git a/src/store/modules/common.js b/src/store/modules/common.js
new file mode 100644
index 0000000..cbcb463
--- /dev/null
+++ b/src/store/modules/common.js
@@ -0,0 +1,92 @@
+import {getStore, removeStore, setStore} from '../../util/store'
+import website from '../../config/website'
+
+const common = {
+
+ state: {
+ language: getStore({name: 'language'}) || 'zh',
+ isCollapse: false,
+ isFullScren: false,
+ isShade: false,
+ screen: -1,
+ isLock: getStore({name: 'isLock'}) || false,
+ showTag: true,
+ showDebug: true,
+ showCollapse: true,
+ showSearch: true,
+ showLock: true,
+ showFullScren: true,
+ showTheme: true,
+ showMenu: true,
+ showColor: true,
+ colorName: getStore({name: 'colorName'}) || '#409EFF',
+ themeName: getStore({name: 'themeName'}) || 'theme-default',
+ lockPasswd: getStore({name: 'lockPasswd'}) || '',
+ website: website,
+ },
+ mutations: {
+ SET_LANGUAGE: (state, language) => {
+ state.language = language
+ setStore({
+ name: 'language',
+ content: state.language
+ })
+ },
+ SET_SHADE: (state, active) => {
+ state.isShade = active;
+ },
+ SET_COLLAPSE: (state) => {
+ state.isCollapse = !state.isCollapse;
+ },
+ SET_FULLSCREN: (state) => {
+ state.isFullScren = !state.isFullScren;
+ },
+ SET_LOCK: (state) => {
+ state.isLock = true;
+ setStore({
+ name: 'isLock',
+ content: state.isLock,
+ type: 'session'
+ })
+ },
+ SET_SCREEN: (state, screen) => {
+ state.screen = screen;
+ },
+ SET_COLOR_NAME: (state, colorName) => {
+ state.colorName = colorName;
+ setStore({
+ name: 'colorName',
+ content: state.colorName,
+ })
+ },
+ SET_THEME_NAME: (state, themeName) => {
+ state.themeName = themeName;
+ setStore({
+ name: 'themeName',
+ content: state.themeName,
+ })
+ },
+ SET_LOCK_PASSWD: (state, lockPasswd) => {
+ state.lockPasswd = lockPasswd;
+ setStore({
+ name: 'lockPasswd',
+ content: state.lockPasswd,
+ type: 'session'
+ })
+ },
+ CLEAR_LOCK: (state) => {
+ state.isLock = false;
+ state.lockPasswd = '';
+ removeStore({
+ name: 'lockPasswd',
+ type: 'session'
+ });
+ removeStore({
+ name: 'isLock',
+ type: 'session'
+ });
+ },
+ }
+};
+
+export default common
\ No newline at end of file
diff --git a/src/store/modules/logs.js b/src/store/modules/logs.js
new file mode 100644
index 0000000..fdf959d
--- /dev/null
+++ b/src/store/modules/logs.js
@@ -0,0 +1,43 @@
+import {getStore, setStore} from '../../util/store'
+import {dateFormat} from '../../util/date'
+import {sendLogs} from '../../api/user'
+
+const logs = {
+ state: {
+ logsList: getStore({name: 'logsList'}) || [],
+ },
+ actions: {
+ //发送错误日志
+ SendLogs({state, commit}) {
+ return new Promise((resolve, reject) => {
+ sendLogs(state.logsList).then(() => {
+ commit('CLEAR_LOGS');
+ resolve();
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+ },
+ mutations: {
+ ADD_LOGS: (state, {type, message, stack, info}) => {
+ state.logsList.push(Object.assign({
+ url: window.location.href,
+ time: dateFormat(new Date())
+ }, {
+ type,
+ message,
+ stack,
+ info: info.toString()
+ }))
+ setStore({name: 'logsList', content: state.logsList})
+ },
+ CLEAR_LOGS: (state) => {
+ state.logsList = [];
+ setStore({name: 'logsList', content: state.logsList})
+ }
+ }
+
+};
+
+export default logs;
\ No newline at end of file
diff --git a/src/store/modules/tags.js b/src/store/modules/tags.js
new file mode 100644
index 0000000..ecb9a80
--- /dev/null
+++ b/src/store/modules/tags.js
@@ -0,0 +1,73 @@
+import {getStore, setStore} from '../../util/store'
+import {diff} from '../../util/util'
+import website from '../../config/website'
+
+const isFirstPage = website.isFirstPage;
+const tagWel = website.fistPage;
+const tagObj = {
+ label: '', //标题名称
+ value: '', //标题的路径
+ params: '', //标题的路径参数
+ query: '', //标题的参数
+ meta: {},//额外参数
+ group: [], //分组
+}
+
+//处理首个标签
+function setFistTag(list) {
+ if (list.length == 1) {
+ list[0].close = false;
+ } else {
+ list.forEach(ele => {
+ if (ele.value === tagWel.value && isFirstPage === false) {
+ ele.close = false
+ } else {
+ ele.close = true
+ }
+ })
+ }
+}
+
+
+const navs = {
+ state: {
+ tagList: getStore({name: 'tagList'}) || [],
+ tag: getStore({name: 'tag'}) || tagObj,
+ tagWel: tagWel
+ },
+ actions: {},
+ mutations: {
+ ADD_TAG: (state, action) => {
+ state.tag = action;
+ setStore({name: 'tag', content: state.tag, type: 'session'})
+ if (state.tagList.some(ele => diff(ele, action))) return
+ state.tagList.push(action)
+ setFistTag(state.tagList);
+ setStore({name: 'tagList', content: state.tagList, type: 'session'})
+ },
+ DEL_TAG: (state, action) => {
+ state.tagList = state.tagList.filter(item => {
+ return !diff(item, action);
+ })
+ setFistTag(state.tagList);
+ setStore({name: 'tagList', content: state.tagList, type: 'session'})
+ },
+ DEL_ALL_TAG: (state) => {
+ state.tagList = [state.tagWel];
+ setStore({name: 'tagList', content: state.tagList, type: 'session'})
+ },
+ DEL_TAG_OTHER: (state) => {
+ state.tagList = state.tagList.filter(item => {
+ if (item.value === state.tag.value) {
+ return true;
+ } else if (!website.isFirstPage && item.value === website.fistPage.value) {
+ return true;
+ }
+ })
+ setFistTag(state.tagList);
+ setStore({name: 'tagList', content: state.tagList, type: 'session'})
+ },
+ }
+};
+
+export default navs
\ 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..689c385
--- /dev/null
+++ b/src/store/modules/user.js
@@ -0,0 +1,200 @@
+import {removeToken, setToken} from '../../util/auth'
+import {getStore, setStore} from '../../util/store'
+import {isURL, validatenull} from '../../util/validate'
+import {deepClone} from '../../util/util'
+import website from '../../config/website'
+import {getButtons, getMenu, getTopMenu, getUserInfo, loginByUsername, logout, refeshToken} from '../../api/user'
+
+function addPath(ele, first) {
+ const menu = website.menu;
+ const propsConfig = menu.props;
+ const propsDefault = {
+ label: propsConfig.label || 'name',
+ path: propsConfig.path || 'path',
+ icon: propsConfig.icon || 'icon',
+ children: propsConfig.children || 'children'
+ }
+ const icon = ele[propsDefault.icon];
+ ele[propsDefault.icon] = validatenull(icon) ? menu.iconDefault : icon;
+ const isChild = ele[propsDefault.children] && ele[propsDefault.children].length !== 0;
+ if (!isChild) ele[propsDefault.children] = [];
+ if (!isChild && first && !isURL(ele[propsDefault.path])) {
+ ele[propsDefault.path] = ele[propsDefault.path] + '/index'
+ } else {
+ ele[propsDefault.children].forEach(child => {
+ addPath(child);
+ })
+ }
+
+}
+
+const user = {
+ state: {
+ userInfo: getStore({name: 'userInfo'}) || [],
+ permission: getStore({name: 'permission'}) || {},
+ roles: [],
+ menu: getStore({name: 'menu'}) || [],
+ menuAll: [],
+ token: getStore({name: 'token'}) || '',
+ },
+ actions: {
+ //根据用户名登录
+ LoginByUsername({commit}, userInfo) {
+ return new Promise((resolve, reject) => {
+ loginByUsername(userInfo.tenantId, userInfo.username, userInfo.password, userInfo.type).then(res => {
+ const data = res.data.data;
+ commit('SET_TOKEN', data.accessToken);
+ commit('SET_USERIFNO', data);
+ commit('DEL_ALL_TAG');
+ commit('CLEAR_LOCK');
+ resolve();
+ }).catch(error => {
+ reject(error);
+ })
+ })
+ },
+ GetButtons({commit}) {
+ return new Promise((resolve) => {
+ getButtons().then(res => {
+ const data = res.data.data;
+ commit('SET_PERMISSION', data);
+ resolve();
+ })
+ })
+ },
+ //根据手机号登录
+ LoginByPhone({commit}, userInfo) {
+ return new Promise((resolve) => {
+ loginByUsername(userInfo.phone, userInfo.code).then(res => {
+ const data = res.data.data;
+ commit('SET_TOKEN', data);
+ commit('DEL_ALL_TAG');
+ commit('CLEAR_LOCK');
+ resolve();
+ })
+ })
+ },
+ GetUserInfo({commit}) {
+ return new Promise((resolve, reject) => {
+ getUserInfo().then((res) => {
+ const data = res.data.data;
+ commit('SET_ROLES', data.roles);
+ resolve(data);
+ }).catch(err => {
+ reject(err);
+ })
+ })
+ },
+ //刷新token
+ RefeshToken({state, commit}) {
+ return new Promise((resolve, reject) => {
+ refeshToken(state.refeshToken).then(res => {
+ const data = res.data.data;
+ commit('SET_TOKEN', data);
+ resolve(data);
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+ // 登出
+ LogOut({commit}) {
+ return new Promise((resolve, reject) => {
+ logout().then(() => {
+ commit('SET_TOKEN', '')
+ commit('SET_MENU', [])
+ commit('SET_ROLES', [])
+ commit('DEL_ALL_TAG');
+ commit('CLEAR_LOCK');
+ removeToken()
+ resolve()
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+ //注销session
+ FedLogOut({commit}) {
+ return new Promise(resolve => {
+ commit('SET_TOKEN', '')
+ commit('SET_MENU', [])
+ commit('SET_ROLES', [])
+ commit('DEL_ALL_TAG');
+ commit('CLEAR_LOCK');
+ removeToken()
+ resolve()
+ })
+ },
+ GetTopMenu() {
+ return new Promise(resolve => {
+ getTopMenu().then((res) => {
+ const data = res.data.data || []
+ resolve(data)
+ })
+ })
+ },
+ //获取系统菜单
+ GetMenu({commit, dispatch}, parentId) {
+ return new Promise(resolve => {
+ getMenu(parentId).then((res) => {
+ const data = res.data.data
+ let menu = deepClone(data);
+ menu.forEach(ele => {
+ addPath(ele, true);
+ })
+ commit('SET_MENU', menu)
+ dispatch('GetButtons');
+ resolve(menu)
+ })
+ })
+ },
+ },
+ mutations: {
+ SET_TOKEN: (state, token) => {
+ setToken(token)
+ state.token = token;
+ setStore({name: 'token', content: state.token, type: 'session'})
+ },
+ SET_USERIFNO: (state, userInfo) => {
+ state.userInfo = userInfo;
+ setStore({name: 'userInfo', content: state.userInfo})
+ },
+ SET_MENU: (state, menu) => {
+ state.menu = menu
+ setStore({name: 'menu', content: state.menu, type: 'session'})
+ },
+ SET_MENU_ALL: (state, menuAll) => {
+ state.menuAll = menuAll;
+ },
+ SET_ROLES: (state, roles) => {
+ state.roles = roles;
+ },
+ SET_PERMISSION: (state, permission) => {
+ let result = [];
+
+ function getCode(list) {
+ list.forEach(ele => {
+ if (typeof (ele) === 'object') {
+ const chiildren = ele.children;
+ const code = ele.code;
+ if (chiildren) {
+ getCode(chiildren)
+ } else {
+ result.push(code);
+ }
+ }
+
+ })
+ }
+
+ getCode(permission);
+ state.permission = {};
+ result.forEach(ele => {
+ state.permission[ele] = true;
+ });
+ setStore({name: 'permission', content: state.permission, type: 'session'})
+ }
+ }
+
+}
+export default user
diff --git a/src/store/store.js b/src/store/store.js
index 5955c89..0f4a5a9 100644
--- a/src/store/store.js
+++ b/src/store/store.js
@@ -1,20 +1,29 @@
import Vue from 'vue'
import Vuex from 'vuex'
+import user from './modules/user'
+import common from './modules/common'
+import tags from './modules/tags'
+import logs from './modules/logs'
+import getters from './getters'
-Vue.use(Vuex)
+Vue.use(Vuex);
-export default new Vuex.Store({
- state: {
- nav: '/'
- },
- mutations: {
- handleSelect(state, index) {
- state.nav = index;
- }
- },
- getters:{
- getNav(state){
- return state.nav;
- }
- }
-})
+const store = new Vuex.Store({
+ state: {
+ nav: '/'
+ },
+ mutations: {
+ handleSelect(state, index) {
+ state.nav = index;
+ }
+ },
+ modules: {
+ user,
+ common,
+ logs,
+ tags
+ },
+ getters
+});
+
+export default store
\ No newline at end of file
diff --git a/src/styles/animate/vue-transition.scss b/src/styles/animate/vue-transition.scss
new file mode 100644
index 0000000..c73902c
--- /dev/null
+++ b/src/styles/animate/vue-transition.scss
@@ -0,0 +1,65 @@
+// 过渡动画 横向渐变
+.fade-transverse-leave-active,
+.fade-transverse-enter-active {
+ transition: all .5s;
+}
+
+.fade-transverse-enter {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+
+.fade-transverse-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+// 过渡动画 缩放渐变
+.fade-scale-leave-active,
+.fade-scale-enter-active {
+ transition: all .5s;
+}
+
+.fade-scale-enter {
+ opacity: 0;
+ transform: scale(1.2);
+}
+
+.fade-scale-leave-to {
+ opacity: 0;
+ transform: scale(0.8);
+}
+
+@-webkit-keyframes animate-cloud {
+ from {
+ background-position: 600px 100%;
+ }
+ to {
+ background-position: 0 100%;
+ }
+ }
+ @-moz-keyframes animate-cloud {
+ from {
+ background-position: 600px 100%;
+ }
+ to {
+ background-position: 0 100%;
+ }
+ }
+ @-ms-keyframes animate-cloud {
+ from {
+ background-position: 600px 100%;
+ }
+ to {
+ background-position: 0 100%;
+ }
+ }
+ @-o-keyframes animate-cloud {
+ from {
+ background-position: 600px 100%;
+ }
+ to {
+ background-position: 0 100%;
+ }
+ }
+
\ No newline at end of file
diff --git a/src/styles/common.scss b/src/styles/common.scss
new file mode 100644
index 0000000..82a1d0b
--- /dev/null
+++ b/src/styles/common.scss
@@ -0,0 +1,28 @@
+// 全局变量
+@import './variables.scss';
+// ele样式覆盖
+@import './element-ui.scss';
+// 顶部右侧显示
+@import './top.scss';
+// 导航标签
+@import './tags.scss';
+// 工具类函数
+@import './mixin.scss';
+// 侧面导航栏
+@import './sidebar.scss';
+// 动画
+@import './animate/vue-transition.scss';
+//主题
+@import './theme/index.scss';
+//适配
+@import './media.scss';
+//通用配置
+@import './normalize.scss';
+
+a{
+ text-decoration: none;
+ color:#333;
+}
+*{
+ outline: none;
+}
\ No newline at end of file
diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss
new file mode 100644
index 0000000..71f33c5
--- /dev/null
+++ b/src/styles/element-ui.scss
@@ -0,0 +1,71 @@
+.el-dropdown-menu__item {
+ font-size: 12px !important;
+ line-height: 28px !important;
+}
+.el-card.is-always-shadow {
+ box-shadow: none;
+ border: none !important;
+}
+
+.el-scrollbar__view {
+ height: 100%;
+}
+
+.el-menu--horizontal {
+ border-bottom: none !important;
+}
+
+.el-menu {
+ border-right: none !important;
+}
+
+.el-menu--display,
+.el-menu--display+.el-submenu__icon-arrow {
+ display: none;
+}
+
+
+
+.el-message__icon,
+.el-message__content {
+ display: inline-block;
+}
+
+.el-date-editor .el-range-input,
+.el-date-editor .el-range-separator {
+ height: auto;
+ overflow: hidden;
+}
+
+.el-dialog__wrapper {
+ z-index: 2048;
+}
+
+.el-scrollbar__wrap {
+ overflow-x: hidden;
+}
+
+.el-col {
+ margin-bottom: 8px;
+}
+
+.el-main {
+ padding: 0 !important;
+}
+.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .el-menu--horizontal>.el-submenu .el-submenu__title:hover {
+ background-color:transparent;
+}
+
+
+.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .el-menu--horizontal>.el-submenu .el-submenu__title:hover{
+ background-color: transparent !important;
+}
+
+.el-card__header {
+ padding: 6px 18px !important;
+}
+
+.el-card__body {
+ padding: 16px !important;
+}
+
diff --git a/src/styles/login.scss b/src/styles/login.scss
new file mode 100644
index 0000000..98df885
--- /dev/null
+++ b/src/styles/login.scss
@@ -0,0 +1,163 @@
+.login-container {
+ display: flex;
+ align-items: center;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ margin: 0 auto;
+ background: url("http://www.17sucai.com/preview/242158/2015-01-10/%E7%99%BB%E5%BD%95/images/cloud.jpg")
+ 0 bottom repeat-x #049ec4;
+ animation: animate-cloud 20s linear infinite;
+}
+.login-weaper {
+ margin: 0 auto;
+ width: 1000px;
+ box-shadow: -4px 5px 10px rgba(0, 0, 0, 0.4);
+ .el-input-group__append {
+ border: none;
+ }
+}
+
+.login-left,
+.login-border {
+ position: relative;
+ min-height: 500px;
+ align-items: center;
+ display: flex;
+}
+.login-left {
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+ justify-content: center;
+ flex-direction: column;
+ background-color: #409EFF;
+ color: #fff;
+ float: left;
+ width: 50%;
+ position: relative;
+}
+.login-left .img {
+ width: 140px;
+}
+.login-time {
+ position: absolute;
+ left: 25px;
+ top: 25px;
+ width: 100%;
+ color: #fff;
+ font-weight: 200;
+ opacity: 0.9;
+ font-size: 18px;
+ overflow: hidden;
+}
+.login-left .title {
+ margin-top: 60px;
+ text-align: center;
+ color: #fff;
+ font-weight: 300;
+ letter-spacing: 2px;
+ font-size: 25px;
+}
+
+.login-border {
+ border-left: none;
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+ color: #fff;
+ background-color: #fff;
+ width: 50%;
+ float: left;
+ box-sizing: border-box;
+}
+.login-main {
+ margin: 0 auto;
+ width: 65%;
+ box-sizing: border-box;
+}
+.login-main > h3 {
+ margin-bottom: 20px;
+}
+.login-main > p {
+ color: #76838f;
+}
+.login-title {
+ color: #333;
+ margin-bottom: 40px;
+ font-weight: 500;
+ font-size: 22px;
+ text-align: center;
+ letter-spacing: 4px;
+}
+.login-menu {
+ margin-top: 40px;
+ width: 100%;
+ text-align: center;
+ a {
+ color: #999;
+ font-size: 12px;
+ margin: 0px 8px;
+ }
+}
+.login-submit {
+ width: 100%;
+ height: 45px;
+ border: 1px solid #409EFF;
+ background: none;
+ font-size: 18px;
+ letter-spacing: 2px;
+ font-weight: 300;
+ color: #409EFF;
+ cursor: pointer;
+ margin-top: 30px;
+ font-family: "neo";
+ transition: 0.25s;
+}
+.login-form {
+ margin: 10px 0;
+ i {
+ color: #333;
+ }
+ .el-form-item__content {
+ width: 100%;
+ }
+ .el-form-item {
+ margin-bottom: 12px;
+ }
+ .el-input {
+ input {
+ padding-bottom: 10px;
+ text-indent: 5px;
+ background: transparent;
+ border: none;
+ border-radius: 0;
+ color: #333;
+ border-bottom: 1px solid rgb(235, 237, 242);
+ }
+ .el-input__prefix {
+ i {
+ padding: 0 5px;
+ font-size: 16px !important;
+ }
+ }
+ }
+}
+.login-code {
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ margin: 0 0 0 10px;
+}
+.login-code-img {
+ margin-top: 2px;
+ width: 100px;
+ height: 38px;
+ background-color: #fdfdfd;
+ border: 1px solid #f0f0f0;
+ color: #333;
+ font-size: 14px;
+ font-weight: bold;
+ letter-spacing: 5px;
+ line-height: 38px;
+ text-indent: 5px;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/src/styles/media.scss b/src/styles/media.scss
new file mode 100644
index 0000000..44bede4
--- /dev/null
+++ b/src/styles/media.scss
@@ -0,0 +1,165 @@
+.avue-left,
+.avue-header,
+.avue-top,
+.avue-logo,
+.avue-layout
+.login-logo,
+.avue-main {
+ transition: all .3s;
+}
+.avue-contail {
+ width: 100%;
+ height: 100%;
+ background: #f0f2f5;
+ background-size: 100%;
+ background-repeat: no-repeat;
+}
+
+
+.avue-left {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 240px;
+ height: 100%;
+ z-index: 1025;
+}
+
+.avue--collapse {
+ .avue-left,
+ .avue-logo {
+ width: 60px;
+ }
+ .avue-header {
+ padding-left: 60px;
+ }
+ .avue-main {
+ width: calc(100% - 60px);
+ left: 60px;
+ }
+}
+
+.avue-header {
+ padding-left: 240px;
+ width: 100%;
+ background-color: #fff;
+ box-sizing: border-box;
+}
+
+.avue-main {
+ position: absolute;
+ left: 240px;
+ padding: 0;
+ padding-bottom: 20px;
+ width: calc(100% - 240px);
+ height: calc(100% - 70px);
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.avue-view {
+ padding-bottom: 22px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.avue-footer {
+ margin: 0 auto;
+ padding: 0 22px;
+ width: 1300px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .logo {
+ margin-left: -50px;
+ }
+ .copyright {
+ color: #666;
+ line-height: 1.5;
+ font-size: 12px;
+ }
+}
+
+.avue-shade {
+ position: fixed;
+ display: none;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, .3);
+ z-index: 1024;
+ &--show {
+ display: block;
+ }
+}
+
+@media screen and (max-width: 992px) {
+ $width: 240px;
+ // ele的自适应
+ .el-dialog,
+ .el-message-box {
+ width: 98% !important;
+ }
+ //登录页面
+ .login-left {
+ display: none !important;
+ }
+ .login-logo {
+ padding-top: 30px !important;
+ margin-left: -30px;
+ }
+ .login-weaper{
+ margin: 0 auto;
+ width: 96% !important;
+ }
+ .login-border {
+ border-radius: 5px;
+ padding: 40px;
+ margin: 0 auto;
+ float: none !important;
+ width: 100% !important;
+ }
+ .login-main {
+ width: 100% !important;
+ }
+ //主框架
+ .avue-tags {
+ display: none;
+ }
+ .avue-left,
+ .avue-logo {
+ left: -$width;
+ }
+ .avue-main {
+ left: 0;
+ width: 100%;
+ }
+ .avue-header {
+ margin-bottom: 15px;
+ padding-left: 15px;
+ }
+ .top-bar__item {
+ display: none;
+ }
+ .avue--collapse {
+ .avue-left,
+ .avue-logo {
+ width: $width;
+ left: 0;
+ }
+ .avue-main {
+ left: $width;
+ width: 100%;
+ }
+ .avue-header {
+ padding: 0;
+ transform: translate3d(230px, 0, 0);
+ }
+ .avue-shade {
+ display: block;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/mixin.scss b/src/styles/mixin.scss
new file mode 100644
index 0000000..0523edd
--- /dev/null
+++ b/src/styles/mixin.scss
@@ -0,0 +1,73 @@
+@mixin clearfix {
+ &:after {
+ content: "";
+ display: table;
+ clear: both;
+ }
+}
+
+@mixin scrollBar {
+ ::-webkit-scrollbar-track-piece {
+ background-color: transparent;
+ }
+ ::-webkit-scrollbar {
+ width: 7px;
+ height: 7px;
+ background-color: transparent;
+ }
+ ::-webkit-scrollbar-thumb {
+ border-radius: 5px;
+ background-color: hsla(220, 4%, 58%, .3);
+ }
+}
+
+@mixin radius($width, $size, $color) {
+ width: $width;
+ height: $width;
+ line-height: $width;
+ border-radius: $width;
+ text-align: center;
+ border-width: $size;
+ border-style: solid;
+ border-color: $color;
+}
+
+@mixin relative {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+@mixin pct($pct) {
+ width: #{$pct};
+ position: relative;
+ margin: 0 auto;
+}
+
+@mixin triangle($width, $height, $color, $direction) {
+ $width: $width/2;
+ $color-border-style: $height solid $color;
+ $transparent-border-style: $width solid transparent;
+ height: 0;
+ width: 0;
+ @if $direction==up {
+ border-bottom: $color-border-style;
+ border-left: $transparent-border-style;
+ border-right: $transparent-border-style;
+ }
+ @else if $direction==right {
+ border-left: $color-border-style;
+ border-top: $transparent-border-style;
+ border-bottom: $transparent-border-style;
+ }
+ @else if $direction==down {
+ border-top: $color-border-style;
+ border-left: $transparent-border-style;
+ border-right: $transparent-border-style;
+ }
+ @else if $direction==left {
+ border-right: $color-border-style;
+ border-top: $transparent-border-style;
+ border-bottom: $transparent-border-style;
+ }
+}
\ No newline at end of file
diff --git a/src/styles/normalize.scss b/src/styles/normalize.scss
new file mode 100644
index 0000000..e51d268
--- /dev/null
+++ b/src/styles/normalize.scss
@@ -0,0 +1,502 @@
+/*! normalize.css v2.1.2 | MIT License | git.io/normalize */
+/*
+/*! 我就是自己看看,然后翻译下下,让大家看看 */
+
+/* ==========================================================================
+ HTML5 display definitions
+
+ HTML5 新增元素定义
+
+ ========================================================================== */
+
+/**
+ * Correct `block` display not defined in IE 8/9.
+ *
+ * 修正IE 8/9 中未定义的块级元素。
+ */
+
+ article,
+ aside,
+ details,
+ figcaption,
+ figure,
+ footer,
+ header,
+ hgroup,
+ main,
+ nav,
+ section,
+ summary {
+ display: block;
+ }
+
+ /**
+ * Correct `inline-block` display not defined in IE 8/9.
+ *
+ * 修正在 IE 8/9 中未定义的 'inline-block' 元素。
+ */
+
+ audio,
+ canvas,
+ video {
+ display: inline-block;
+ }
+
+ /**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ *
+ * 阻止现在浏览器显示未定义 control 播放控件的 'audio' 声音元素。
+ * 删除 IOS 5 设备中显示的多余的高度。
+ */
+
+ audio:not([controls]) {
+ display: none;
+ height: 0;
+ }
+
+ /**
+ * Address styling not present in IE 8/9.
+ *
+ * 处理 IE 8/9 中不存在的样式。
+ */
+
+ [hidden] {
+ display: none;
+ }
+
+ /* ==========================================================================
+ Base
+
+ 基本设置
+ ========================================================================== */
+
+ /**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS text size adjust after orientation change, without disabling
+ * user zoom.
+ *
+ * 1. 设置默认字体类型为 sans-serif.
+ * 2. 当用户放大或缩小页面时不改变字体大小。
+ */
+
+ html {
+ font-family: sans-serif; /* 1 */
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+ }
+
+ /**
+ * Remove default margin.
+ *
+ * 删除默认边距。
+ */
+
+ body {
+ margin: 0;
+ }
+
+ /* ==========================================================================
+ Links
+
+ 链接
+ ========================================================================== */
+
+ /**
+ * Address `outline` inconsistency between Chrome and other browsers.
+ *
+ * 处理 Chrome 与其它浏览器中关于 'outline' 的不一致性。
+ */
+
+ a:focus {
+ outline: thin dotted;
+ }
+
+ /**
+ * Improve readability when focused and also mouse hovered in all browsers.
+ *
+ * 为所有浏览器改善当激活或悬停在元素上时元素内容的可读性。
+ */
+
+ a:active,
+ a:hover {
+ outline: 0;
+ }
+
+ /* ==========================================================================
+ Typography
+
+ 排版
+ ========================================================================== */
+
+ /**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari 5, and Chrome.
+ *
+ * 处理多变的 'h1' 字体大小及其在 Firefox 4+, Safari 5, 及 Chrome时浏览器中的
+ * 'section' 与 'article' 元素中的边距。
+ */
+
+ h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+ }
+
+ /**
+ * Address styling not present in IE 8/9, Safari 5, and Chrome.
+ *
+ * 处理在 IE 8/9, Safari 5, 及 Chrome 没有的样式。
+ */
+
+ abbr[title] {
+ border-bottom: 1px dotted;
+ }
+
+ /**
+ * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
+ *
+ * 处理 Firefox 4+, Safari 5, 及 Chrome 中默认的 'bolder' 样式为 'bold'.
+ */
+
+ b,
+ strong {
+ font-weight: bold;
+ }
+
+ /**
+ * Address styling not present in Safari 5 and Chrome.
+ *
+ * 处理在 Safari 5 和 Chrome 没有的样式。
+ */
+
+ dfn {
+ font-style: italic;
+ }
+
+ /**
+ * Address differences between Firefox and other browsers.
+ *
+ * 处理 Firefox 与其它浏览器的差异。
+ */
+
+ hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+ }
+
+ /**
+ * Address styling not present in IE 8/9.
+ *
+ * 处理在 IE 8/9 中没有的样式。
+ */
+
+ mark {
+ background: #ff0;
+ color: #000;
+ }
+
+ /**
+ * Correct font family set oddly in Safari 5 and Chrome.
+ *
+ * 修正确 Safari 5 和 Chrome 中古怪的默认字体。
+ */
+
+ code,
+ kbd,
+ pre,
+ samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+ }
+
+ /**
+ * Improve readability of pre-formatted text in all browsers.
+ *
+ * 为所有浏览器改善预格式化文本的可读性。
+ */
+
+ pre {
+ white-space: pre-wrap;
+ }
+
+ /**
+ * Set consistent quote types.
+ *
+ * 设置一致的引用格式。
+ */
+
+ q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+ }
+
+ /**
+ * Address inconsistent and variable font size in all browsers.
+ *
+ * 处理所有浏览器中字体大小的不一致性[译者注:原文直译为:处理所有
+ * 浏览器中的不一致和多变的字体大小]。
+ */
+
+ small {
+ font-size: 80%;
+ }
+
+ /**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ *
+ * 阻止所有浏览器中 'sub' 和 'sup' 元素影响 'line-height'.
+ * [译者注:就是不让上标与下标影响行高。]
+ */
+
+ sub,
+ sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+ }
+
+ sup {
+ top: -0.5em;
+ }
+
+ sub {
+ bottom: -0.25em;
+ }
+
+ /* ==========================================================================
+ Embedded content
+
+ 嵌入的内容
+ ========================================================================== */
+
+ /**
+ * Remove border when inside `a` element in IE 8/9.
+ *
+ * 删除 IE 8/9 中当内容位于 'a' 中出现的边框。
+ */
+
+ img {
+ border: 0;
+ }
+
+ /**
+ * Correct overflow displayed oddly in IE 9.
+ *
+ * 修正 IE 9 中显示古怪的溢出内容。
+ */
+
+ svg:not(:root) {
+ overflow: hidden;
+ }
+
+ /* ==========================================================================
+ Figures
+
+ Figure 图像/图表/代码等
+ ========================================================================== */
+
+ /**
+ * Address margin not present in IE 8/9 and Safari 5.
+ *
+ * 处理在 IE 8/9 和 Safari 5 没有的边距。
+ */
+
+ figure {
+ margin: 0;
+ }
+
+ /* ==========================================================================
+ Forms
+ ========================================================================== */
+
+ /**
+ * Define consistent border, margin, and padding.
+ *
+ * 定义一致的边框、外边距及内边距。
+ */
+
+ fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+ }
+
+ /**
+ * 1. Correct `color` not being inherited in IE 8/9.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ * 1. 修正在 IE 8/9 中没有继承的 'color'.
+ *
+ * [译者注:说是修正颜色嘛,可下面没有关于颜色的呀,这也行?求大神解释!]
+ * 2. 去掉内边距,避免当用户清空表单组时认为出错了。
+ */
+
+ legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+ }
+
+ /**
+ * 1. Correct font family not being inherited in all browsers.
+ * 2. Correct font size not being inherited in all browsers.
+ * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
+ *
+ * 1. 修正所有浏览器中未被继承的字体类型。
+ * 2. 修正所有浏览器中未被继承的字体大小。
+ * 3. 处理 Firefox 4+, Safari 5, 及 Chrome 中默认设置不同的外边距。
+ */
+
+ button,
+ input,
+ select,
+ textarea {
+ font-family: inherit; /* 1 */
+ font-size: 100%; /* 2 */
+ margin: 0; /* 3 */
+ }
+
+ /**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ *
+ * 处理 Firefox 4+ 中的客户端样式表里使用 '!important' 设置的 'line-height'.
+ */
+
+ button,
+ input {
+ line-height: normal;
+ }
+
+ /**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
+ * Correct `select` style inheritance in Firefox 4+ and Opera.
+ *
+ * 处理 'button' 和 'select' 的 'text-transform' 继承的不一致性。
+ * 所有其它表单控件元素不继承 'text-transform' 的值。
+ * 修正 Chrome, Safari 5+, 及 IE 8+ 中 'button' 的继承样式。
+ * 修正 Firefox 4+ 和 Opera 中 'select' 的继承样式。
+ */
+
+ button,
+ select {
+ text-transform: none;
+ }
+
+ /**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ *
+ * 1. 避免 Android 4.0.* 中 WebKit 的一个bug, 防止 'audio' 与 'video' 的播放控件失效。
+ * 2. 修正 iOS 中不可点击的 'input' 样式。
+ * 3. 改善图片类型的 'input' 等光标样式的可用性与一致性。
+ */
+
+ button,
+ html input[type="button"], /* 1 */
+ input[type="reset"],
+ input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+ }
+
+ /**
+ * Re-set default cursor for disabled elements.
+ *
+ * 重置不可用元素的默认光标样式。
+ */
+
+ button[disabled],
+ html input[disabled] {
+ cursor: default;
+ }
+
+ /**
+ * 1. Address box sizing set to `content-box` in IE 8/9.
+ * 2. Remove excess padding in IE 8/9.
+ *
+ * 1. 处理 IE 8/9 中设置为 'content-box' 的盒子模型。
+ * 2. 删除 IE 8/9 中多余的内边距。
+ */
+
+ input[type="checkbox"],
+ input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+ }
+
+ /**
+ * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
+ * (include `-moz` to future-proof).
+ *
+ * 1. 处理 Safari 5 和 Chrome 中默认设置为 'appearance' 的 'searchfield'.
+ * 2. 处理 Safari 5 和 Chrome 中默认设置为 'box-sizing' 的 'border-box'
+ * (包括不会过时的 '-moz').
+ */
+
+ input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box; /* 2 */
+ box-sizing: content-box;
+ }
+
+ /**
+ * Remove inner padding and search cancel button in Safari 5 and Chrome
+ * on OS X.
+ *
+ * 删除 Safari 5 和 OS X 上的 Chrome 中的输入框上的内边距和搜索取消按钮。
+ */
+
+ input[type="search"]::-webkit-search-cancel-button,
+ input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+ }
+
+ /**
+ * Remove inner padding and border in Firefox 4+.
+ *
+ * 删除 Firefox 4+ button 与 input 上的内边距。
+ */
+
+ button::-moz-focus-inner,
+ input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+ }
+
+ /**
+ * 1. Remove default vertical scrollbar in IE 8/9.
+ * 2. Improve readability and alignment in all browsers.
+ *
+ * 1. 删除 IE8/9 中默认的垂直滚动条。
+ * 2. 改善所有浏览器中的可读性并使文本垂直对齐。
+ */
+
+ textarea {
+ overflow: auto; /* 1 */
+ vertical-align: top; /* 2 */
+ }
+
+ /* ==========================================================================
+ Tables
+
+ 表格
+ ========================================================================== */
+
+ /**
+ * Remove most spacing between table cells.
+ *
+ * 删除表格里单元格间的间距。
+ */
+
+ table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ }
+
\ No newline at end of file
diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss
new file mode 100644
index 0000000..96edd24
--- /dev/null
+++ b/src/styles/sidebar.scss
@@ -0,0 +1,90 @@
+
+
+.el-menu--popup{
+ .el-menu-item{
+ background-color: #20222a;
+ i{
+ margin-right: 5px;
+ }
+ i,span{
+ color:hsla(0,0%,100%,.7);
+ }
+ &:hover{
+ i,span{
+ color:#fff;
+ }
+ }
+ &.is-active {
+ background-color: rgba(0,0,0,.8);
+ &:before {
+ content: '';
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 4px;
+ background: #409eff;
+ position: absolute;
+ }
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+
+}
+.avue-sidebar {
+ user-select: none;
+ position: relative;
+ padding-top: 60px;
+ height: 100%;
+ position: relative;
+ background-color: #20222a;
+ transition: width .2s;
+ box-sizing: border-box;
+ box-shadow: 2px 0 6px rgba(0,21,41,.35);
+ &--tip{
+ width:90%;
+ height: 140px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 5px;
+ position: absolute;
+ top:5px;
+ left:5%;
+ color:#ccc;
+ z-index: 2;
+ text-align: center;
+ font-size: 14px;
+ background-color: rgba(0,0,0,.4);
+ }
+ .el-menu-item,.el-submenu__title{
+ i{
+ margin-right: 5px;
+ }
+ i,span{
+ color:hsla(0,0%,100%,.7);
+ }
+ &:hover{
+ background: transparent;
+ i,span{
+ color:#fff;
+ }
+ }
+ &.is-active {
+ &:before {
+ content: '';
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 4px;
+ background: #409eff;
+ position: absolute;
+ }
+ background-color: rgba(0,0,0,.8);
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/tags.scss b/src/styles/tags.scss
new file mode 100644
index 0000000..283be5e
--- /dev/null
+++ b/src/styles/tags.scss
@@ -0,0 +1,92 @@
+
+
+.avue-tags {
+ user-select: none;
+ position: relative;
+ padding: 0 10px;
+ margin-bottom: 10px;
+ box-sizing: border-box;
+ overflow: hidden;
+ border-top: 1px solid #f6f6f6;
+ background-color: #fff;
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05);
+ .el-tabs--card>.el-tabs__header {
+ margin: 0;
+ }
+ .el-tabs--card>.el-tabs__header .el-tabs__nav,
+ .el-tabs--card>.el-tabs__header .el-tabs__item,
+ .el-tabs--card>.el-tabs__header {
+ border: none;
+ }
+ .el-tabs--card>.el-tabs__header .el-tabs__item:first-child {
+ border-left-width: 1px
+ }
+ .el-tabs--card>.el-tabs__header .el-tabs__item {
+ margin: 0 3px;
+ height: 40px;
+ line-height:40px;
+ font-size: 13px;
+ font-weight: normal;
+ color: #ccc;
+ &.is-active {
+ color: #409EFF;
+ border-bottom: 3px solid #409EFF;
+ }
+ }
+ .el-tabs__nav-prev,
+ .el-tabs__nav-next {
+ width: 20px;
+ line-height: 34px;
+ font-size: 18x;
+ text-align: center;
+ }
+ &__box {
+ position: relative;
+ box-sizing: border-box;
+ padding-right: 106px;
+ width: 100%;
+ &--close {
+ .el-tabs__item {
+ &:first-child {
+ padding: 0 20px !important;
+ .el-icon-close {
+ display: none;
+ }
+ }
+ }
+ }
+ }
+ &__contentmenu{
+ position: fixed;
+ width:120px;
+ background-color: #fff;
+ z-index:1024;
+ border-radius: 5px;
+ box-shadow: 1px 2px 10px #ccc;
+ .item{
+ cursor: pointer;
+ font-size: 14px;
+ padding: 8px 20px 8px 15px;
+ color: #606266;
+ &:first-child{
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ }
+ &:last-child{
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ }
+ &:hover{
+ background-color: #409EFF;
+ color:#fff;
+ }
+ }
+ }
+ &__menu {
+ position: absolute !important;
+ top: 3px;
+ right: 0;
+ padding: 1px 0 0 15px;
+ box-sizing: border-box;
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/d2.scss b/src/styles/theme/d2.scss
new file mode 100644
index 0000000..479fc0f
--- /dev/null
+++ b/src/styles/theme/d2.scss
@@ -0,0 +1,57 @@
+.theme-d2 {
+ .avue-logo{
+ color: #409EFF;
+ background-color: #ebf1f6;
+ box-shadow: none;
+ .avue-logo_title{
+ font-size: 22px;
+ font-weight: 400;
+ }
+ }
+ .avue-top{
+ background-color: #ebf1f6;
+ box-shadow: none;
+ }
+ .avue-main{
+ padding: 0 5px;
+ }
+ .avue-tags{
+ margin-left: 6px;
+ padding: 0;
+ border: 1px solid #e4e7ed;
+ border-radius: 3px;
+ background-color: #ebf1f6;
+ box-shadow: none;
+ .el-tabs__item{
+ border-left: 1px solid #cfd7e5 !important;
+ margin: 0 !important;
+ background-color: rgba(0,0,0,.03) !important;
+ color: #606266 !important;
+ font-size: 14px !important;
+ font-weight: 500 !important;
+ &:first-child{
+ border-left: none !important;
+ }
+ }
+ .is-active{
+ border-bottom:1px solid #fff !important;
+ background-color: #fff !important;
+ color: #409EFF !important;
+ }
+ }
+ .avue-sidebar{
+ background-color: #ebf1f6;
+ box-shadow: none;
+ .el-menu-item,.el-submenu__title{
+ i,span{
+ color:#606266
+ }
+ &:hover,&.is-active{
+ background: hsla(0,0%,100%,.5);
+ i,span{
+ color: #409EFF;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/hey.scss b/src/styles/theme/hey.scss
new file mode 100644
index 0000000..cc4a4fe
--- /dev/null
+++ b/src/styles/theme/hey.scss
@@ -0,0 +1,45 @@
+.theme-hey{
+ .avue-sidebar{
+ background-color: #fff;
+ box-shadow: 0 1px 4px rgba(0,21,41,.08);
+ .el-menu-item,.el-submenu__title{
+ i,span{
+ color: rgba(49,58,70,.8);
+ }
+ &:hover{
+ background: transparent;
+ i,span{
+ color:#409eff;
+ }
+ }
+ &.is-active {
+ &:before {
+ left:auto;
+ right: 0 ;
+ }
+ background-color: #f0f6ff;
+ i,span{
+ color:#409eff;
+ }
+ }
+ }
+ }
+ .avue-logo{
+ background-color: #fff;
+ box-shadow: none;
+ .avue-logo_title{
+ color:#409eff;
+ font-size: 24px;
+ }
+ }
+ .avue-tags{
+ background: #f3f6f8;
+ .el-tabs__item{
+ color: rgba(0,0,0,.65) !important;
+ }
+ .is-active{
+ background-color: #fff;
+ border-bottom: none !important;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/index.scss b/src/styles/theme/index.scss
new file mode 100644
index 0000000..4356e78
--- /dev/null
+++ b/src/styles/theme/index.scss
@@ -0,0 +1,15 @@
+// 白色主题
+@import './white.scss';
+
+// 炫酷主题
+@import './star.scss';
+
+
+// d2主题
+@import './d2.scss';
+
+//iview
+@import './iview.scss';
+
+//heyui
+@import './hey.scss';
diff --git a/src/styles/theme/iview.scss b/src/styles/theme/iview.scss
new file mode 100644
index 0000000..1c6a844
--- /dev/null
+++ b/src/styles/theme/iview.scss
@@ -0,0 +1,79 @@
+.theme-iview {
+ .avue-logo{
+ background: #001529;
+ box-shadow: none;
+ text-align: center;
+ .avue-logo_title{
+ div{
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+ font-size: 22px;
+ color:#fff;
+ font-weight: 500;
+ margin: 10px auto;
+ width: 180px;
+ height: 45px;
+ background-color: #409EFF;
+ }
+ }
+ }
+ .avue-tags{
+ padding: 3px 5px 5px 0;
+ background: #f0f0f0;
+ box-shadow: inset 0 0 3px 2px hsla(0,0%,39.2%,.1);
+ .is-active{
+ &:before{
+ background: #409EFF !important;
+ }
+ }
+ .el-tabs__item{
+ padding: 0 15px !important;
+ position: relative;
+ height: 32px !important;
+ line-height:32px !important;
+ border: 1px solid #e8eaec!important;
+ color: #515a6e!important;
+ background: #fff!important;
+ border-radius: 3px;
+ &:before{
+ content:'';
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ margin-right:10px;
+ border-radius: 50%;
+ background: #e8eaec;
+ }
+ }
+ }
+
+ .avue-sidebar{
+ background: #001529;
+ .el-menu-item{
+ &.is-active {
+ background-color: #000c17;
+ &:before {
+ display: none;
+ }
+ i,span{
+ color:#409EFF;
+ }
+ }
+ }
+ .el-submenu{
+ .el-menu-item{
+ &.is-active {
+ background-color: #409EFF;
+ &:before {
+ display: none;
+ }
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/star.scss b/src/styles/theme/star.scss
new file mode 100644
index 0000000..b593fb2
--- /dev/null
+++ b/src/styles/theme/star.scss
@@ -0,0 +1,92 @@
+.theme-star {
+ .avue-contail {
+ background-image: url("/img/bg/star-squashed.jpg");
+ background-size: 100% 100%;
+ }
+ .avue-logo{
+ color: #fff;
+ }
+ .avue-header,
+ .avue-logo,
+ .tags-container {
+ background-color: transparent;
+ }
+ .el-card,.error-page {
+ opacity: .9;
+ }
+ .avue-tabs {
+ padding: 0 20px ;
+ }
+ .avue-tags{
+ background-color: transparent;
+ border-top: none;
+ }
+ .avue-top{
+ .avue-breadcrumb{
+ color:#fff;
+ }
+ .el-menu-item{
+ i,span{
+ color:#fff;
+ }
+ &.is-active{
+ background-color: rgba(0,0,0,.4)
+ }
+ }
+ .el-dropdown{
+ color:#fff;
+ }
+ }
+ .avue-sidebar{
+ box-shadow: 2px 0 6px rgba(0, 21, 41, 0.15);
+ background-color:transparent;
+ .el-menu-item,.el-submenu__title{
+ i,span{
+ color:#fff
+ }
+ &:hover{
+ background: transparent;
+ i,span{
+ color:#409EFF;
+ }
+ }
+ &.is-active {
+ background-color: rgba(0,0,0,.4);
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+ }
+
+ .top-search {
+ .el-input__inner{
+ color: #333;
+ }
+ input::-webkit-input-placeholder,
+ textarea::-webkit-input-placeholder {
+ /* WebKit browsers */
+ color: #fff;
+ }
+ input:-moz-placeholder,
+ textarea:-moz-placeholder {
+ /* Mozilla Firefox 4 to 18 */
+ color: #fff;
+ }
+ input::-moz-placeholder,
+ textarea::-moz-placeholder {
+ /* Mozilla Firefox 19+ */
+ color: #fff;
+ }
+ input:-ms-input-placeholder,
+ textarea:-ms-input-placeholder {
+ /* Internet Explorer 10+ */
+ color: #fff;
+ }
+ }
+ .top-bar__item {
+ i {
+ color: #fff;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/theme/white.scss b/src/styles/theme/white.scss
new file mode 100644
index 0000000..ed81bc2
--- /dev/null
+++ b/src/styles/theme/white.scss
@@ -0,0 +1,120 @@
+.theme-white {
+ .el-menu--popup{
+ .el-menu-item{
+ background-color: #fff;
+ i,span{
+ color:#666;
+ }
+ &:hover{
+ i,span{
+ color:#333;
+ }
+ }
+ &.is-active {
+ background-color: #409EFF;
+ &:before {
+ content: '';
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 4px;
+ background: #409eff;
+ position: absolute;
+ }
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+ }
+ .avue-header,
+ .avue-logo,
+ .tags-container {
+ background-color: #409EFF;
+ }
+ .avue-sidebar--tip{
+ background-color:transparent;
+ color:#333;
+ }
+ .el-dropdown{
+ color:#fff;
+ }
+ .avue-logo_title{
+ font-weight: 400;
+ color:#fff;
+ }
+ .logo_title,
+ .avue-breadcrumb
+ {
+ color: #fff ;
+ i {
+ color: #fff;
+ }
+ }
+ .avue-top{
+ .el-menu-item {
+ i,
+ span {
+ color: #fff ;
+ }
+ &:hover {
+ i,
+ span {
+ color: #fff ;
+ }
+ }
+ }
+ }
+ .avue-sidebar{
+ box-shadow: 2px 0 6px rgba(0, 21, 41, 0.15);
+ background-color: #fff;
+ padding-top: 70px;
+ .el-menu-item,.el-submenu__title{
+ i,span{
+ color:#666
+ }
+ &:hover{
+ background: transparent;
+ i,span{
+ color:#333;
+ }
+ }
+ &.is-active {
+ background-color: #409EFF;
+ i,span{
+ color:#fff;
+ }
+ }
+ }
+ }
+ .top-search {
+ .el-input__inner{
+ color: #333;
+ }
+ input::-webkit-input-placeholder,
+ textarea::-webkit-input-placeholder {
+ /* WebKit browsers */
+ color: #fff;
+ }
+ input:-moz-placeholder,
+ textarea:-moz-placeholder {
+ /* Mozilla Firefox 4 to 18 */
+ color: #fff;
+ }
+ input::-moz-placeholder,
+ textarea::-moz-placeholder {
+ /* Mozilla Firefox 19+ */
+ color: #fff;
+ }
+ input:-ms-input-placeholder,
+ textarea:-ms-input-placeholder {
+ /* Internet Explorer 10+ */
+ color: #fff;
+ }
+ }
+ .top-bar__item {
+ i {
+ color: #fff;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/top.scss b/src/styles/top.scss
new file mode 100644
index 0000000..a39bb81
--- /dev/null
+++ b/src/styles/top.scss
@@ -0,0 +1,102 @@
+.avue-top {
+ padding: 0 20px;
+ position: relative;
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
+ color: rgba(0, 0, 0, .65);
+ font-size: 28px;
+ height: 64px;
+ box-sizing: border-box;
+ white-space: nowrap;
+ .el-menu-item{
+ i,span{
+ font-size: 13px;
+ }
+ }
+}
+.avue-breadcrumb {
+ height: 100%;
+ i{
+ font-size: 30px !important;
+ }
+ &--active {
+ transform:rotate(90deg);
+ }
+}
+
+.top-menu {
+ box-sizing: border-box;
+ .el-menu-item {
+ padding: 0 10px;
+ border:none !important;
+ }
+}
+
+.top-search {
+ line-height: 64px;
+ position: absolute !important;
+ left: 20px;
+ top:0;
+ width: 400px !important;
+ .el-input__inner {
+ font-size: 13px;
+ border: none;
+ background-color: transparent;
+ }
+}
+
+.top-bar__img {
+ margin: 0 8px 0 5px;
+ padding: 2px;
+ width: 30px;
+ height: 30px;
+ border-radius: 100%;
+ box-sizing: border-box;
+ border: 1px solid #eee;
+ vertical-align: middle;
+}
+
+.top-bar__left,
+.top-bar__right {
+ height: 64px;
+ position: absolute;
+ top: 0;
+ i{
+ line-height: 64px;
+ }
+}
+
+.top-bar__left {
+ left: 20px;
+}
+
+.top-bar__right {
+ right: 20px;
+ display: flex;
+ align-items: center;
+}
+
+.top-bar__item {
+ position: relative;
+ display: inline-block;
+ height: 64px;
+ margin:0 10px;
+ font-size: 16px;
+ &--show {
+ display: inline-block !important;
+ }
+ .el-badge__content.is-fixed{
+ top:12px;
+ right: 5px;
+ }
+}
+
+.top-bar__title {
+ height: 100%;
+ padding:0 40px;
+ box-sizing: border-box;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: inherit;
+ font-weight: 400;
+}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
new file mode 100644
index 0000000..219e4a7
--- /dev/null
+++ b/src/styles/variables.scss
@@ -0,0 +1,2 @@
+//main
+$mainBg: #409eff;
diff --git a/src/util/auth.js b/src/util/auth.js
new file mode 100644
index 0000000..598499f
--- /dev/null
+++ b/src/util/auth.js
@@ -0,0 +1,15 @@
+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..80ff1a1
--- /dev/null
+++ b/src/util/date.js
@@ -0,0 +1,50 @@
+export const calcDate = (date1, date2) => {
+ var date3 = date2 - date1;
+
+ var days = Math.floor(date3 / (24 * 3600 * 1000))
+
+ var leave1 = date3 % (24 * 3600 * 1000) //计算天数后剩余的毫秒数
+ var hours = Math.floor(leave1 / (3600 * 1000))
+
+ var leave2 = leave1 % (3600 * 1000) //计算小时数后剩余的毫秒数
+ var minutes = Math.floor(leave2 / (60 * 1000))
+
+ var leave3 = leave2 % (60 * 1000) //计算分钟数后剩余的毫秒数
+ var seconds = Math.round(date3 / 1000)
+ return {
+ leave1,
+ leave2,
+ leave3,
+ days: days,
+ hours: hours,
+ minutes: minutes,
+ seconds: seconds,
+ }
+}
+/**
+ * 日期格式化
+ */
+export function dateFormat(date) {
+ let format = 'yyyy-MM-dd hh:mm:ss';
+ if (date != 'Invalid Date') {
+ var 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 (var 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
new file mode 100644
index 0000000..6388ec2
--- /dev/null
+++ b/src/util/store.js
@@ -0,0 +1,121 @@
+import {
+ validatenull
+} from '@/util/validate';
+import website from '@/config/website'
+
+const keyName = website.key + '-';
+/**
+ * 存储localStorage
+ */
+export const setStore = (params = {}) => {
+ let {
+ name,
+ content,
+ type,
+ } = params;
+ name = keyName + name
+ let obj = {
+ dataType: typeof (content),
+ content: content,
+ type: type,
+ datetime: new Date().getTime()
+ }
+ if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
+ else window.localStorage.setItem(name, JSON.stringify(obj));
+}
+/**
+ * 获取localStorage
+ */
+
+export const getStore = (params = {}) => {
+ let {
+ name,
+ debug
+ } = params;
+ name = keyName + name
+ let obj = {},
+ content;
+ obj = window.sessionStorage.getItem(name);
+ if (validatenull(obj)) obj = window.localStorage.getItem(name);
+ if (validatenull(obj)) return;
+ try {
+ obj = JSON.parse(obj);
+ } catch{
+ return obj;
+ }
+ if (debug) {
+ return obj;
+ }
+ if (obj.dataType == 'string') {
+ content = obj.content;
+ } else if (obj.dataType == 'number') {
+ content = Number(obj.content);
+ } else if (obj.dataType == 'boolean') {
+ content = eval(obj.content);
+ } else if (obj.dataType == 'object') {
+ content = obj.content;
+ }
+ return content;
+}
+/**
+ * 删除localStorage
+ */
+export const removeStore = (params = {}) => {
+ let {
+ name,
+ type
+ } = params;
+ name = keyName + name
+ if (type) {
+ window.sessionStorage.removeItem(name);
+ } else {
+ window.localStorage.removeItem(name);
+ }
+
+}
+
+/**
+ * 获取全部localStorage
+ */
+export const getAllStore = (params = {}) => {
+ let list = [];
+ let {
+ type
+ } = params;
+ if (type) {
+ for (let i = 0; i <= window.sessionStorage.length; i++) {
+ list.push({
+ name: window.sessionStorage.key(i),
+ content: getStore({
+ name: window.sessionStorage.key(i),
+ type: 'session'
+ })
+ })
+ }
+ } else {
+ for (let i = 0; i <= window.localStorage.length; i++) {
+ list.push({
+ name: window.localStorage.key(i),
+ content: getStore({
+ name: window.localStorage.key(i),
+ })
+ })
+
+ }
+ }
+ return list;
+
+}
+
+/**
+ * 清空全部localStorage
+ */
+export const clearStore = (params = {}) => {
+ let { type } = params;
+ if (type) {
+ window.sessionStorage.clear();
+ } else {
+ window.localStorage.clear()
+ }
+
+}
\ No newline at end of file
diff --git a/src/util/util.js b/src/util/util.js
new file mode 100644
index 0000000..3bb9172
--- /dev/null
+++ b/src/util/util.js
@@ -0,0 +1,288 @@
+import { validatenull } from './validate'
+//表单序列化
+export const serialize = data => {
+ let list = [];
+ Object.keys(data).forEach(ele => {
+ list.push(`${ele}=${data[ele]}`)
+ })
+ return list.join('&');
+};
+export const getObjType = obj => {
+ var toString = Object.prototype.toString;
+ var 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)];
+};
+/**
+ * 对象深拷贝
+ */
+export const deepClone = data => {
+ var type = getObjType(data);
+ var obj;
+ if (type === 'array') {
+ obj = [];
+ } else if (type === 'object') {
+ obj = {};
+ } else {
+ //不再具有下一层次
+ return data;
+ }
+ if (type === 'array') {
+ for (var i = 0, len = data.length; i < len; i++) {
+ obj.push(deepClone(data[i]));
+ }
+ } else if (type === 'object') {
+ for (var key in data) {
+ obj[key] = deepClone(data[key]);
+ }
+ }
+ return obj;
+};
+/**
+ * 设置灰度模式
+ */
+export const toggleGrayMode = (status) => {
+ if (status) {
+ document.body.className = document.body.className + ' grayMode';
+ } else {
+ document.body.className = document.body.className.replace(' grayMode', '');
+ }
+};
+/**
+ * 设置主题
+ */
+export const setTheme = (name) => {
+ document.body.className = name;
+}
+
+/**
+ * 加密处理
+ */
+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;
+};
+
+
+/**
+ * 浏览器判断是否全屏
+ */
+export const fullscreenToggel = () => {
+ if (fullscreenEnable()) {
+ exitFullScreen();
+ } else {
+ reqFullScreen();
+ }
+};
+/**
+ * esc监听全屏
+ */
+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 = () => {
+ var isFullscreen = document.isFullScreen || document.mozIsFullScreen || document.webkitIsFullScreen
+ return isFullscreen;
+}
+
+/**
+ * 浏览器全屏
+ */
+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();
+ }
+};
+/**
+ * 递归寻找子类的父类
+ */
+
+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);
+ }
+ }
+ }
+ }
+ }
+};
+/**
+ * 判断2个对象属性和值是否相等
+ */
+
+/**
+ * 动态插入css
+ */
+
+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);
+};
+/**
+ * 判断路由是否相等
+ */
+export const diff = (obj1, obj2) => {
+ delete obj1.close;
+ var o1 = obj1 instanceof Object;
+ var o2 = obj2 instanceof Object;
+ if (!o1 || !o2) { /* 判断不是对象 */
+ return obj1 === obj2;
+ }
+
+ if (Object.keys(obj1).length !== Object.keys(obj2).length) {
+ return false;
+ //Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,例如:数组返回下表:let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
+ }
+
+ for (var attr in obj1) {
+ var t1 = obj1[attr] instanceof Object;
+ var 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
+ */
+export const findByvalue = (dic, value) => {
+ let result = '';
+ if (validatenull(dic)) return value;
+ if (typeof (value) == 'string' || typeof (value) == 'number' || typeof (value) == 'boolean') {
+ let index = 0;
+ index = findArray(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 = findArray(dic, ele);
+ if (index != -1) {
+ result.push(dic[index].label);
+ } else {
+ result.push(value);
+ }
+ });
+ result = result.toString();
+ }
+ return result;
+};
+/**
+ * 根据字典的value查找对应的index
+ */
+export const findArray = (dic, value) => {
+ for (let i = 0; i < dic.length; i++) {
+ if (dic[i].value == value) {
+ return i;
+ }
+ }
+ return -1;
+};
+/**
+ * 生成随机len位数字
+ */
+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 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
new file mode 100644
index 0000000..a774710
--- /dev/null
+++ b/src/util/validate.js
@@ -0,0 +1,253 @@
+/**
+ * Created by jiachenpan on 16/11/18.
+ */
+
+export function isvalidUsername(str) {
+ const valid_map = ['admin', 'editor']
+ return valid_map.indexOf(str.trim()) >= 0
+}
+
+/* 合法uri*/
+export function validateURL(textval) {
+ 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(textval)
+}
+/**
+ * 邮箱
+ * @param {*} s
+ */
+export function isEmail(s) {
+ return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s)
+}
+
+/**
+ * 手机号码
+ * @param {*} s
+ */
+export function isMobile(s) {
+ return /^1[0-9]{10}$/.test(s)
+}
+
+/**
+ * 电话号码
+ * @param {*} s
+ */
+export function isPhone(s) {
+ return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s)
+}
+
+/**
+ * URL地址
+ * @param {*} s
+ */
+export function isURL(s) {
+ return /^http[s]?:\/\/.*/.test(s)
+}
+
+/* 小写字母*/
+export function validateLowerCase(str) {
+ const reg = /^[a-z]+$/
+ return reg.test(str)
+}
+
+/* 大写字母*/
+export function validateUpperCase(str) {
+ const reg = /^[A-Z]+$/
+ return reg.test(str)
+}
+
+/* 大小写字母*/
+export function validatAlphabets(str) {
+ const reg = /^[A-Za-z]+$/
+ return reg.test(str)
+}
+/*验证pad还是pc*/
+export const vaildatePc = function() {
+ const userAgentInfo = navigator.userAgent;
+ const Agents = ["Android", "iPhone",
+ "SymbianOS", "Windows Phone",
+ "iPad", "iPod"
+ ];
+ let flag = true;
+ for (var v = 0; v < Agents.length; v++) {
+ if (userAgentInfo.indexOf(Agents[v]) > 0) {
+ flag = false;
+ break;
+ }
+ }
+ return flag;
+ }
+ /**
+ * validate email
+ * @param email
+ * @returns {boolean}
+ */
+export function validateEmail(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)
+}
+
+/**
+ * 判断身份证号码
+ */
+export function cardid(code) {
+ let list = [];
+ let result = true;
+ let msg = '';
+ var city = {
+ 11: "北京",
+ 12: "天津",
+ 13: "河北",
+ 14: "山西",
+ 15: "内蒙古",
+ 21: "辽宁",
+ 22: "吉林",
+ 23: "黑龙江 ",
+ 31: "上海",
+ 32: "江苏",
+ 33: "浙江",
+ 34: "安徽",
+ 35: "福建",
+ 36: "江西",
+ 37: "山东",
+ 41: "河南",
+ 42: "湖北 ",
+ 43: "湖南",
+ 44: "广东",
+ 45: "广西",
+ 46: "海南",
+ 50: "重庆",
+ 51: "四川",
+ 52: "贵州",
+ 53: "云南",
+ 54: "西藏 ",
+ 61: "陕西",
+ 62: "甘肃",
+ 63: "青海",
+ 64: "宁夏",
+ 65: "新疆",
+ 71: "台湾",
+ 81: "香港",
+ 82: "澳门",
+ 91: "国外 "
+ };
+ if (!validatenull(code)) {
+ if (code.length == 18) {
+ if (!code || !/(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(code)) {
+ msg = "证件号码格式错误";
+ } else if (!city[code.substr(0, 2)]) {
+ msg = "地址编码错误";
+ } else {
+ //18位身份证需要验证最后一位校验位
+ code = code.split('');
+ //∑(ai×Wi)(mod 11)
+ //加权因子
+ var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
+ //校验位
+ var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2, 'x'];
+ var sum = 0;
+ var ai = 0;
+ var wi = 0;
+ for (var i = 0; i < 17; i++) {
+ ai = code[i];
+ wi = factor[i];
+ sum += ai * wi;
+ }
+ if (parity[sum % 11] != code[17]) {
+ msg = "证件号码校验位错误";
+ } else {
+ result = false;
+ }
+
+ }
+ } else {
+ msg = "证件号码长度不为18位";
+ }
+
+ } else {
+ msg = "证件号码不能为空";
+ }
+ list.push(result);
+ list.push(msg);
+ return list;
+}
+/**
+ * 判断手机号码是否正确
+ */
+export function isvalidatemobile(phone) {
+ let list = [];
+ let result = true;
+ let msg = '';
+ var isPhone = /^0\d{2,3}-?\d{7,8}$/;
+ //增加134 减少|1349[0-9]{7},增加181,增加145,增加17[678]
+ if (!validatenull(phone)) {
+ if (phone.length == 11) {
+ if (isPhone.test(phone)) {
+ msg = '手机号码格式不正确';
+ } else {
+ result = false;
+ }
+ } else {
+ msg = '手机号码长度不为11位';
+ }
+ } else {
+ msg = '手机号码不能为空';
+ }
+ list.push(result);
+ list.push(msg);
+ return list;
+}
+/**
+ * 判断姓名是否正确
+ */
+export function validatename(name) {
+ var regName = /^[\u4e00-\u9fa5]{2,4}$/;
+ if (!regName.test(name)) return false;
+ return true;
+}
+/**
+ * 判断是否为整数
+ */
+export function validatenum(num, type) {
+ let regName = /[^\d.]/g;
+ if (type == 1) {
+ if (!regName.test(num)) return false;
+ } else if (type == 2) {
+ regName = /[^\d]/g;
+ if (!regName.test(num)) return false;
+ }
+ return true;
+}
+/**
+ * 判断是否为小数
+ */
+export function validatenumord(num, type) {
+ let regName = /[^\d.]/g;
+ if (type == 1) {
+ if (!regName.test(num)) return false;
+ } else if (type == 2) {
+ regName = /[^\d.]/g;
+ if (!regName.test(num)) return false;
+ }
+ return true;
+}
+/**
+ * 判断是否为空
+ */
+export function validatenull(val) {
+ if (typeof val == 'boolean') {
+ return false;
+ }
+ if (typeof val == 'number') {
+ return false;
+ }
+ if (val instanceof Array) {
+ if (val.length == 0) return true;
+ } else if (val instanceof Object) {
+ if (JSON.stringify(val) === '{}') return true;
+ } else {
+ if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true;
+ return false;
+ }
+ return false;
+}
\ No newline at end of file