mirror of https://github.com/halo-dev/halo-admin
parent
121776da41
commit
2cfe229790
|
@ -2601,6 +2601,34 @@
|
|||
"webpack-sources": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.0.0-beta.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.5.tgz",
|
||||
"integrity": "sha512-ciWfzNefqWlmzKznCWY9hl+fPP4KlQ0A9MtHbJ/8DpyY+dAM8gDrjufIdxwTgC4szE4EZC3A6ip/BbrqM84GqA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||
"chalk": "^3.0.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^1.2.3",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
|
@ -16105,86 +16133,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.0.0-beta.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.5.tgz",
|
||||
"integrity": "sha512-ciWfzNefqWlmzKznCWY9hl+fPP4KlQ0A9MtHbJ/8DpyY+dAM8gDrjufIdxwTgC4szE4EZC3A6ip/BbrqM84GqA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||
"chalk": "^3.0.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^1.2.3",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-ls": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-ls/-/vue-ls-3.2.1.tgz",
|
||||
|
|
26
package.json
26
package.json
|
@ -1,6 +1,17 @@
|
|||
{
|
||||
"name": "halo-admin",
|
||||
"version": "1.4.0-beta.2",
|
||||
"author": "halo-dev",
|
||||
"description": "Halo admin client.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/halo-dev/halo-admin.git"
|
||||
},
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/halo-dev/halo-admin/issues"
|
||||
},
|
||||
"homepage": "https://github.com/halo-dev/halo-admin#readme",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
|
@ -137,16 +148,5 @@
|
|||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 10"
|
||||
],
|
||||
"description": "Halo admin client.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/halo-dev/halo-admin.git"
|
||||
},
|
||||
"author": "halo-dev",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/halo-dev/halo-admin/issues"
|
||||
},
|
||||
"homepage": "https://github.com/halo-dev/halo-admin#readme"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -150,7 +150,7 @@ export default {
|
|||
this.queryParam.sort = this.pagination.sort
|
||||
attachmentApi
|
||||
.query(this.queryParam)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.attachments = response.data.data.content
|
||||
this.pagination.total = response.data.data.total
|
||||
})
|
||||
|
|
|
@ -47,7 +47,7 @@ export default {
|
|||
handleAttachmentUpload(pos, $file) {
|
||||
var formdata = new FormData()
|
||||
formdata.append('file', $file)
|
||||
attachmentApi.upload(formdata).then(response => {
|
||||
attachmentApi.upload(formdata).then((response) => {
|
||||
var responseObject = response.data
|
||||
var HaloEditor = this.$refs.md
|
||||
HaloEditor.$img2Url(pos, encodeURI(responseObject.data.path))
|
||||
|
|
|
@ -29,27 +29,23 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
getStrDom(str, fullLength) {
|
||||
return (
|
||||
<span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span>
|
||||
)
|
||||
return <span>{cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '')}</span>
|
||||
},
|
||||
getTooltip(fullStr, fullLength) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<template slot="title">{ fullStr }</template>
|
||||
{ this.getStrDom(fullStr, fullLength) }
|
||||
<template slot="title">{fullStr}</template>
|
||||
{this.getStrDom(fullStr, fullLength)}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { tooltip, length } = this.$props
|
||||
const str = this.$slots.default.map(vNode => vNode.text).join('')
|
||||
const str = this.$slots.default.map((vNode) => vNode.text).join('')
|
||||
const fullLength = getStrFullLength(str)
|
||||
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
|
||||
return (
|
||||
strDom
|
||||
)
|
||||
return strDom
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import "../index";
|
||||
@import '../index';
|
||||
|
||||
@footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar";
|
||||
@footer-toolbar-prefix-cls: ~'@{ant-pro-prefix}-footer-toolbar';
|
||||
|
||||
.@{footer-toolbar-prefix-cls} {
|
||||
position: fixed;
|
||||
|
@ -16,8 +16,8 @@
|
|||
z-index: 1000;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-form-model
|
||||
ref="loginForm"
|
||||
:model="form.model"
|
||||
:rules="form.rules"
|
||||
layout="vertical"
|
||||
@keyup.enter.native="handleLogin"
|
||||
>
|
||||
<a-form-model-item
|
||||
v-if="!form.needAuthCode"
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.1s'}"
|
||||
prop="username"
|
||||
>
|
||||
<a-input
|
||||
placeholder="用户名/邮箱"
|
||||
v-model="form.model.username"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="user"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="!form.needAuthCode"
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.2s'}"
|
||||
prop="password"
|
||||
>
|
||||
<a-input
|
||||
v-model="form.model.password"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="lock"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="form.needAuthCode"
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.1s'}"
|
||||
prop="authcode"
|
||||
>
|
||||
<a-input
|
||||
placeholder="两步验证码"
|
||||
v-model="form.model.authcode"
|
||||
:maxLength="6"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="safety-certificate"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.3s'}"
|
||||
>
|
||||
<a-button
|
||||
:loading="form.logging"
|
||||
type="primary"
|
||||
:block="true"
|
||||
@click="handleLoginClick"
|
||||
>{{ buttonName }}</a-button>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import adminApi from '@/api/admin'
|
||||
import { mapActions } from 'vuex'
|
||||
export default {
|
||||
name: 'LoginForm',
|
||||
data() {
|
||||
const authcodeValidate = (rule, value, callback) => {
|
||||
if (!value && this.form.needAuthCode) {
|
||||
callback(new Error('* 请输入两步验证码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
form: {
|
||||
model: {
|
||||
authcode: null,
|
||||
password: null,
|
||||
username: null
|
||||
},
|
||||
rules: {
|
||||
username: [{ required: true, message: '* 用户名/邮箱不能为空', trigger: ['change'] }],
|
||||
password: [{ required: true, message: '* 密码不能为空', trigger: ['change'] }],
|
||||
authcode: [{ validator: authcodeValidate, trigger: ['change'] }]
|
||||
},
|
||||
needAuthCode: false,
|
||||
logging: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
buttonName() {
|
||||
return this.form.needAuthCode ? '验证' : '登录'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['login', 'refreshUserCache', 'refreshOptionsCache']),
|
||||
handleLoginClick() {
|
||||
const _this = this
|
||||
_this.$refs.loginForm.validate((valid) => {
|
||||
if (valid) {
|
||||
_this.form.logging = true
|
||||
if (_this.form.needAuthCode && _this.form.model.authcode) {
|
||||
_this.handleLogin()
|
||||
} else {
|
||||
adminApi
|
||||
.loginPreCheck(_this.form.model.username, _this.form.model.password)
|
||||
.then((response) => {
|
||||
const data = response.data.data
|
||||
if (data && data.needMFACode) {
|
||||
_this.form.needAuthCode = true
|
||||
_this.form.model.authcode = null
|
||||
} else {
|
||||
_this.handleLogin()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
_this.form.logging = false
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleLogin() {
|
||||
const _this = this
|
||||
_this.form.logging = true
|
||||
_this.$refs.loginForm.validate((valid) => {
|
||||
if (valid) {
|
||||
_this
|
||||
.login(_this.form.model)
|
||||
.then((response) => {
|
||||
_this.$emit('success')
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
_this.form.logging = false
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-modal
|
||||
v-model="loginModal"
|
||||
title="重新登录"
|
||||
:footer="null"
|
||||
:width="320"
|
||||
:maskClosable="false"
|
||||
@cancel="handleCancelLogin"
|
||||
>
|
||||
<LoginForm @success="onLoginSucceed" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LoginForm from './LoginForm'
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
export default {
|
||||
name: 'LoginModal',
|
||||
components: {
|
||||
LoginForm
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['loginModal'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['ToggleLoginModal']),
|
||||
onLoginSucceed() {
|
||||
this.$emit('success')
|
||||
},
|
||||
handleCancelLogin() {
|
||||
this.ToggleLoginModal(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
|
@ -6,7 +6,7 @@
|
|||
v-model="collapsed"
|
||||
:trigger="null"
|
||||
>
|
||||
<logo/>
|
||||
<logo />
|
||||
<s-menu
|
||||
:collapsed="collapsed"
|
||||
:menu="menus"
|
||||
|
|
|
@ -103,7 +103,7 @@ export default {
|
|||
},
|
||||
renderMenuItem(menu) {
|
||||
const target = menu.meta.target || null
|
||||
const CustomTag = target && 'a' || 'router-link'
|
||||
const CustomTag = (target && 'a') || 'router-link'
|
||||
const props = { to: { name: menu.name } }
|
||||
const attrs = { href: menu.path, target: menu.meta.target }
|
||||
return (
|
||||
|
@ -135,10 +135,8 @@ export default {
|
|||
return null
|
||||
}
|
||||
const props = {}
|
||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||
return (
|
||||
<Icon {... { props } }/>
|
||||
)
|
||||
typeof icon === 'object' ? (props.component = icon) : (props.type = icon)
|
||||
return <Icon {...{ props }} />
|
||||
}
|
||||
},
|
||||
render() {
|
||||
|
@ -162,6 +160,6 @@ export default {
|
|||
return this.renderItem(item)
|
||||
})
|
||||
|
||||
return (<Menu {...dynamicProps}>{menuTree}</Menu>)
|
||||
return <Menu {...dynamicProps}>{menuTree}</Menu>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
|||
return null
|
||||
}
|
||||
const props = {}
|
||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||
typeof icon === 'object' ? (props.component = icon) : (props.type = icon)
|
||||
return h(Icon, { props: { ...props } })
|
||||
},
|
||||
renderMenuItem: function(h, menu, pIndex, index) {
|
||||
|
|
|
@ -3,22 +3,38 @@
|
|||
<div class="page-header-index-wide">
|
||||
<s-breadcrumb />
|
||||
<div class="detail">
|
||||
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||
<div
|
||||
class="main"
|
||||
v-if="!$route.meta.hiddenHeaderContent"
|
||||
>
|
||||
<div class="row">
|
||||
<img v-if="logo" :src="logo" class="logo"/>
|
||||
<img
|
||||
v-if="logo"
|
||||
:src="logo"
|
||||
class="logo"
|
||||
/>
|
||||
<!-- <h1 v-if="title" class="title">{{ title }}</h1> -->
|
||||
<div class="action">
|
||||
<slot name="action"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div v-if="avatar" class="avatar">
|
||||
<div
|
||||
v-if="avatar"
|
||||
class="avatar"
|
||||
>
|
||||
<a-avatar :src="avatar" />
|
||||
</div>
|
||||
<div v-if="this.$slots.content" class="headerContent">
|
||||
<div
|
||||
v-if="this.$slots.content"
|
||||
class="headerContent"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
<div v-if="this.$slots.extra" class="extra">
|
||||
<div
|
||||
v-if="this.$slots.extra"
|
||||
class="extra"
|
||||
>
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="setting-drawer-index-item">
|
||||
<h3 class="setting-drawer-index-title">{{ title }}</h3>
|
||||
<slot></slot>
|
||||
<a-divider v-if="divider"/>
|
||||
<a-divider v-if="divider" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -23,16 +23,14 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.setting-drawer-index-item {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.setting-drawer-index-item {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.setting-drawer-index-title {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
line-height: 22px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.setting-drawer-index-title {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
line-height: 22px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<a-breadcrumb class="breadcrumb">
|
||||
<a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
|
||||
<a-breadcrumb-item
|
||||
v-for="(item, index) in breadList"
|
||||
:key="item.name"
|
||||
>
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
|
@ -27,7 +30,7 @@ export default {
|
|||
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
|
||||
|
||||
this.name = this.$route.name
|
||||
this.$route.matched.forEach(item => {
|
||||
this.$route.matched.forEach((item) => {
|
||||
// item.name !== 'index' && this.breadList.push(item)
|
||||
this.breadList.push(item)
|
||||
})
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<template>
|
||||
<div class="head-info" :class="center && 'center'">
|
||||
<div
|
||||
class="head-info"
|
||||
:class="center && 'center'"
|
||||
>
|
||||
<span>{{ title }}</span>
|
||||
<p>{{ content }}</p>
|
||||
<em v-if="bordered"/>
|
||||
<em v-if="bordered" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -31,37 +34,37 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head-info {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
padding: 0 32px 0 0;
|
||||
min-width: 125px;
|
||||
.head-info {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
padding: 0 32px 0 0;
|
||||
min-width: 125px;
|
||||
|
||||
&.center {
|
||||
text-align: center;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
p {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
}
|
||||
em {
|
||||
background-color: #e8e8e8;
|
||||
position: absolute;
|
||||
height: 56px;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
&.center {
|
||||
text-align: center;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
p {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
}
|
||||
em {
|
||||
background-color: #e8e8e8;
|
||||
position: absolute;
|
||||
height: 56px;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -114,13 +114,13 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
converttedPostComments() {
|
||||
return this.postComments.map(comment => {
|
||||
return this.postComments.map((comment) => {
|
||||
comment.content = marked(decodeHTML(comment.content))
|
||||
return comment
|
||||
})
|
||||
},
|
||||
converttedSheetComments() {
|
||||
return this.sheetComments.map(comment => {
|
||||
return this.sheetComments.map((comment) => {
|
||||
comment.content = marked(decodeHTML(comment.content))
|
||||
return comment
|
||||
})
|
||||
|
@ -148,7 +148,7 @@ export default {
|
|||
}
|
||||
commentApi
|
||||
.latestComment('posts', 5, 'AUDITING')
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.postComments = response.data.data
|
||||
})
|
||||
.finally(() => {
|
||||
|
@ -163,7 +163,7 @@ export default {
|
|||
}
|
||||
commentApi
|
||||
.latestComment('sheets', 5, 'AUDITING')
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.sheetComments = response.data.data
|
||||
})
|
||||
.finally(() => {
|
||||
|
|
|
@ -34,7 +34,7 @@ export default {
|
|||
onLogoClick() {
|
||||
this.clickCount++
|
||||
if (this.clickCount === 10) {
|
||||
optionApi.save(this.optionsToCreate).then(response => {
|
||||
optionApi.save(this.optionsToCreate).then((response) => {
|
||||
this.refreshOptionsCache()
|
||||
this.$message.success(`开发者选项已启用!`)
|
||||
this.clickCount = 0
|
||||
|
|
|
@ -89,7 +89,7 @@ export default {
|
|||
.then(() => {
|
||||
window.location.reload()
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
that.$message.error({
|
||||
title: '错误',
|
||||
description: err.message
|
||||
|
|
|
@ -138,6 +138,6 @@
|
|||
-webkit-transition-duration: 1ms !important;
|
||||
transition-duration: 1ms !important;
|
||||
-webkit-animation-iteration-count: 1 !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,7 @@ body {
|
|||
|
||||
&.mobile,
|
||||
&.tablet {
|
||||
|
||||
.ant-layout-content {
|
||||
|
||||
.content {
|
||||
margin: 24px 0 0;
|
||||
}
|
||||
|
@ -53,7 +51,6 @@ body {
|
|||
}
|
||||
|
||||
.topmenu {
|
||||
|
||||
/* 必须为 topmenu 才能启用流式布局 */
|
||||
&.content-width-Fluid {
|
||||
.header-index-wide {
|
||||
|
@ -66,10 +63,9 @@ body {
|
|||
&.mobile {
|
||||
.sidemenu {
|
||||
.ant-header-fixedHeader {
|
||||
|
||||
&.ant-header-side-opened,
|
||||
&.ant-header-side-closed {
|
||||
width: 100%
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +80,7 @@ body {
|
|||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
|
@ -98,7 +94,7 @@ body {
|
|||
right: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
transition: width .2s;
|
||||
transition: width 0.2s;
|
||||
|
||||
&.ant-header-side-opened {
|
||||
width: 100%;
|
||||
|
@ -136,7 +132,6 @@ body {
|
|||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sidemenu {
|
||||
|
@ -146,14 +141,14 @@ body {
|
|||
right: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
transition: width .2s;
|
||||
transition: width 0.2s;
|
||||
|
||||
&.ant-header-side-opened {
|
||||
width: calc(100% - 256px)
|
||||
width: calc(100% - 256px);
|
||||
}
|
||||
|
||||
&.ant-header-side-closed {
|
||||
width: calc(100% - 80px)
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,13 +157,12 @@ body {
|
|||
height: 64px;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header,
|
||||
.top-nav-header-index {
|
||||
|
||||
.user-wrapper {
|
||||
float: right;
|
||||
height: 100%;
|
||||
|
@ -177,7 +171,7 @@ body {
|
|||
cursor: pointer;
|
||||
padding: 0 18px;
|
||||
display: inline-block;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
height: 100%;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
|
||||
|
@ -188,7 +182,7 @@ body {
|
|||
.avatar {
|
||||
margin: 20px 0 20px 0;
|
||||
color: #1890ff;
|
||||
background: hsla(0, 0%, 100%, .85);
|
||||
background: hsla(0, 0%, 100%, 0.85);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
@ -201,7 +195,6 @@ body {
|
|||
|
||||
&.dark {
|
||||
.user-wrapper {
|
||||
|
||||
.action {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
|
||||
|
@ -220,11 +213,8 @@ body {
|
|||
&.mobile,
|
||||
&.tablet {
|
||||
.top-nav-header-index {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
|
||||
.trigger {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
padding: 0 24px;
|
||||
|
@ -243,9 +233,7 @@ body {
|
|||
}
|
||||
|
||||
&.light {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
.trigger {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
|
@ -257,14 +245,11 @@ body {
|
|||
}
|
||||
|
||||
&.tablet {
|
||||
|
||||
// overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
|
||||
.top-nav-header-index {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
.logo>a {
|
||||
.logo > a {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
@ -277,13 +262,12 @@ body {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.top-nav-header-index {
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
position: relative;
|
||||
transition: background .3s, width .2s;
|
||||
transition: background 0.3s, width 0.2s;
|
||||
|
||||
.header-index-wide {
|
||||
max-width: 1200px;
|
||||
|
@ -310,7 +294,7 @@ body {
|
|||
height: 64px;
|
||||
position: relative;
|
||||
line-height: 64px;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
overflow: hidden;
|
||||
|
||||
img,
|
||||
|
@ -373,7 +357,6 @@ body {
|
|||
height: 64px;
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.topmenu {
|
||||
|
@ -404,13 +387,13 @@ body {
|
|||
}
|
||||
|
||||
.ant-drawer-body {
|
||||
padding: 0
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单样式
|
||||
.sider {
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
|
||||
position: relative;
|
||||
z-index: @ant-global-sider-zindex;
|
||||
min-height: 100vh;
|
||||
|
@ -435,7 +418,7 @@ body {
|
|||
overflow: hidden;
|
||||
line-height: 64px;
|
||||
background: #002140;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
|
||||
img,
|
||||
svg,
|
||||
|
@ -477,7 +460,6 @@ body {
|
|||
border-right-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 外置的样式控制
|
||||
|
@ -494,19 +476,16 @@ body {
|
|||
width: 160px;
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item>.anticon:first-child,
|
||||
.ant-dropdown-menu-item>a>.anticon:first-child,
|
||||
.ant-dropdown-menu-submenu-title>.anticon:first-child .ant-dropdown-menu-submenu-title>a>.anticon:first-child {
|
||||
.ant-dropdown-menu-item > .anticon:first-child,
|
||||
.ant-dropdown-menu-item > a > .anticon:first-child,
|
||||
.ant-dropdown-menu-submenu-title > .anticon:first-child .ant-dropdown-menu-submenu-title > a > .anticon:first-child {
|
||||
min-width: 12px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-page-search-wrapper {
|
||||
|
||||
.ant-form-inline {
|
||||
|
||||
.ant-form-item {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
@ -518,7 +497,7 @@ body {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
>.ant-form-item-label {
|
||||
> .ant-form-item-label {
|
||||
line-height: 32px;
|
||||
padding-right: 8px;
|
||||
width: auto;
|
||||
|
@ -536,15 +515,13 @@ body {
|
|||
margin-bottom: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-table-thead>tr>th {
|
||||
.ant-table-thead > tr > th {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.table-operator {
|
||||
margin-bottom: 18px;
|
||||
|
||||
|
@ -580,17 +557,17 @@ body {
|
|||
.card-container {
|
||||
background: #f5f5f5;
|
||||
|
||||
&>.ant-tabs-card {
|
||||
&>.ant-tabs-content {
|
||||
& > .ant-tabs-card {
|
||||
& > .ant-tabs-content {
|
||||
margin-top: -16px;
|
||||
|
||||
&>.ant-tabs-tabpane {
|
||||
& > .ant-tabs-tabpane {
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&>.ant-tabs-bar {
|
||||
& > .ant-tabs-bar {
|
||||
border-color: #fff;
|
||||
|
||||
.ant-tabs-tab {
|
||||
|
@ -607,7 +584,6 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.ant-comment {
|
||||
.ant-comment-actions {
|
||||
margin-bottom: 0 !important;
|
||||
|
@ -666,7 +642,7 @@ body {
|
|||
flex-flow: row wrap;
|
||||
|
||||
.ant-pagination-options-size-changer.ant-select {
|
||||
margin: 0
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
|
@ -877,7 +853,6 @@ body {
|
|||
line-height: initial !important;
|
||||
}
|
||||
|
||||
|
||||
.theme-screenshot {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
@ -892,4 +867,4 @@ body {
|
|||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@ html [type='button'] {
|
|||
}
|
||||
|
||||
// The prefix to use on all css classes from ant-pro.
|
||||
@ant-pro-prefix : ant-pro;
|
||||
@ant-global-sider-zindex : 106;
|
||||
@ant-global-header-zindex : 105;
|
||||
@ant-pro-prefix: ant-pro;
|
||||
@ant-global-sider-zindex: 106;
|
||||
@ant-global-header-zindex: 105;
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
@import './animate.less';
|
||||
.container-wrapper {
|
||||
background: #ffffff;
|
||||
position: absolute;
|
||||
border-radius: 5px;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
margin: -160px 0 0 -160px;
|
||||
width: 320px;
|
||||
padding: 18px 28px 28px 28px;
|
||||
box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1);
|
||||
background: #ffffff;
|
||||
position: absolute;
|
||||
border-radius: 5px;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
margin: -160px 0 0 -160px;
|
||||
width: 320px;
|
||||
padding: 18px 28px 28px 28px;
|
||||
box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.halo-logo {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
.halo-logo {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
vertical-align: text-bottom;
|
||||
font-size: 38px;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #1790fe;
|
||||
background-image: linear-gradient(-20deg, #6e45e2 0%, #88d3ce 100%);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
span {
|
||||
vertical-align: text-bottom;
|
||||
font-size: 38px;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #1790fe;
|
||||
background-image: linear-gradient(-20deg, #6e45e2 0%, #88d3ce 100%);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
|
||||
small {
|
||||
margin-left: 5px;
|
||||
font-size: 35%;
|
||||
}
|
||||
}
|
||||
small {
|
||||
margin-left: 5px;
|
||||
font-size: 35%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tip {
|
||||
cursor: pointer;
|
||||
margin-left: 0.5rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
cursor: pointer;
|
||||
margin-left: 0.5rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@
|
|||
</a-layout>
|
||||
|
||||
<setting-drawer ref="drawer"></setting-drawer>
|
||||
|
||||
<LoginModal @success="onLoginSucceed" />
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
|
@ -71,6 +73,7 @@ import SideMenu from '@/components/Menu/SideMenu'
|
|||
import GlobalHeader from '@/components/GlobalHeader'
|
||||
import GlobalFooter from '@/components/GlobalFooter'
|
||||
import SettingDrawer from '@/components/SettingDrawer/SettingDrawer'
|
||||
import LoginModal from '@/components/Login/LoginModal'
|
||||
|
||||
export default {
|
||||
name: 'BasicLayout',
|
||||
|
@ -80,7 +83,8 @@ export default {
|
|||
SideMenu,
|
||||
GlobalHeader,
|
||||
GlobalFooter,
|
||||
SettingDrawer
|
||||
SettingDrawer,
|
||||
LoginModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -92,7 +96,7 @@ export default {
|
|||
computed: {
|
||||
...mapState({
|
||||
// 动态主路由
|
||||
mainMenu: state => state.permission.addRouters
|
||||
mainMenu: (state) => state.permission.addRouters
|
||||
}),
|
||||
contentPaddingLeft() {
|
||||
if (!this.fixSidebar || this.isMobile()) {
|
||||
|
@ -110,7 +114,7 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.menus = asyncRouterMap.find(item => item.path === '/').children
|
||||
this.menus = asyncRouterMap.find((item) => item.path === '/').children
|
||||
// this.menus = this.mainMenu.find((item) => item.path === '/').children
|
||||
this.collapsed = !this.sidebarOpened
|
||||
},
|
||||
|
@ -126,7 +130,7 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSidebar']),
|
||||
...mapActions(['setSidebar', 'ToggleLoginModal']),
|
||||
toggle() {
|
||||
this.collapsed = !this.collapsed
|
||||
this.setSidebar(!this.collapsed)
|
||||
|
@ -137,7 +141,7 @@ export default {
|
|||
if (this.sidebarOpened) {
|
||||
left = this.isDesktop() ? '256px' : '80px'
|
||||
} else {
|
||||
left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0')
|
||||
left = (this.isMobile() && '0') || (this.fixSidebar && '80px') || '0'
|
||||
}
|
||||
return left
|
||||
},
|
||||
|
@ -148,6 +152,9 @@ export default {
|
|||
},
|
||||
drawerClose() {
|
||||
this.collapsed = false
|
||||
},
|
||||
onLoginSucceed() {
|
||||
this.ToggleLoginModal(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const getters = {
|
|||
theme: state => state.app.theme,
|
||||
color: state => state.app.color,
|
||||
layoutSetting: state => state.app.layoutSetting,
|
||||
loginModal: state => state.app.loginModal,
|
||||
token: state => state.user.token,
|
||||
user: state => state.user.user,
|
||||
addRouters: state => state.permission.addRouters,
|
||||
|
|
|
@ -24,7 +24,8 @@ const app = {
|
|||
autoHideHeader: false,
|
||||
color: null,
|
||||
apiUrl: null,
|
||||
layoutSetting: false
|
||||
layoutSetting: false,
|
||||
loginModal: false
|
||||
},
|
||||
mutations: {
|
||||
SET_API_URL: (state, apiUrl) => {
|
||||
|
@ -78,6 +79,9 @@ const app = {
|
|||
TOGGLE_LAYOUT_SETTING: (state, show) => {
|
||||
Vue.ls.set(LAYOUT_SETTING, show)
|
||||
state.layoutSetting = show
|
||||
},
|
||||
TOGGLE_LOGIN_MODAL: (state, show) => {
|
||||
state.loginModal = show
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -116,6 +120,9 @@ const app = {
|
|||
},
|
||||
ToggleLayoutSetting({ commit }, show) {
|
||||
commit('TOGGLE_LAYOUT_SETTING', show)
|
||||
},
|
||||
ToggleLoginModal({ commit }, show) {
|
||||
commit('TOGGLE_LOGIN_MODAL', show)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import axios from 'axios'
|
|||
import Vue from 'vue'
|
||||
import { message, notification } from 'ant-design-vue'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
import { isObject } from './util'
|
||||
|
||||
const service = axios.create({
|
||||
|
@ -38,7 +37,8 @@ async function refreshToken(error) {
|
|||
await refreshTask
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data && err.response.data.data === refreshToken) {
|
||||
router.push({ name: 'Login' })
|
||||
message.warning('当前登录状态已失效,请重新登录')
|
||||
store.dispatch('ToggleLoginModal', true)
|
||||
}
|
||||
Vue.$log.error('Failed to refresh token', err)
|
||||
} finally {
|
||||
|
@ -124,7 +124,8 @@ service.interceptors.response.use(
|
|||
}
|
||||
} else {
|
||||
// Login
|
||||
router.push({ name: 'Login' })
|
||||
message.warning('当前登录状态已失效,请重新登录')
|
||||
store.dispatch('ToggleLoginModal', true)
|
||||
}
|
||||
} else if (data.status === 403) {
|
||||
// TODO handle 403 status error
|
||||
|
|
|
@ -265,13 +265,13 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.verifyIsInstall()
|
||||
beforeMount() {
|
||||
this.handleVerifyIsInstall()
|
||||
this.$set(this.installation, 'url', window.location.protocol + '//' + window.location.host)
|
||||
},
|
||||
methods: {
|
||||
async verifyIsInstall() {
|
||||
await adminApi.isInstalled().then(response => {
|
||||
async handleVerifyIsInstall() {
|
||||
await adminApi.isInstalled().then((response) => {
|
||||
if (response.data.data) {
|
||||
this.$router.push({ name: 'Login' })
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ export default {
|
|||
},
|
||||
handleNextStep() {
|
||||
if (this.stepCurrent === 0) {
|
||||
this.$refs.generalForm.validate(valid => {
|
||||
this.$refs.generalForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.stepCurrent++
|
||||
} else {
|
||||
|
@ -287,7 +287,7 @@ export default {
|
|||
}
|
||||
})
|
||||
} else if (this.stepCurrent === 1) {
|
||||
this.$refs.blogForm.validate(valid => {
|
||||
this.$refs.blogForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.stepCurrent++
|
||||
} else {
|
||||
|
@ -307,7 +307,7 @@ export default {
|
|||
install() {
|
||||
adminApi
|
||||
.install(this.installation)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.$log.debug('Installation response', response)
|
||||
this.$message.success('安装成功!')
|
||||
setTimeout(() => {
|
||||
|
@ -326,7 +326,7 @@ export default {
|
|||
const hide = this.$message.loading('数据导入中...', 0)
|
||||
migrateApi
|
||||
.migrate(this.migrationData)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.$log.debug('Migrated successfullly')
|
||||
this.$message.success('数据导入成功!')
|
||||
this.install()
|
||||
|
|
|
@ -2,101 +2,60 @@
|
|||
<div class="container-wrapper">
|
||||
<div class="halo-logo animated fadeInUp">
|
||||
<span>Halo
|
||||
<small v-if="apiModifyVisible">API 设置</small>
|
||||
<small v-if="authcodeVisible">两步验证</small>
|
||||
<small v-if="apiForm.visible">API 设置</small>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-show="formVisible == 'login-form'"
|
||||
v-show="!apiForm.visible"
|
||||
class="login-form animated"
|
||||
>
|
||||
<a-form
|
||||
layout="vertical"
|
||||
@keyup.enter.native="handleLoginPreCheck"
|
||||
>
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.1s'}"
|
||||
>
|
||||
<a-input
|
||||
placeholder="用户名/邮箱"
|
||||
v-model="username"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="user"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.2s'}"
|
||||
>
|
||||
<a-input
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="lock"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.3s'}"
|
||||
>
|
||||
<a-button
|
||||
:loading="landing"
|
||||
type="primary"
|
||||
:block="true"
|
||||
@click="handleLoginPreCheck"
|
||||
>登录</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<a-row>
|
||||
<LoginForm @success="onLoginSucceed" />
|
||||
<a-row>
|
||||
<a-col :span="24">
|
||||
<router-link :to="{ name:'ResetPassword' }">
|
||||
<a
|
||||
class="tip animated fadeInRight"
|
||||
v-if="resetPasswordButton"
|
||||
v-if="resetPasswordButtonVisible"
|
||||
href="javascript:void(0);"
|
||||
>
|
||||
找回密码
|
||||
</a>
|
||||
</router-link>
|
||||
<a
|
||||
@click="toggleShowApiForm"
|
||||
@click="handleToggleShowApiForm"
|
||||
class="tip animated fadeInUp"
|
||||
:style="{'animation-delay': '0.4s'}"
|
||||
>
|
||||
<a-icon type="setting" />
|
||||
</a>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<div
|
||||
v-show="apiModifyVisible"
|
||||
v-show="apiForm.visible"
|
||||
class="api-form animated"
|
||||
>
|
||||
<a-form layout="vertical">
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.1s'}"
|
||||
extra="* 如果 Admin 不是独立部署,请不要更改此 API"
|
||||
>
|
||||
<a-input
|
||||
placeholder="API 地址"
|
||||
v-model="apiUrl"
|
||||
<a-tooltip
|
||||
placement="top"
|
||||
title="如果 Admin 不是独立部署,请不要更改此 API"
|
||||
trigger="click"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="api"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
<a-input
|
||||
placeholder="API 地址"
|
||||
v-model="apiForm.apiUrl"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="api"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
|
@ -104,7 +63,7 @@
|
|||
>
|
||||
<a-button
|
||||
:block="true"
|
||||
@click="handleApiUrlRestore"
|
||||
@click="handleRestoreApiUrl"
|
||||
>恢复默认</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
|
@ -114,58 +73,12 @@
|
|||
<a-button
|
||||
type="primary"
|
||||
:block="true"
|
||||
@click="handleApiModifyOk"
|
||||
@click="handleModifyApiUrl"
|
||||
>保存设置</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<a-row>
|
||||
<a
|
||||
@click="toggleShowApiForm"
|
||||
class="tip animated fadeInUp"
|
||||
:style="{'animation-delay': '0.4s'}"
|
||||
>
|
||||
<a-icon type="rollback" />
|
||||
</a>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="authcodeVisible"
|
||||
class="authcode-form animated"
|
||||
>
|
||||
<a-form layout="vertical" @keyup.enter.native="handleLogin">
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.1s'}"
|
||||
>
|
||||
<a-input
|
||||
placeholder="两步验证码"
|
||||
v-model="authcode"
|
||||
:maxLength="6"
|
||||
>
|
||||
<a-icon
|
||||
slot="prefix"
|
||||
type="safety-certificate"
|
||||
style="color: rgba(0,0,0,.25)"
|
||||
/>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="animated fadeInUp"
|
||||
:style="{'animation-delay': '0.3s'}"
|
||||
>
|
||||
<a-button
|
||||
:loading="landing"
|
||||
type="primary"
|
||||
:block="true"
|
||||
@click="handleLogin"
|
||||
>验证</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<a-row>
|
||||
<a
|
||||
@click="toggleShowLoginForm"
|
||||
@click="handleToggleShowApiForm"
|
||||
class="tip animated fadeInUp"
|
||||
:style="{'animation-delay': '0.4s'}"
|
||||
>
|
||||
|
@ -181,107 +94,47 @@
|
|||
import adminApi from '@/api/admin'
|
||||
import { mapActions, mapGetters, mapMutations } from 'vuex'
|
||||
|
||||
import LoginForm from '@/components/Login/LoginForm'
|
||||
export default {
|
||||
components: {
|
||||
LoginForm
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: null,
|
||||
password: null,
|
||||
authcode: null,
|
||||
needAuthCode: false,
|
||||
formVisible: 'login-form', // login-form api-form authcode-form
|
||||
loginVisible: true,
|
||||
apiModifyVisible: false,
|
||||
authcodeVisible: false,
|
||||
defaultApiBefore: window.location.protocol + '//',
|
||||
apiUrl: window.location.host,
|
||||
resetPasswordButton: false,
|
||||
landing: false
|
||||
resetPasswordButtonVisible: false,
|
||||
apiForm: {
|
||||
apiUrl: window.location.host,
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ defaultApiUrl: 'apiUrl' })
|
||||
},
|
||||
created() {
|
||||
this.verifyIsInstall()
|
||||
beforeMount() {
|
||||
const _this = this
|
||||
_this.handleVerifyIsInstall()
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.keyCode === 72 && e.altKey && e.shiftKey) {
|
||||
_this.toggleHidden()
|
||||
_this.resetPasswordButtonVisible = !_this.resetPasswordButtonVisible
|
||||
}
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
formVisible(value) {
|
||||
this.loginVisible = (value === 'authcode-form')
|
||||
this.apiModifyVisible = (value === 'api-form')
|
||||
this.authcodeVisible = (value === 'authcode-form')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['login', 'refreshUserCache', 'refreshOptionsCache']),
|
||||
...mapActions(['refreshUserCache', 'refreshOptionsCache']),
|
||||
...mapMutations({
|
||||
setApiUrl: 'SET_API_URL',
|
||||
restoreApiUrl: 'RESTORE_API_URL'
|
||||
}),
|
||||
verifyIsInstall() {
|
||||
adminApi.isInstalled().then(response => {
|
||||
handleVerifyIsInstall() {
|
||||
adminApi.isInstalled().then((response) => {
|
||||
if (!response.data.data) {
|
||||
this.$router.push({ name: 'Install' })
|
||||
}
|
||||
})
|
||||
},
|
||||
handleLoginPreCheck() {
|
||||
if (!this.username) {
|
||||
this.$message.warn('用户名不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.password) {
|
||||
this.$message.warn('密码不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
adminApi.loginPreCheck(this.username, this.password).then(response => {
|
||||
if (response.data.data && response.data.data.needMFACode) {
|
||||
this.formVisible = 'authcode-form'
|
||||
this.authcode = null
|
||||
this.needAuthCode = true
|
||||
} else {
|
||||
this.needAuthCode = false
|
||||
this.handleLogin()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleLogin() {
|
||||
if (!this.username) {
|
||||
this.$message.warn('用户名不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.password) {
|
||||
this.$message.warn('密码不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
if (this.needAuthCode && !this.authcode) {
|
||||
this.$message.warn('两步验证码不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
this.landing = true
|
||||
this.login({ username: this.username, password: this.password, authcode: this.authcode })
|
||||
.then(response => {
|
||||
// Go to dashboard
|
||||
this.loginSuccess()
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.landing = false
|
||||
}, 400)
|
||||
})
|
||||
},
|
||||
loginSuccess() {
|
||||
// Cache the user info
|
||||
onLoginSucceed() {
|
||||
// Refresh the user info
|
||||
this.refreshUserCache()
|
||||
this.refreshOptionsCache()
|
||||
if (this.$route.query.redirect) {
|
||||
|
@ -290,27 +143,19 @@ export default {
|
|||
this.$router.replace({ name: 'Dashboard' })
|
||||
}
|
||||
},
|
||||
handleApiModifyOk() {
|
||||
this.setApiUrl(this.apiUrl)
|
||||
this.formVisible = 'login-form'
|
||||
handleModifyApiUrl() {
|
||||
this.setApiUrl(this.apiForm.apiUrl)
|
||||
this.apiForm.visible = false
|
||||
},
|
||||
handleApiUrlRestore() {
|
||||
handleRestoreApiUrl() {
|
||||
this.restoreApiUrl()
|
||||
this.apiUrl = this.defaultApiUrl
|
||||
this.apiForm.apiUrl = this.defaultApiUrl
|
||||
},
|
||||
toggleShowApiForm() {
|
||||
this.formVisible = this.apiModifyVisible ? 'login-form' : 'api-form'
|
||||
this.apiModifyVisible = !this.apiModifyVisible
|
||||
if (this.apiModifyVisible) {
|
||||
this.apiUrl = this.defaultApiUrl
|
||||
handleToggleShowApiForm() {
|
||||
this.apiForm.visible = !this.apiForm.visible
|
||||
if (this.apiForm.visible) {
|
||||
this.apiForm.apiUrl = this.defaultApiUrl
|
||||
}
|
||||
},
|
||||
toggleShowLoginForm() {
|
||||
this.formVisible = 'login-form'
|
||||
this.password = null
|
||||
},
|
||||
toggleHidden() {
|
||||
this.resetPasswordButton = !this.resetPasswordButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue