初始化
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import './styles/index.scss'; // 全局自定义的css样式
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function login(username, password) {
|
||||
return request({
|
||||
url: '/user/login',
|
||||
method: 'post',
|
||||
data: {
|
||||
username,
|
||||
password
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo(token) {
|
||||
return request({
|
||||
url: '/user/info',
|
||||
method: 'get',
|
||||
params: { token }
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/user/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div id="wrapper">
|
||||
<img id="logo" src="~@/assets/logo.png" alt="electron-vue">
|
||||
<main>
|
||||
<div class="left-side">
|
||||
<span class="title">
|
||||
Welcome to your new project!
|
||||
</span>
|
||||
<system-information></system-information>
|
||||
</div>
|
||||
|
||||
<div class="right-side">
|
||||
<div class="doc">
|
||||
<div class="title">Getting Started</div>
|
||||
<p>
|
||||
electron-vue comes packed with detailed documentation that covers everything from
|
||||
internal configurations, using the project structure, building your application,
|
||||
and so much more.
|
||||
</p>
|
||||
<button @click="open('https://simulatedgreg.gitbooks.io/electron-vue/content/')">Read the Docs</button><br><br>
|
||||
</div>
|
||||
<div class="doc">
|
||||
<div class="title alt">Other Documentation</div>
|
||||
<button class="alt" @click="open('https://electron.atom.io/docs/')">Electron</button>
|
||||
<button class="alt" @click="open('https://vuejs.org/v2/guide/')">Vue.js</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SystemInformation from './LandingPage/SystemInformation'
|
||||
|
||||
export default {
|
||||
name: 'landing-page',
|
||||
components: { SystemInformation },
|
||||
methods: {
|
||||
open (link) {
|
||||
this.$electron.shell.openExternal(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro');
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body { font-family: 'Source Sans Pro', sans-serif; }
|
||||
|
||||
#wrapper {
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse at top left,
|
||||
rgba(255, 255, 255, 1) 40%,
|
||||
rgba(229, 229, 229, .9) 100%
|
||||
);
|
||||
height: 100vh;
|
||||
padding: 60px 80px;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#logo {
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
width: 420px;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
main > div { flex-basis: 50%; }
|
||||
|
||||
.left-side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
color: #555;
|
||||
font-size: 23px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #2c3e50;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.title.alt {
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.doc p {
|
||||
color: black;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.doc button {
|
||||
font-size: .8em;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
padding: 0.75em 2em;
|
||||
border-radius: 2em;
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
background-color: #4fc08d;
|
||||
transition: all 0.15s ease;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #4fc08d;
|
||||
}
|
||||
|
||||
.doc button.alt {
|
||||
color: #42b983;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title">Information</div>
|
||||
<div class="items">
|
||||
<div class="item">
|
||||
<div class="name">Path:</div>
|
||||
<div class="value">{{ path }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="name">Route Name:</div>
|
||||
<div class="value">{{ name }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="name">Vue.js:</div>
|
||||
<div class="value">{{ vue }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="name">Electron:</div>
|
||||
<div class="value">{{ electron }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="name">Node:</div>
|
||||
<div class="value">{{ node }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="name">Platform:</div>
|
||||
<div class="value">{{ platform }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
electron: process.versions.electron,
|
||||
name: this.$route.name,
|
||||
node: process.versions.node,
|
||||
path: this.$route.path,
|
||||
platform: require('os').platform(),
|
||||
vue: require('vue/package.json').version
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
color: #888;
|
||||
font-size: 18px;
|
||||
font-weight: initial;
|
||||
letter-spacing: .25px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.items { margin-top: 8px; }
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.item .name {
|
||||
color: #6a6a6a;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.item .value {
|
||||
color: #35495e;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click" class="international" @command="handleSetLanguage">
|
||||
<div>
|
||||
<svg-icon class-name="international-icon" icon-class="language" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :disabled="language==='zhCN'" command="zhCN">
|
||||
简体中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='en'" command="en">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language==='zhTW'" command="zhTW">
|
||||
繁体中文
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
language() {
|
||||
return this.$store.getters.language
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSetLanguage(lang) {
|
||||
this.$i18n.locale = lang
|
||||
this.$store.dispatch('app/setLanguage', lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
export default {
|
||||
route: {
|
||||
dashboard: 'Dashboard',
|
||||
documentation: 'Documentation',
|
||||
},
|
||||
navbar: {
|
||||
dashboard: 'Dashboard',
|
||||
github: 'Github',
|
||||
logOut: 'Log Out',
|
||||
profile: 'Profile',
|
||||
theme: 'Theme',
|
||||
size: 'Global Size'
|
||||
},
|
||||
login: {
|
||||
title: 'Login Form',
|
||||
logIn: 'Login',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
any: 'any',
|
||||
thirdparty: 'Or connect with',
|
||||
thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import Cookies from 'js-cookie'
|
||||
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
|
||||
import elementZhCNLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
|
||||
import elementZhTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang
|
||||
import enLocale from './en'
|
||||
import zhCNLocale from './zh-CN'
|
||||
import zhTWLocale from './zh-TW'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
...enLocale,
|
||||
...elementEnLocale
|
||||
},
|
||||
zhCN: {
|
||||
...zhCNLocale,
|
||||
...elementZhCNLocale
|
||||
},
|
||||
zhTW: {
|
||||
...zhTWLocale,
|
||||
...elementZhTWLocale
|
||||
}
|
||||
}
|
||||
export function getLanguage() {
|
||||
const chooseLanguage = Cookies.get('language')
|
||||
if (chooseLanguage) return chooseLanguage
|
||||
|
||||
// if has not choose language
|
||||
const language = (navigator.language || navigator.browserLanguage).toLowerCase()
|
||||
const locales = Object.keys(messages)
|
||||
for (const locale of locales) {
|
||||
if (language.indexOf(locale) > -1) {
|
||||
return locale
|
||||
}
|
||||
}
|
||||
return 'zhCN'
|
||||
}
|
||||
const i18n = new VueI18n({
|
||||
// set locale
|
||||
// options: en | zhCN | zhTW
|
||||
locale: getLanguage(),
|
||||
// set locale messages
|
||||
messages
|
||||
})
|
||||
|
||||
export default i18n
|
||||
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
route: {
|
||||
dashboard: '首页',
|
||||
},
|
||||
navbar: {
|
||||
dashboard: '首页',
|
||||
github: '项目地址',
|
||||
logOut: '退出登录',
|
||||
profile: '个人中心',
|
||||
theme: '换肤',
|
||||
size: '布局大小'
|
||||
},
|
||||
login: {
|
||||
title: '系统登录',
|
||||
logIn: '登录',
|
||||
username: '账号',
|
||||
password: '密码',
|
||||
any: '随便填',
|
||||
thirdparty: '第三方登录',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
route: {
|
||||
dashboard: '首页',
|
||||
},
|
||||
navbar: {
|
||||
dashboard: '首页',
|
||||
github: '项目地址',
|
||||
logOut: '退出登录',
|
||||
profile: '个人中心',
|
||||
theme: '换肤',
|
||||
size: '布局大小'
|
||||
},
|
||||
login: {
|
||||
title: '系统登录',
|
||||
logIn: '登录',
|
||||
username: '账号',
|
||||
password: '密码',
|
||||
any: '随便填',
|
||||
thirdparty: '第三方登录',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import Vue from 'vue'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
|
||||
import i18n from './lang' // internationalization
|
||||
import './permission' // permission control
|
||||
|
||||
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
|
||||
Vue.http = Vue.prototype.$http = axios
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.use(ElementUI, {
|
||||
size: Cookies.get('size') || 'medium', // set element-ui default size
|
||||
i18n: (key, value) => i18n.t(key, value)
|
||||
})
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
components: { App },
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
template: '<App/>'
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,40 @@
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import NProgress from 'nprogress' // Progress 进度条
|
||||
import 'nprogress/nprogress.css'// Progress 进度条样式
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
const whiteList = ['/login'] // 不重定向白名单
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
if (!store.getters.token) {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
|
||||
} else {
|
||||
if (store.getters.roles.length === 0) {
|
||||
store.dispatch('GetInfo').then(res => { // 拉取用户信息
|
||||
next()
|
||||
}).catch((err) => {
|
||||
store.dispatch('FedLogOut').then(() => {
|
||||
Message.error(err || 'Verification failed, please login again')
|
||||
next({ path: '/' })
|
||||
})
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next()
|
||||
} else {
|
||||
next('/login')
|
||||
NProgress.done()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done() // 结束Progress
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{ path: '/login', component: () => import('@/views/login/index') },
|
||||
{ path: '/404', component: () => import('@/views/404') },
|
||||
{ path: '/', component: () => import('@/views/dashboard/index'), name: '首页' }
|
||||
]
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
const getters = {
|
||||
language: state => state.app.language,
|
||||
size: state => state.app.size,
|
||||
device: state => state.app.device,
|
||||
token: state => state.user.token,
|
||||
avatar: state => state.user.avatar,
|
||||
name: state => state.user.name,
|
||||
roles: state => state.user.roles,
|
||||
}
|
||||
export default getters
|
||||
@@ -0,0 +1,19 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import getters from './getters'
|
||||
|
||||
import { createPersistedState, createSharedMutations } from 'vuex-electron'
|
||||
|
||||
import modules from './modules'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules,
|
||||
getters,
|
||||
plugins: [
|
||||
createPersistedState(),
|
||||
createSharedMutations()
|
||||
],
|
||||
strict: process.env.NODE_ENV !== 'production'
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
const state = {
|
||||
main: 0
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
DECREMENT_MAIN_COUNTER (state) {
|
||||
state.main--
|
||||
},
|
||||
INCREMENT_MAIN_COUNTER (state) {
|
||||
state.main++
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
someAsyncTask ({ commit }) {
|
||||
// do something async
|
||||
commit('INCREMENT_MAIN_COUNTER')
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import { getLanguage } from '@/lang/index'
|
||||
|
||||
const state = {
|
||||
device: 'desktop',
|
||||
language: getLanguage(),
|
||||
size: Cookies.get('size') || 'medium'
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
TOGGLE_DEVICE: (state, device) => {
|
||||
state.device = device
|
||||
},
|
||||
SET_LANGUAGE: (state, language) => {
|
||||
state.language = language
|
||||
Cookies.set('language', language)
|
||||
},
|
||||
SET_SIZE: (state, size) => {
|
||||
state.size = size
|
||||
Cookies.set('size', size)
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
toggleDevice({ commit }, device) {
|
||||
commit('TOGGLE_DEVICE', device)
|
||||
},
|
||||
setLanguage({ commit }, language) {
|
||||
commit('SET_LANGUAGE', language)
|
||||
},
|
||||
setSize({ commit }, size) {
|
||||
commit('SET_SIZE', size)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* The file enables `@/store/index.js` to import all vuex modules
|
||||
* in a one-shot manner. There should not be any reason to edit this file.
|
||||
*/
|
||||
|
||||
const files = require.context('.', false, /\.js$/)
|
||||
const modules = {}
|
||||
|
||||
files.keys().forEach(key => {
|
||||
if (key === './index.js') return
|
||||
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
|
||||
})
|
||||
|
||||
export default modules
|
||||
@@ -0,0 +1,87 @@
|
||||
import { login, logout, getInfo } from '@/api/login'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
token: getToken(),
|
||||
name: '',
|
||||
avatar: '',
|
||||
roles: []
|
||||
},
|
||||
|
||||
mutations: {
|
||||
SET_TOKEN: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
SET_NAME: (state, name) => {
|
||||
state.name = name
|
||||
},
|
||||
SET_AVATAR: (state, avatar) => {
|
||||
state.avatar = avatar
|
||||
},
|
||||
SET_ROLES: (state, roles) => {
|
||||
state.roles = roles
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 登录
|
||||
Login({ commit }, userInfo) {
|
||||
const username = userInfo.username.trim()
|
||||
return new Promise((resolve, reject) => {
|
||||
login(username, userInfo.password).then(response => {
|
||||
const data = response.data
|
||||
setToken(data.token)
|
||||
commit('SET_TOKEN', data.token)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo(state.token).then(response => {
|
||||
const data = response.data
|
||||
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
commit('SET_ROLES', data.roles)
|
||||
} else {
|
||||
reject('getInfo: roles must be a non-null array !')
|
||||
}
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 登出
|
||||
LogOut({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
removeToken()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 前端 登出
|
||||
FedLogOut({ commit }) {
|
||||
return new Promise(resolve => {
|
||||
removeToken()
|
||||
commit('SET_TOKEN', '')
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default user
|
||||
@@ -0,0 +1,29 @@
|
||||
//to reset element-ui default css
|
||||
.el-upload {
|
||||
input[type="file"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
|
||||
.el-dialog {
|
||||
transform: none;
|
||||
left: 0;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
//element ui upload
|
||||
.upload-container {
|
||||
.el-upload {
|
||||
width: 100%;
|
||||
.el-upload-dragger {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
@import './variables.scss';
|
||||
@import './mixin.scss';
|
||||
@import './transition.scss';
|
||||
@import './element-ui.scss';
|
||||
@import './sidebar.scss';
|
||||
|
||||
body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
div:focus{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
//main-container全局样式
|
||||
.app-main{
|
||||
min-height: 100%
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* flex布局 */
|
||||
.flex_box {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex_row_between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex_row_center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex_row_around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.flex_row_end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.flex_col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex_col_top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.flex_col_bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.flex_wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex_shrink {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* 文字溢出隐藏 */
|
||||
.text_hidden {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.text_more {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.text_three {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrollBar {
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#app {
|
||||
|
||||
// 主体区域
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
// 侧边栏
|
||||
.sidebar-container {
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
||||
}
|
||||
transition: width .28s;
|
||||
width: 180px !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.el-menu {
|
||||
border: none;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 36px !important;
|
||||
}
|
||||
.main-container {
|
||||
margin-left: 36px;
|
||||
}
|
||||
.submenu-title-noDropdown {
|
||||
padding-left: 10px !important;
|
||||
position: relative;
|
||||
.el-tooltip {
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
}
|
||||
.el-submenu {
|
||||
&>.el-submenu__title {
|
||||
padding-left: 10px !important;
|
||||
&>span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-container .nest-menu .el-submenu>.el-submenu__title,
|
||||
.sidebar-container .el-submenu .el-menu-item {
|
||||
min-width: 180px !important;
|
||||
background-color: $subMenuBg !important;
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
.el-menu--collapse .el-menu .el-submenu {
|
||||
min-width: 180px !important;
|
||||
}
|
||||
|
||||
//适配移动端
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.sidebar-container {
|
||||
top: 50px;
|
||||
transition: transform .28s;
|
||||
width: 180px !important;
|
||||
}
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-180px, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//globl transition css
|
||||
|
||||
/*fade*/
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/*fade*/
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.breadcrumb-move {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//sidebar
|
||||
$menuBg:#304156;
|
||||
$subMenuBg:#1f2d3d;
|
||||
$menuHover:#001528;
|
||||
@@ -0,0 +1,15 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const TokenKey = 'Admin-Token'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return Cookies.set(TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import i18n from '@/lang'
|
||||
|
||||
const title = 'SOONWORKERDISK'
|
||||
|
||||
export default function getPageTitle(key) {
|
||||
const hasKey = i18n.te(`route.${key}`)
|
||||
if (hasKey) {
|
||||
const pageName = i18n.t(`route.${key}`)
|
||||
return `${pageName} - ${title}`
|
||||
}
|
||||
return `${title}`
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// translate router.meta.title, be used in breadcrumb sidebar tagsview
|
||||
export function generateTitle(title) {
|
||||
const hasKey = this.$te('route.' + title)
|
||||
|
||||
if (hasKey) {
|
||||
// $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
const translatedTitle = this.$t('route.' + title)
|
||||
|
||||
return translatedTitle
|
||||
}
|
||||
return title
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* Created by PanJiaChen on 16/11/18.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse the time to string
|
||||
* @param {(Object|string|number)} time
|
||||
* @param {string} cFormat
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function parseTime(time, cFormat) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string')) {
|
||||
if ((/^[0-9]+$/.test(time))) {
|
||||
// support "1548221490638"
|
||||
time = parseInt(time)
|
||||
} else {
|
||||
// support safari
|
||||
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
|
||||
time = time.replace(new RegExp(/-/gm), '/')
|
||||
}
|
||||
}
|
||||
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
|
||||
const value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
|
||||
return value.toString().padStart(2, '0')
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {string} option
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatTime(time, option) {
|
||||
if (('' + time).length === 10) {
|
||||
time = parseInt(time) * 1000
|
||||
} else {
|
||||
time = +time
|
||||
}
|
||||
const d = new Date(time)
|
||||
const now = Date.now()
|
||||
|
||||
const diff = (now - d) / 1000
|
||||
|
||||
if (diff < 30) {
|
||||
return '刚刚'
|
||||
} else if (diff < 3600) {
|
||||
// less 1 hour
|
||||
return Math.ceil(diff / 60) + '分钟前'
|
||||
} else if (diff < 3600 * 24) {
|
||||
return Math.ceil(diff / 3600) + '小时前'
|
||||
} else if (diff < 3600 * 24 * 2) {
|
||||
return '1天前'
|
||||
}
|
||||
if (option) {
|
||||
return parseTime(time, option)
|
||||
} else {
|
||||
return (
|
||||
d.getMonth() +
|
||||
1 +
|
||||
'月' +
|
||||
d.getDate() +
|
||||
'日' +
|
||||
d.getHours() +
|
||||
'时' +
|
||||
d.getMinutes() +
|
||||
'分'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getQueryObject(url) {
|
||||
url = url == null ? window.location.href : url
|
||||
const search = url.substring(url.lastIndexOf('?') + 1)
|
||||
const obj = {}
|
||||
const reg = /([^?&=]+)=([^?&=]*)/g
|
||||
search.replace(reg, (rs, $1, $2) => {
|
||||
const name = decodeURIComponent($1)
|
||||
let val = decodeURIComponent($2)
|
||||
val = String(val)
|
||||
obj[name] = val
|
||||
return rs
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input value
|
||||
* @returns {number} output value
|
||||
*/
|
||||
export function byteLength(str) {
|
||||
// returns the byte length of an utf8 string
|
||||
let s = str.length
|
||||
for (var i = str.length - 1; i >= 0; i--) {
|
||||
const code = str.charCodeAt(i)
|
||||
if (code > 0x7f && code <= 0x7ff) s++
|
||||
else if (code > 0x7ff && code <= 0xffff) s += 2
|
||||
if (code >= 0xDC00 && code <= 0xDFFF) i--
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} actual
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function cleanArray(actual) {
|
||||
const newArray = []
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual[i]) {
|
||||
newArray.push(actual[i])
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} json
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function param(json) {
|
||||
if (!json) return ''
|
||||
return cleanArray(
|
||||
Object.keys(json).map(key => {
|
||||
if (json[key] === undefined) return ''
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
|
||||
})
|
||||
).join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function param2Obj(url) {
|
||||
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
|
||||
if (!search) {
|
||||
return {}
|
||||
}
|
||||
const obj = {}
|
||||
const searchArr = search.split('&')
|
||||
searchArr.forEach(v => {
|
||||
const index = v.indexOf('=')
|
||||
if (index !== -1) {
|
||||
const name = v.substring(0, index)
|
||||
const val = v.substring(index + 1, v.length)
|
||||
obj[name] = val
|
||||
}
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} val
|
||||
* @returns {string}
|
||||
*/
|
||||
export function html2Text(val) {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = val
|
||||
return div.textContent || div.innerText
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two objects, giving the last one precedence
|
||||
* @param {Object} target
|
||||
* @param {(Object|Array)} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function objectMerge(target, source) {
|
||||
if (typeof target !== 'object') {
|
||||
target = {}
|
||||
}
|
||||
if (Array.isArray(source)) {
|
||||
return source.slice()
|
||||
}
|
||||
Object.keys(source).forEach(property => {
|
||||
const sourceProperty = source[property]
|
||||
if (typeof sourceProperty === 'object') {
|
||||
target[property] = objectMerge(target[property], sourceProperty)
|
||||
} else {
|
||||
target[property] = sourceProperty
|
||||
}
|
||||
})
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {string} className
|
||||
*/
|
||||
export function toggleClass(element, className) {
|
||||
if (!element || !className) {
|
||||
return
|
||||
}
|
||||
let classString = element.className
|
||||
const nameIndex = classString.indexOf(className)
|
||||
if (nameIndex === -1) {
|
||||
classString += '' + className
|
||||
} else {
|
||||
classString =
|
||||
classString.substr(0, nameIndex) +
|
||||
classString.substr(nameIndex + className.length)
|
||||
}
|
||||
element.className = classString
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @returns {Date}
|
||||
*/
|
||||
export function getTime(type) {
|
||||
if (type === 'start') {
|
||||
return new Date().getTime() - 3600 * 1000 * 24 * 90
|
||||
} else {
|
||||
return new Date(new Date().toDateString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} func
|
||||
* @param {number} wait
|
||||
* @param {boolean} immediate
|
||||
* @return {*}
|
||||
*/
|
||||
export function debounce(func, wait, immediate) {
|
||||
let timeout, args, context, timestamp, result
|
||||
|
||||
const later = function() {
|
||||
// 据上一次触发时间间隔
|
||||
const last = +new Date() - timestamp
|
||||
|
||||
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
||||
if (last < wait && last > 0) {
|
||||
timeout = setTimeout(later, wait - last)
|
||||
} else {
|
||||
timeout = null
|
||||
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args)
|
||||
if (!timeout) context = args = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
context = this
|
||||
timestamp = +new Date()
|
||||
const callNow = immediate && !timeout
|
||||
// 如果延时不存在,重新设定延时
|
||||
if (!timeout) timeout = setTimeout(later, wait)
|
||||
if (callNow) {
|
||||
result = func.apply(context, args)
|
||||
context = args = null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just a simple version of deep copy
|
||||
* Has a lot of edge cases bug
|
||||
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
|
||||
* @param {Object} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function deepClone(source) {
|
||||
if (!source && typeof source !== 'object') {
|
||||
throw new Error('error arguments', 'deepClone')
|
||||
}
|
||||
const targetObj = source.constructor === Array ? [] : {}
|
||||
Object.keys(source).forEach(keys => {
|
||||
if (source[keys] && typeof source[keys] === 'object') {
|
||||
targetObj[keys] = deepClone(source[keys])
|
||||
} else {
|
||||
targetObj[keys] = source[keys]
|
||||
}
|
||||
})
|
||||
return targetObj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arr
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function uniqueArr(arr) {
|
||||
return Array.from(new Set(arr))
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function createUniqueString() {
|
||||
const timestamp = +new Date() + ''
|
||||
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
|
||||
return (+(randomNum + timestamp)).toString(32)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element has a class
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClass(ele, cls) {
|
||||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class to element
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function addClass(ele, cls) {
|
||||
if (!hasClass(ele, cls)) ele.className += ' ' + cls
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove class from element
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function removeClass(ele, cls) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
|
||||
ele.className = ele.className.replace(reg, ' ')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
*Created by PanJiaChen on 16/11/29.
|
||||
* @param {Sting} url
|
||||
* @param {Sting} title
|
||||
* @param {Number} w
|
||||
* @param {Number} h
|
||||
*/
|
||||
export default function 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import store from '@/store'
|
||||
|
||||
/**
|
||||
* @param {Array} value
|
||||
* @returns {Boolean}
|
||||
* @example see @/views/permission/directive.vue
|
||||
*/
|
||||
export default function checkPermission(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const roles = store.getters && store.getters.roles
|
||||
const permissionRoles = value
|
||||
|
||||
const hasPermission = roles.some(role => {
|
||||
return permissionRoles.includes(role)
|
||||
})
|
||||
return hasPermission
|
||||
} else {
|
||||
console.error(`need roles! Like v-permission="['admin','editor']"`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import axios from 'axios'
|
||||
import { MessageBox, Message } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
baseURL: process.env.BASE_API, // url = base url + request url
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 5000 // request timeout
|
||||
})
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// do something before request is sent
|
||||
|
||||
if (store.getters.token) {
|
||||
// let each request carry token
|
||||
// ['X-Token'] is a custom headers key
|
||||
// please modify it according to the actual situation
|
||||
config.headers['X-Token'] = getToken()
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
// do something with request error
|
||||
console.log(error) // for debug
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
/**
|
||||
* If you want to get http information such as headers or status
|
||||
* Please return response => response
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine the request status by custom code
|
||||
* Here is just an example
|
||||
* You can also judge the status by HTTP Status Code
|
||||
*/
|
||||
response => {
|
||||
const res = response.data
|
||||
|
||||
// if the custom code is not 20000, it is judged as an error.
|
||||
if (res.code !== 20000) {
|
||||
Message({
|
||||
message: res.message || 'Error',
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
|
||||
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
|
||||
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
|
||||
// to re-login
|
||||
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
|
||||
confirmButtonText: 'Re-Login',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
store.dispatch('user/resetToken').then(() => {
|
||||
location.reload()
|
||||
})
|
||||
})
|
||||
}
|
||||
return Promise.reject(new Error(res.message || 'Error'))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error) // for debug
|
||||
Message({
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
@@ -0,0 +1,58 @@
|
||||
Math.easeInOutQuad = function(t, b, c, d) {
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b
|
||||
}
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
var requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
|
||||
})()
|
||||
|
||||
/**
|
||||
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
* @param {number} amount
|
||||
*/
|
||||
function move(amount) {
|
||||
document.documentElement.scrollTop = amount
|
||||
document.body.parentNode.scrollTop = amount
|
||||
document.body.scrollTop = amount
|
||||
}
|
||||
|
||||
function position() {
|
||||
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} to
|
||||
* @param {number} duration
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function scrollTo(to, duration, callback) {
|
||||
const start = position()
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||
var animateScroll = function() {
|
||||
// increment the time
|
||||
currentTime += increment
|
||||
// find the value with the quadratic in-out easing function
|
||||
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||
// move the document.body
|
||||
move(val)
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof (callback) === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Created by PanJiaChen on 16/11/18.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUsername(str) {
|
||||
const valid_map = ['admin', 'editor']
|
||||
return valid_map.indexOf(str.trim()) >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validURL(url) {
|
||||
const reg = /^(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 reg.test(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validLowerCase(str) {
|
||||
const reg = /^[a-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUpperCase(str) {
|
||||
const reg = /^[A-Z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validAlphabets(str) {
|
||||
const reg = /^[A-Za-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validEmail(email) {
|
||||
const reg = /^(([^<>()\[\]\\.,;:\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 reg.test(email)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isString(str) {
|
||||
if (typeof str === 'string' || str instanceof String) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isArray(arg) {
|
||||
if (typeof Array.isArray === 'undefined') {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]'
|
||||
}
|
||||
return Array.isArray(arg)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<div style="background:#f0f2f5;margin-top: -20px;">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" :src="img_404" alt="404">
|
||||
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
|
||||
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
|
||||
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__info">版权所有<a class="link-type" href="https://wallstreetcn.com" target='_blank'>华尔街见闻</a></div>
|
||||
<div class="bullshit__headline">{{ message }}</div>
|
||||
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
|
||||
<a href="/" class="bullshit__return-home">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import img_404 from '@/assets/404_images/404.png'
|
||||
import img_404_cloud from '@/assets/404_images/404_cloud.png'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
img_404,
|
||||
img_404_cloud
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
message() {
|
||||
return '特朗普说这个页面你不能进......'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
margin: 20px auto 60px;
|
||||
padding: 0 100px;
|
||||
overflow: hidden;
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
padding: 150px 0;
|
||||
overflow: hidden;
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
&__child {
|
||||
position: absolute;
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 150px 0;
|
||||
overflow: hidden;
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,662 @@
|
||||
<template>
|
||||
<div class="dashboard-container">
|
||||
<img class="login_bg" src="@/assets/images/index.png" />
|
||||
<div style="height: 100%; position: relative; display: none;">
|
||||
<div
|
||||
v-if="beginStep && parseInt(currentStep) >= 0 && parseInt(currentStep) < 8"
|
||||
class="bg_box"
|
||||
style="position: absolute; top: 0; left: 0; z-index: 10; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5)"
|
||||
></div>
|
||||
<div style="height: 100%; width: 100%; display: flex">
|
||||
<div :span="3" style="height: 100%; width: 164px">
|
||||
<div class="grid-content bg-purple left">
|
||||
<div class="title">
|
||||
{{ $t('index.wordSpace') }}
|
||||
</div>
|
||||
<!-- @click="() => (printData = index)" -->
|
||||
<div class="card" v-for="item of infoData" :key="item.PrinterID" style="background-color: #f5f5f5; text-align: left">
|
||||
<span style="font-size: 18px; margin-left: 40px">{{ getPrintName(item.PrinterID, true) }}</span>
|
||||
<span style="font-size: 9px">{{ item.PrinterType == '-1' ? $t('index.workspacenodata') : item.PrinterType }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :span="18" style="height: 100%; width: calc(100% - 328px)">
|
||||
<div class="grid-content bg-purple-light mid" id="rte" style="position: relative">
|
||||
<div class="title" style="position: relative; height: 45px; display: flex; justify-content: space-between">
|
||||
<el-popover v-if="guideStep" :placement="guideStep[0].placement" width="250" trigger="manual" v-model="guideStep[0].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step1`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ parseInt(currentStep) == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" :class="{ guide_body: beginStep && currentStep == 0 }" class="switch" style="position: relative; left: 0; top: 3px">
|
||||
<el-switch active-color="#13ce66" inactive-color="#ff4949" @change="handleOpenSwitch" :value="state"> </el-switch>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[1].placement" width="250" trigger="manual" v-model="guideStep[1].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step2`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" :class="{ guide_body: beginStep && currentStep == 1 }" class="information" style="font-size: 15px; display: flex; gap: 25px; position: relative; top: 5px">
|
||||
<div style="display: inline-block">
|
||||
<div style="text-decoration: underline">
|
||||
{{ $t('main.doneCopyNum') }}
|
||||
</div>
|
||||
<div>{{ doneCopyNum }}</div>
|
||||
</div>
|
||||
<div style="display: inline-block">
|
||||
<div style="text-decoration: underline">
|
||||
{{ $t('main.cancelCopyNum') }}
|
||||
</div>
|
||||
<div>{{ cancelCopyNum }}</div>
|
||||
</div>
|
||||
<div style="display: inline-block">
|
||||
<div style="text-decoration: underline">
|
||||
{{ $t('main.failNum') }}
|
||||
</div>
|
||||
<div>{{ failNum }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[2].placement" width="250" trigger="manual" v-model="guideStep[2].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step3`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center" class="add" slot="reference" :class="{ guide_body: beginStep && currentStep == 2 }">
|
||||
<span style="font-size: 18px; line-height: 45px">{{ $t('main.submitTask') }}</span>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[7].placement" width="250" trigger="manual" v-model="guideStep[7].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step8`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-right: 10px" slot="reference" :class="{ guide_body: beginStep && currentStep == 7 }">
|
||||
<el-button type="primary" @click="newWork" :disabled="infoData.length == 0 && state == false">
|
||||
{{ $t('index.newWork') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary" @click="newNetWork" v-if="false">
|
||||
{{ $t('index.newWork') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="openWork()" v-if="true" :disabled="infoData.length == 0 && state == false">
|
||||
{{ $t('admin.openWork') }}
|
||||
</el-button>
|
||||
<!--
|
||||
<el-select v-model="selectLan">
|
||||
<el-option
|
||||
v-for="item in languages"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option> </el-select
|
||||
>
|
||||
<el-button icon="el-icon-video-play" @click="start_job">启动任务</el-button>
|
||||
<el-button icon="el-icon-video-pause" @click="suspend_job">暂停任务</el-button>
|
||||
<el-button icon="el-icon-circle-close">停止任务</el-button>-->
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="user">
|
||||
<span style="margin-right: 20px">{{ $t('admin.hello') }} {{ user }}</span>
|
||||
<router-link to="/admin" v-if="isAdmin">{{ $t('main.adminMange') }}</router-link>
|
||||
<router-link to="/">{{ $t('main.out') }}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[4].placement" width="250" trigger="manual" v-model="guideStep[4].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step5`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="brief" slot="reference" :class="{ guide_body: beginStep && currentStep == 4 }">
|
||||
<el-row :gutter="20" style="height: 100%">
|
||||
<el-col :span="12" style="height: 100%">
|
||||
<div class="grid-content bg-purple b_left" style="padding-top: 1px !important; display: flex; align-items: center">
|
||||
<!-- <div class="b_title">{{ $t("main.systemInformation") }}</div> -->
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center">
|
||||
<img v-if="os.CardsoonModel" :src="os.CardsoonModel.indexOf(`TH80N`) != -1 ? N80N : N800N" width="150" height="95" />
|
||||
<span style="font-weight: bold; font-size: 18px">{{ $t('main.systemInformation') }}</span>
|
||||
</div>
|
||||
<div style="margin-left: 20px">
|
||||
<div class="item">{{ $t('main.deviceType') }}{{ os.CardsoonModel }}</div>
|
||||
<div class="item">{{ $t('main.serialNumber') }}{{ os.SystemSn }}</div>
|
||||
<div class="item">{{ $t('main.hostName') }}{{ os.hostname }}</div>
|
||||
<div class="item">{{ $t('main.hostIp') }}{{ os.ip }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12" style="height: 100%">
|
||||
<div class="grid-content bg-purple b_mid" style="padding-top: 1px !important; display: flex; align-items: center">
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center">
|
||||
<span style="font-weight: bold; font-size: 18px">{{ $t('main.hardwareConfiguration') }}</span>
|
||||
</div>
|
||||
<div style="margin-left: 20px">
|
||||
<!-- <div class="item">
|
||||
{{ $t("main.hardwareConfiguration") }}
|
||||
</div> -->
|
||||
<div class="item">{{ $t('main.Cpu') }}{{ os.cpu }}</div>
|
||||
<div class="item">{{ $t('main.Memory') }}{{ os.ram }}</div>
|
||||
<div class="item">{{ $t('main.hardDrive') }}{{ os.HD }}</div>
|
||||
<div class="item">
|
||||
{{ $t('main.cacheSpace') }}:{{ os.remainCacheSpace }} /
|
||||
{{ os.totalCacheSpace }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-popover>
|
||||
<!-- <div
|
||||
class="brief"
|
||||
v-if="infoData.length != 0 && false"
|
||||
style="margin-top: 20px; height: 200px"
|
||||
>
|
||||
<el-row :gutter="10" style="height: 100%">
|
||||
<el-col :span="6" style="height: 100%">
|
||||
<div
|
||||
class="grid-content bg-purple b_left"
|
||||
style="padding-top: 1px !important"
|
||||
>
|
||||
<div class="b_title">
|
||||
{{ $t("index.systemState") }}
|
||||
</div>
|
||||
<div style="margin-top: 30px">
|
||||
<div class="item">
|
||||
{{ $t("index.wordSpaceOnline")
|
||||
}}{{
|
||||
printerStatus[infoData[printData].PrinterID]
|
||||
? $t(
|
||||
`index.${
|
||||
printerStatus[infoData[printData].PrinterID]
|
||||
}`
|
||||
)
|
||||
: ""
|
||||
}}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{ $t("index.equipModel")
|
||||
}}{{ infoData[printData].ModeName.replace("Fagoo", "") }}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{ $t("index.series") }}{{ infoData[printData].SerialNo }}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{ $t("index.machineVers")
|
||||
}}{{ infoData[printData].FirewareVersion }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="5" style="height: 100%">
|
||||
<div
|
||||
class="grid-content bg-purple b_left"
|
||||
style="padding-top: 1px !important; border-right: 0"
|
||||
>
|
||||
<div class="b_title"></div>
|
||||
<div style="margin-top: 30px">
|
||||
<div class="item">
|
||||
{{ $t("index.numbers") }}{{ infoData[printData].CardNum }}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{ $t("index.dice")
|
||||
}}{{
|
||||
infoData[printData].DualSide == 1
|
||||
? this.$t("index.twoDice")
|
||||
: this.infoData[printData] != null
|
||||
? this.$t("index.sigleDice")
|
||||
: ""
|
||||
}}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{ $t("index.ribbonType")
|
||||
}}{{ infoData[printData].RibbonType }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="5" style="height: 100%">
|
||||
<div
|
||||
class="grid-content bg-purple b_mid"
|
||||
style="
|
||||
padding-top: 1px !important;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<el-progress
|
||||
type="circle"
|
||||
:format="ribbonFormat"
|
||||
:key="printData"
|
||||
width="75"
|
||||
:percentage="
|
||||
infoData[printData].RibbonAmount == undefined ||
|
||||
infoData[printData].RibbonAmount == '0'
|
||||
? 0
|
||||
: (
|
||||
(infoData[printData].RibbonAmount /
|
||||
getRibbonAmount) *
|
||||
100
|
||||
).toFixed(0)
|
||||
"
|
||||
:color="'#67C23A'"
|
||||
></el-progress>
|
||||
|
||||
<div style="margin-left: 20px">
|
||||
<div>{{ $t("index.ribbonNumber") }}</div>
|
||||
<div>
|
||||
{{ infoData[printData].RibbonAmount }}/{{
|
||||
getRibbonAmount
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" style="height: 100%">
|
||||
<div
|
||||
class="grid-content bg-purple b_mid"
|
||||
style="
|
||||
padding-top: 1px !important;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<el-progress
|
||||
type="circle"
|
||||
:key="printData"
|
||||
:format="remaCapaFormat"
|
||||
width="75"
|
||||
:percentage="
|
||||
infoData[printData].PrinterRemaCapa == undefined ||
|
||||
infoData[printData].PrinterRemaCapa == '0'
|
||||
? 0
|
||||
: (
|
||||
(infoData[printData].PrinterRemaCapa /
|
||||
getPrinterRemaCapaAmount) *
|
||||
100
|
||||
).toFixed(0)
|
||||
"
|
||||
:color="'#67C23A'"
|
||||
></el-progress>
|
||||
<div style="margin-left: 20px">
|
||||
<div>
|
||||
{{ $t("index.inCardState") }}
|
||||
</div>
|
||||
<div>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="() => printerTypeChange('auto')"
|
||||
>{{ $t("index.autoRefresh") }}</el-button
|
||||
>
|
||||
<el-dropdown @command="handleTypeCommand">
|
||||
<el-button type="primary" size="mini"
|
||||
>{{ $t("index.refreshMore")
|
||||
}}<i class="el-icon-arrow-down el-icon--right"></i
|
||||
></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="item in _PrinterTypeList"
|
||||
:command="item.value"
|
||||
>{{ item.label }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-button-group>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div> -->
|
||||
<el-popover v-if="guideStep" :placement="guideStep[5].placement" width="250" trigger="manual" v-model="guideStep[5].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step6`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" :class="{ guide_body: beginStep && currentStep == 5 }" class="machines work" style="height: 130px; margin-top: 10px">
|
||||
<el-table :data="infoData" max-height="180" size="mini" style="width: 100%" :empty-text="$t('index.nodata')" row-class-name="rowclass">
|
||||
<el-table-column prop="gzz" :label="$t('main.tableMachine')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ getPrintName(scope.row.PrinterID, true) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('main.tableState')" min-width="80">
|
||||
<template slot-scope="scope">{{ $t('main.tableOnline') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="SerialNo" :label="$t('main.tableSerialNo')" min-width="80"> </el-table-column>
|
||||
<el-table-column prop="RibbonType" :label="$t('main.tableRibbonType')" min-width="80"> </el-table-column>
|
||||
<el-table-column prop="RibbonAmount" :label="$t('main.tableRibbonAmount')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-progress
|
||||
:color="customColors"
|
||||
:width="45"
|
||||
define-back-color="#ebeef5"
|
||||
:format="() => scope.row.RibbonAmount"
|
||||
:stroke-width="3"
|
||||
:percentage="
|
||||
scope.row.RibbonAmount == undefined || scope.row.RibbonAmount == '0'
|
||||
? 0
|
||||
: parseFloat(((scope.row.RibbonAmount / getRibbonAmount(scope.row.ModeName, scope.row.RibbonType)) * 100).toFixed(0))
|
||||
"
|
||||
></el-progress>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="PrinterType" :label="$t('main.tablePrinterType')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown trigger="click" @command="(e) => handleMachineCommand(e, scope.row.PrinterID)">
|
||||
<span class="el-dropdown-link" style="color: #000; font-size: 12px"> {{ scope.row.PrinterType }}<i class="el-icon-arrow-down el-icon--right"></i> </span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="auto">
|
||||
<span style="color: #000; font-size: 12px">{{ $t('main.autoSetting') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="null">
|
||||
<span style="color: #000; font-size: 12px">{{ $t('main.cancelSetting') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in _PrinterTypeList" :key="item.value" :command="item.value">
|
||||
<span style="color: #000; font-size: 12px">{{ item.label }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="PrinterRemaCapa" :label="$t('main.tablePrinterRemaCapa')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-progress
|
||||
:color="customColors"
|
||||
:width="45"
|
||||
define-back-color="#ebeef5"
|
||||
:format="() => (scope.row.PrinterRemaCapa > 0 ? scope.row.PrinterRemaCapa : 0)"
|
||||
:stroke-width="3"
|
||||
:percentage="
|
||||
scope.row.PrinterRemaCapa == undefined || scope.row.PrinterRemaCapa == '0'
|
||||
? 0
|
||||
: parseFloat((((scope.row.PrinterRemaCapa > 0 ? scope.row.PrinterRemaCapa : 0) / getPrinterRemaCapaAmount(scope.row.ModeName)) * 100).toFixed(0))
|
||||
"
|
||||
></el-progress>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[6].placement" width="250" trigger="manual" v-model="guideStep[6].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step7`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" :class="{ guide_body: beginStep && currentStep == 6 }">
|
||||
<el-tabs v-model="activeName" style="background-color: #fff; padding-bottom: 20px; max-height: 240px; margin-top: 10px 20px">
|
||||
<el-tab-pane :label="$t(`index.workList`)" name="first">
|
||||
<div class="work">
|
||||
<el-table :data="tasks" max-height="180" style="width: 100%" size="mini" :empty-text="$t('index.nodata')" row-class-name="rowclass">
|
||||
<el-table-column label="#" min-width="38">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="jId" :label="$t('index.wordID')" min-width="130"> </el-table-column>
|
||||
<el-table-column prop="DataSource" :label="$t('index.wordOrigin')" min-width="85"> </el-table-column>
|
||||
<el-table-column :label="$t('index.wordSpace')" min-width="60">
|
||||
<template slot-scope="scope">
|
||||
{{ getPrintName(scope.row.PrinterID, true) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('main.USBType')" min-width="90">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.PrinterType }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="80" prop="TaskCapacity" :label="$t('main.TaskCapacity')"> </el-table-column>
|
||||
<el-table-column v-if="false" prop="DataDir" :label="$t('main.taskFile')"> </el-table-column>
|
||||
<el-table-column prop="CreateTime" :label="$t('index.createTime')" min-width="120"> </el-table-column>
|
||||
<el-table-column
|
||||
v-if="false"
|
||||
prop="usb_version"
|
||||
:label="$t('main.USBVersion')"
|
||||
:filters="[
|
||||
{ text: '2.0', value: 2 },
|
||||
{ text: '3.0', value: 3 }
|
||||
]"
|
||||
:filter-method="filterHandler"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column prop="FinishTime" :label="$t('index.finishedTime')" min-width="120"> </el-table-column>
|
||||
<el-table-column prop="JobCompletion" min-width="60" :label="$t('index.finishedNumber')"> </el-table-column>
|
||||
<el-table-column :label="$t('index.state')" prop="JobStatus" width="110">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown trigger="click" @command="(e) => handleTaskCommand(e, scope.row.JobID)" :disabled="scope.row.JobStatus != 'Fail'">
|
||||
<span class="el-dropdown-link" style="color: #000; font-size: 12px">
|
||||
{{ jobState(scope.row.JobStatus) }}
|
||||
<i class="el-icon-arrow-down el-icon--right" v-if="scope.row.JobStatus == 'Fail'"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="refresh">
|
||||
<span style="color: #000; font-size: 12px">{{ $t('main.commandRefresh') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="delete">
|
||||
<span style="color: #000; font-size: 12px">{{ $t('main.commandDelete') }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-progress
|
||||
:color="customColors"
|
||||
:width="45"
|
||||
define-back-color="#ebeef5"
|
||||
:stroke-width="3"
|
||||
:percentage="parseFloat(scope.row.TaskPercentage.replace(`%`, ``))"
|
||||
v-if="scope.row.JobStatus == `Copying`"
|
||||
></el-progress>
|
||||
<!-- <el-button
|
||||
style="margin-left: 5px; height: 30px; width: 30px"
|
||||
icon="el-icon-refresh"
|
||||
circle
|
||||
v-if="scope.row.JobStatus == 'Fail'"
|
||||
@click="restart_job(scope.$index)"
|
||||
type="primary"
|
||||
size="mini"
|
||||
>
|
||||
</el-button>
|
||||
<el-button
|
||||
style="margin-left: 5px; height: 30px; width: 30px"
|
||||
icon="el-icon-delete"
|
||||
circle
|
||||
v-if="scope.row.JobStatus == 'Fail'"
|
||||
@click="remove_job(scope.$index)"
|
||||
type="danger"
|
||||
size="mini"
|
||||
>
|
||||
</el-button>
|
||||
<el-button
|
||||
style="margin-left: 5px; height: 30px; width: 30px"
|
||||
icon="el-icon-video-pause"
|
||||
circle
|
||||
v-else-if="scope.row.JobStatus == 'Printing'"
|
||||
@click="suspend_job(scope.$index)"
|
||||
type="primary"
|
||||
size="mini"
|
||||
>
|
||||
</el-button>
|
||||
<el-button
|
||||
style="margin-left: 5px; height: 30px; width: 30px"
|
||||
icon="el-icon-video-play"
|
||||
circle
|
||||
v-else-if="scope.row.JobStatus == '暂停'"
|
||||
@click="start_job(scope.$index)"
|
||||
type="primary"
|
||||
>
|
||||
</el-button> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!--<el-table-column prop="process" label="进度">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="info"
|
||||
label="信息">
|
||||
</el-table-column>-->
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t(`index.curLog`)" name="second">
|
||||
<div class="work">
|
||||
<div class="w_title">
|
||||
<div class="w_right">
|
||||
<el-button type="text" icon="el-icon-document-copy" v-if="isAdmin" @click="getWLogData()">
|
||||
{{ $t('index.openHistoryLog') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c_log" v-html="log"></div></div
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-popover>
|
||||
<s-footer></s-footer>
|
||||
</div>
|
||||
</div>
|
||||
<div :span="3" style="height: 100%; width: 164px">
|
||||
<div class="grid-content bg-purple right" style="overflow: hidden">
|
||||
<el-popover v-if="guideStep" :placement="guideStep[3].placement" width="250" trigger="manual" v-model="guideStep[3].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">
|
||||
{{ $t('guide.title') }}<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ $t(`guide.step4`) }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">{{ $t('guide.skip') }}</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">{{ $t('guide.prev') }}</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title" slot="reference" :class="{ guide_body: beginStep && currentStep == 3 }">
|
||||
{{ $t('index.notice') }}
|
||||
<!--<div class="log" v-for="(item, index) of infoData.sysLogs">
|
||||
{{ item }}
|
||||
</div>-->
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<warnCard v-for="item of errList" :data="item" @ok="okWarning" v-if="item.show" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dialogVisible">
|
||||
<el-dialog :visible.sync="dialogVisible" width="1200px" :destroy-on-close="true" :close-on-click-modal="false" :title="$t('index.newWork')">
|
||||
<work
|
||||
:dice="DualSide == 1 ? 2 : 1"
|
||||
@jobPost="closeWork"
|
||||
@addError="addError"
|
||||
:isNew="isNew"
|
||||
:saveWorkList="saveWorkList"
|
||||
:printerList="infoData"
|
||||
:shareDisk="shareDisk"
|
||||
:isCopy="isLocal"
|
||||
:filterPassedType="filterPassedType"
|
||||
:sizeType="_PrinterTypeList"
|
||||
>
|
||||
</work>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div v-if="dialogNetVisible">
|
||||
<el-dialog :visible.sync="dialogNetVisible" width="1200px" :destroy-on-close="true" :close-on-click-modal="false" :title="$t('index.newWork')">
|
||||
<worknet :dice="DualSide == 1 ? 2 : 1" @jobPost="closeNetWork" :printerList="infoData"> </worknet>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<el-dialog :title="$t('index.entireLog')" :visible.sync="dialogWholeLog">
|
||||
<div class="wLog" v-html="wlog" style="text-align: left"></div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 开始用户引导 -->
|
||||
<el-dialog class="guide_dialog" top="30vh" :show-close="false" title="温馨提示" :visible.sync="showStep" width="350px" :close-on-click-modal="false">
|
||||
<div class="start_box">
|
||||
<div class="start_content">欢迎使用卡树自动拷贝打印系统,我们将引导您使用本系统,请您耐心接受引导</div>
|
||||
<div class="start_hide">
|
||||
<el-checkbox v-model="hideStep">我已熟悉系统,不再显示引导</el-checkbox>
|
||||
</div>
|
||||
<div class="start_btn">
|
||||
<el-button @click="startStep" type="primary">开始引导</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'dashboard',
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles'])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.dashboard-container {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.login_bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<img class="login_bg" src="@/assets/images/login_bg.png" />
|
||||
<div class="flex_box form_box">
|
||||
<el-form class="flex_box flex_col flex_row_center login-form" autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left">
|
||||
<img class="login_logo" src="@/assets/images/logo.png" />
|
||||
<div class="login_box">
|
||||
<div class="login_title">用户登录</div>
|
||||
<div class="login_label">用户名</div>
|
||||
<el-form-item prop="username">
|
||||
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on" placeholder="username" />
|
||||
</el-form-item>
|
||||
<div class="login_label">密码</div>
|
||||
<el-form-item prop="password">
|
||||
<el-input name="password" :type="pwdType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on"
|
||||
placeholder="password"></el-input>
|
||||
<span class="show-pwd" @click="showPwd">
|
||||
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="login_check">
|
||||
<el-checkbox v-model="isChecked">记住账号密码</el-checkbox>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="login_btn" type="primary" :loading="loading" @click.native.prevent="handleLogin">{{ $t('login.logIn') }}</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { validUsername } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'login',
|
||||
data() {
|
||||
const validateUsername = (rule, value, callback) => {
|
||||
if (!validUsername(value)) {
|
||||
callback(new Error('请输入正确的用户名'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入您的密码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
loginForm: {
|
||||
username: 'admin',
|
||||
password: 'admin'
|
||||
},
|
||||
loginRules: {
|
||||
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
|
||||
password: [{ required: true, trigger: 'blur', validator: validatePass }]
|
||||
},
|
||||
isChecked: true,
|
||||
loading: false,
|
||||
pwdType: 'password'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showPwd() {
|
||||
if (this.pwdType === 'password') {
|
||||
this.pwdType = ''
|
||||
} else {
|
||||
this.pwdType = 'password'
|
||||
}
|
||||
},
|
||||
handleLogin() {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
this.$store.dispatch('Login', this.loginForm).then(() => {
|
||||
this.loading = false
|
||||
this.$router.push({ path: '/' })
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
$bg:#F1F1F1;
|
||||
$light_gray:#000000;
|
||||
|
||||
/* reset element-ui css */
|
||||
.login-container {
|
||||
.el-input {
|
||||
display: inline-block;
|
||||
height: 60px;
|
||||
border-radius: 30px;
|
||||
width: 100%;
|
||||
background: #F1F1F1;
|
||||
border-radius: 30px;
|
||||
color: #000;
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
-webkit-appearance: none;
|
||||
padding: 12px 5px 12px 15px;
|
||||
color: $light_gray;
|
||||
height: 60px;
|
||||
font-size: 24px;
|
||||
border-radius: 30px;
|
||||
&:-webkit-autofill {
|
||||
border-radius: 30px;
|
||||
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
|
||||
-webkit-text-fill-color: #000 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
$bg:#F1F1F1;
|
||||
$dark_gray:#889aa4;
|
||||
$light_gray:#eee;
|
||||
.login-container {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.login_bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.form_box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 0 0 120px;
|
||||
}
|
||||
.login-form {
|
||||
height: 100%;
|
||||
.login_logo {
|
||||
width: 844px;
|
||||
height: 158px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
.login_box {
|
||||
width: 450px;
|
||||
background-color: #fff;
|
||||
border-radius: 24px;
|
||||
padding: 48px;
|
||||
.login_title {
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
color: #00C325;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
.login_label {
|
||||
font-size: 24px;
|
||||
color: #B2B2B2;
|
||||
line-height: 36px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.login_check {
|
||||
::v-deep {
|
||||
.el-checkbox__inner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: #00C325;
|
||||
border-color: #00C325;
|
||||
}
|
||||
.el-checkbox__inner::after {
|
||||
width: 6px;
|
||||
height: 14px;
|
||||
left: 8px;
|
||||
}
|
||||
.el-checkbox__label {
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.el-checkbox__input.is-checked+.el-checkbox__label {
|
||||
color: #00C325;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login_btn {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background: #F1F1F1;
|
||||
border-radius: 33px;
|
||||
border: 3px solid #00BCC3;
|
||||
font-size: 24px;
|
||||
color: #00BCC3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tips {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
span {
|
||||
&:first-of-type {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.svg-container {
|
||||
padding: 6px 5px 6px 15px;
|
||||
color: $dark_gray;
|
||||
vertical-align: middle;
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
&_login {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
color: $light_gray;
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.show-pwd {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 7px;
|
||||
font-size: 16px;
|
||||
color: $dark_gray;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user