chore: remove unnecessary dependencies.

pull/301/head
Ryan Wang 2021-03-06 18:38:10 +08:00
parent 14eda89f81
commit 17e0f47465
106 changed files with 17039 additions and 6305 deletions

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

14
.eslintrc.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}

5
.gitignore vendored
View File

@ -1,7 +1,7 @@
.DS_Store .DS_Store
node_modules node_modules
/dist /dist
yarn.lock
# local env files # local env files
.env.local .env.local
@ -11,6 +11,7 @@ yarn.lock
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
# Editor directories and files # Editor directories and files
.idea .idea
@ -19,4 +20,4 @@ yarn-error.log*
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw* *.sw?

View File

@ -1,11 +1,3 @@
module.exports = { module.exports = {
presets: [ presets: ['@vue/cli-plugin-babel/preset']
'@vue/app',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry'
}
]
]
} }

3
jsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"include": ["./src/**/*"]
}

4762
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -47,110 +47,34 @@
"vuex": "^3.6.0" "vuex": "^3.6.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/polyfill": "^7.12.1", "@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-babel": "^3.8.0",
"@vue/cli-plugin-eslint": "^4.5.4", "@vue/cli-plugin-eslint": "^4.5.4",
"@vue/cli-plugin-unit-jest": "^4.5.4", "@vue/cli-plugin-unit-jest": "^4.5.4",
"@vue/cli-service": "^4.5.4", "@vue/cli-service": "^4.5.4",
"@vue/eslint-config-standard": "^4.0.0", "@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "^1.1.3", "@vue/test-utils": "^1.1.3",
"babel-core": "7.0.0-bridge.0", "babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.6.3", "babel-jest": "^26.6.3",
"babel-plugin-import": "^1.13.3", "babel-plugin-import": "^1.13.3",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-html": "^6.1.1", "eslint-plugin-html": "^6.1.1",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"generate-asset-webpack-plugin": "^0.3.0", "prettier": "^1.19.1",
"husky": "^4.3.8",
"less": "^3.13.1", "less": "^3.13.1",
"less-loader": "^5.0.0", "less-loader": "^5.0.0",
"lint-staged": "^10.5.4", "lint-staged": "^10.5.4",
"tailwindcss": "^1.8.8", "tailwindcss": "^1.8.8",
"vue-svg-component-runtime": "^1.0.1",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.6.12" "vue-template-compiler": "^2.6.12"
}, },
"eslintConfig": { "gitHooks": {
"root": true, "pre-commit": "lint-staged"
"env": {
"node": true
},
"extends": [
"plugin:vue/strongly-recommended",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {
"space-before-function-paren": [
"error",
"never"
],
"generator-star-spacing": "off",
"no-mixed-operators": 0,
"vue/max-attributes-per-line": [
2,
{
"singleline": 5,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}
],
"vue/attribute-hyphenation": 0,
"vue/html-self-closing": 0,
"vue/component-name-in-template-casing": 0,
"vue/html-closing-bracket-spacing": 0,
"vue/singleline-html-element-content-newline": 0,
"vue/no-unused-components": 0,
"vue/multiline-html-element-content-newline": 0,
"vue/no-use-v-if-with-v-for": 0,
"vue/html-closing-bracket-newline": 0,
"vue/no-parsing-error": 0,
"no-console": 0,
"no-tabs": 0,
"quotes": [
2,
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": [
2,
"never",
{
"beforeStatementContinuationChars": "never"
}
],
"no-delete-var": 2,
"prefer-const": [
2,
{
"ignoreReadBeforeAssign": false
}
],
"comma-dangle": 0
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}, },
"lint-staged": { "lint-staged": {
"src/**/*.{js,vue}": [ "*.{js,jsx,vue}": [
"eslint --fix", "vue-cli-service lint",
"git add" "git add"
] ]
}, }
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
} }

View File

@ -1,6 +1,3 @@
module.exports = { module.exports = {
plugins: [ plugins: [require('autoprefixer'), require('tailwindcss')]
require('autoprefixer'),
require('tailwindcss')
]
} }

View File

@ -1,9 +1,6 @@
<template> <template>
<a-config-provider :locale="locale"> <a-config-provider :locale="locale">
<div <div id="app" class="h-full">
id="app"
class="h-full"
>
<router-view /> <router-view />
</div> </div>
</a-config-provider> </a-config-provider>
@ -21,7 +18,7 @@ export default {
}, },
mounted() { mounted() {
const { $store } = this const { $store } = this
deviceEnquire((deviceType) => { deviceEnquire(deviceType => {
switch (deviceType) { switch (deviceType) {
case DEVICE_TYPE.DESKTOP: case DEVICE_TYPE.DESKTOP:
$store.commit('TOGGLE_DEVICE', 'desktop') $store.commit('TOGGLE_DEVICE', 'desktop')

View File

@ -12,7 +12,7 @@ journalApi.query = params => {
}) })
} }
journalApi.create = (journal) => { journalApi.create = journal => {
return service({ return service({
url: baseUrl, url: baseUrl,
data: journal, data: journal,

View File

@ -4,7 +4,7 @@ const baseUrl = '/api/admin/journals/comments'
const journalCommentApi = {} const journalCommentApi = {}
journalCommentApi.create = (comment) => { journalCommentApi.create = comment => {
return service({ return service({
url: baseUrl, url: baseUrl,
data: comment, data: comment,

View File

@ -11,7 +11,7 @@ linkApi.listAll = () => {
}) })
} }
linkApi.create = (link) => { linkApi.create = link => {
return service({ return service({
url: baseUrl, url: baseUrl,
data: link, data: link,

View File

@ -4,7 +4,7 @@ const baseUrl = '/api/admin/logs'
const logApi = {} const logApi = {}
logApi.listLatest = (top) => { logApi.listLatest = top => {
return service({ return service({
url: `${baseUrl}/latest`, url: `${baseUrl}/latest`,
params: { params: {

View File

@ -12,7 +12,7 @@ photoApi.query = params => {
}) })
} }
photoApi.create = (photo) => { photoApi.create = photo => {
return service({ return service({
url: baseUrl, url: baseUrl,
data: photo, data: photo,

View File

@ -105,6 +105,6 @@ sheetApi.permalinkType = {
ROOT: { ROOT: {
type: 'ROOT', type: 'ROOT',
text: '根路径' text: '根路径'
}, }
} }
export default sheetApi export default sheetApi

View File

@ -30,7 +30,7 @@ userApi.updatePassword = (oldPassword, newPassword) => {
}) })
} }
userApi.mfaGenerate = (mfaType) => { userApi.mfaGenerate = mfaType => {
return service({ return service({
url: `${baseUrl}/mfa/generate`, url: `${baseUrl}/mfa/generate`,
method: 'put', method: 'put',
@ -52,7 +52,7 @@ userApi.mfaUpdate = (mfaType, mfaKey, authcode) => {
}) })
} }
userApi.mfaCheck = (authcode) => { userApi.mfaCheck = authcode => {
return service({ return service({
url: `${baseUrl}/mfa/check`, url: `${baseUrl}/mfa/check`,
method: 'put', method: 'put',

View File

@ -2,32 +2,21 @@
<div> <div>
<a-drawer <a-drawer
:title="title" :title="title"
:width="isMobile()?'100%':drawerWidth" :width="isMobile() ? '100%' : drawerWidth"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex" <a-input-search placeholder="搜索" v-model="queryParam.keyword" @search="handleQuery()" enterButton />
align="middle"
>
<a-input-search
placeholder="搜索"
v-model="queryParam.keyword"
@search="handleQuery()"
enterButton
/>
</a-row> </a-row>
<a-divider /> <a-divider />
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<a-empty v-if="attachments.length==0" /> <a-empty v-if="attachments.length == 0" />
<div <div
v-else v-else
class="attach-item" class="attach-item"
@ -36,11 +25,7 @@
@click="handleSelectAttachment(item)" @click="handleSelectAttachment(item)"
> >
<span v-show="!handleJudgeMediaType(item)"></span> <span v-show="!handleJudgeMediaType(item)"></span>
<img <img :src="item.thumbPath" v-show="handleJudgeMediaType(item)" loading="lazy" />
:src="item.thumbPath"
v-show="handleJudgeMediaType(item)"
loading="lazy"
>
</div> </div>
</a-spin> </a-spin>
</a-col> </a-col>
@ -58,30 +43,14 @@
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-space> <a-space>
<a-button <a-button type="dashed" v-if="isChooseAvatar" @click="handleSelectGravatar">使 Gravatar</a-button>
type="dashed" <a-button @click="handleShowUploadModal" type="primary">上传附件</a-button>
v-if="isChooseAvatar"
@click="handleSelectGravatar"
>使用 Gravatar</a-button>
<a-button
@click="handleShowUploadModal"
type="primary"
>上传附件</a-button>
</a-space> </a-space>
</div> </div>
</a-drawer> </a-drawer>
<a-modal <a-modal title="上传附件" v-model="uploadVisible" :footer="null" :afterClose="onUploadClose" destroyOnClose>
title="上传附件" <FilePondUpload ref="upload" :uploadHandler="uploadHandler"></FilePondUpload>
v-model="uploadVisible"
:footer="null"
:afterClose="onUploadClose"
destroyOnClose
>
<FilePondUpload
ref="upload"
:uploadHandler="uploadHandler"
></FilePondUpload>
</a-modal> </a-modal>
</div> </div>
</template> </template>
@ -150,7 +119,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
attachmentApi attachmentApi
.query(this.queryParam) .query(this.queryParam)
.then((response) => { .then(response => {
this.attachments = response.data.data.content this.attachments = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })

View File

@ -6,7 +6,8 @@
:loading="loading" :loading="loading"
:size="size" :size="size"
:block="block" :block="block"
>{{ computedText }}</a-button> >{{ computedText }}</a-button
>
</template> </template>
<script> <script>
export default { export default {
@ -14,45 +15,45 @@ export default {
props: { props: {
type: { type: {
type: String, type: String,
default: 'primary', default: 'primary'
}, },
icon: { icon: {
type: String, type: String,
default: null, default: null
}, },
size: { size: {
type: String, type: String,
default: 'default', default: 'default'
}, },
block: { block: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
loading: { loading: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
errored: { errored: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
text: { text: {
type: String, type: String,
default: '', default: ''
}, },
loadedText: { loadedText: {
type: String, type: String,
default: '', default: ''
}, },
erroredText: { erroredText: {
type: String, type: String,
default: '', default: ''
}, }
}, },
data() { data() {
return { return {
loaded: false, loaded: false,
hasError: false, hasError: false
} }
}, },
watch: { watch: {
@ -68,7 +69,7 @@ export default {
this.$emit('callback') this.$emit('callback')
}, 400) }, 400)
} }
}, }
}, },
computed: { computed: {
computedType() { computedType() {
@ -88,12 +89,12 @@ export default {
return this.hasError ? this.erroredText : this.loadedText return this.hasError ? this.erroredText : this.loadedText
} }
return this.text return this.text
}, }
}, },
methods: { methods: {
handleClick() { handleClick() {
this.$emit('click') this.$emit('click')
}, }
}, }
} }
</script> </script>

View File

@ -20,19 +20,19 @@ import attachmentApi from '@/api/attachment'
export default { export default {
name: 'MarkdownEditor', name: 'MarkdownEditor',
components: { components: {
haloEditor, haloEditor
}, },
props: { props: {
originalContent: { originalContent: {
type: String, type: String,
required: false, required: false,
default: '', default: ''
}, }
}, },
data() { data() {
return { return {
toolbars, toolbars,
originalContentData: '', originalContentData: ''
} }
}, },
watch: { watch: {
@ -41,13 +41,13 @@ export default {
}, },
originalContentData(val) { originalContentData(val) {
this.$emit('onContentChange', val) this.$emit('onContentChange', val)
}, }
}, },
methods: { methods: {
handleAttachmentUpload(pos, $file) { handleAttachmentUpload(pos, $file) {
var formdata = new FormData() var formdata = new FormData()
formdata.append('file', $file) formdata.append('file', $file)
attachmentApi.upload(formdata).then((response) => { attachmentApi.upload(formdata).then(response => {
var responseObject = response.data var responseObject = response.data
var HaloEditor = this.$refs.md var HaloEditor = this.$refs.md
HaloEditor.$img2Url(pos, encodeURI(responseObject.data.path)) HaloEditor.$img2Url(pos, encodeURI(responseObject.data.path))
@ -55,7 +55,7 @@ export default {
}, },
handleSaveDraft() { handleSaveDraft() {
this.$emit('onSaveDraft') this.$emit('onSaveDraft')
}, }
}, }
} }
</script> </script>

View File

@ -1,10 +1,6 @@
<template> <template>
<div> <div>
<a-input <a-input type="textarea" v-model="originalContent" :rows="16" />
type="textarea"
v-model="originalContent"
:rows="16"
/>
</div> </div>
</template> </template>
<script> <script>

View File

@ -42,7 +42,7 @@ export default {
}, },
render() { render() {
const { tooltip, length } = this.$props 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 fullLength = getStrFullLength(str)
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength) const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
return strDom return strDom

View File

@ -1,14 +1,8 @@
<template> <template>
<div <div class="footer text-center" style="padding: 0 16px;margin: 48px 0 0;">
class="footer text-center" <div class="copyright" style="color: rgba(0, 0, 0, 0.45);font-size: 14px;">
style="padding: 0 16px;margin: 48px 0 0;"
>
<div
class="copyright"
style="color: rgba(0, 0, 0, 0.45);font-size: 14px;"
>
Proudly power by Proudly power by
<router-link :to="{ name:'About' }"> <router-link :to="{ name: 'About' }">
Halo Halo
</router-link> </router-link>
</div> </div>

View File

@ -1,54 +1,30 @@
<template> <template>
<transition name="showHeader"> <transition name="showHeader">
<div <div v-if="visible" class="header-animat">
v-if="visible"
class="header-animat"
>
<a-layout-header <a-layout-header
v-if="visible" v-if="visible"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :class="[
fixedHeader && 'ant-header-fixedHeader',
sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed'
]"
style="padding:0" style="padding:0"
> >
<div <div v-if="mode === 'sidemenu'" class="header">
v-if="mode === 'sidemenu'"
class="header"
>
<a-icon <a-icon
v-if="device==='mobile'" v-if="device === 'mobile'"
class="trigger" class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'" :type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click="toggle" @click="toggle"
/> />
<a-icon <a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle" />
v-else
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="toggle"
/>
<user-menu></user-menu> <user-menu></user-menu>
</div> </div>
<div <div v-else :class="['top-nav-header-index', theme]">
v-else
:class="['top-nav-header-index', theme]"
>
<div class="header-index-wide"> <div class="header-index-wide">
<div class="header-index-left"> <div class="header-index-left">
<logo <logo class="top-nav-header" v-if="device !== 'mobile'" />
class="top-nav-header" <s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" />
v-if="device !== 'mobile'" <a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
/>
<s-menu
v-if="device !== 'mobile'"
mode="horizontal"
:menu="menus"
:theme="theme"
/>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click="toggle"
/>
</div> </div>
<user-menu class="header-index-right"></user-menu> <user-menu class="header-index-right"></user-menu>
</div> </div>

View File

@ -10,66 +10,41 @@
<a-form-model-item <a-form-model-item
v-if="!form.needAuthCode" v-if="!form.needAuthCode"
class="animated fadeInUp" class="animated fadeInUp"
:style="{'animation-delay': '0.1s'}" :style="{ 'animation-delay': '0.1s' }"
prop="username" prop="username"
> >
<a-input <a-input placeholder="用户名/邮箱" v-model="form.model.username">
placeholder="用户名/邮箱" <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
v-model="form.model.username"
>
<a-icon
slot="prefix"
type="user"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item
v-if="!form.needAuthCode" v-if="!form.needAuthCode"
class="animated fadeInUp" class="animated fadeInUp"
:style="{'animation-delay': '0.2s'}" :style="{ 'animation-delay': '0.2s' }"
prop="password" prop="password"
> >
<a-input <a-input v-model="form.model.password" type="password" placeholder="密码">
v-model="form.model.password" <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
type="password"
placeholder="密码"
>
<a-icon
slot="prefix"
type="lock"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item
v-if="form.needAuthCode" v-if="form.needAuthCode"
class="animated fadeInUp" class="animated fadeInUp"
:style="{'animation-delay': '0.1s'}" :style="{ 'animation-delay': '0.1s' }"
prop="authcode" prop="authcode"
> >
<a-input <a-input placeholder="两步验证码" v-model="form.model.authcode" :maxLength="6">
placeholder="两步验证码" <a-icon slot="prefix" type="safety-certificate" style="color: rgba(0,0,0,.25)" />
v-model="form.model.authcode"
:maxLength="6"
>
<a-icon
slot="prefix"
type="safety-certificate"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item class="animated fadeInUp" :style="{ 'animation-delay': '0.3s' }">
class="animated fadeInUp"
:style="{'animation-delay': '0.3s'}"
>
<a-button <a-button
:loading="form.logging" :loading="form.logging"
type="primary" type="primary"
:block="true" :block="true"
@click="form.needAuthCode ? handleLogin() : handleLoginClick()" @click="form.needAuthCode ? handleLogin() : handleLoginClick()"
>{{ buttonName }}</a-button> >{{ buttonName }}</a-button
>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</div> </div>
@ -92,33 +67,33 @@ export default {
model: { model: {
authcode: null, authcode: null,
password: null, password: null,
username: null, username: null
}, },
rules: { rules: {
username: [{ required: true, message: '* 用户名/邮箱不能为空', trigger: ['change'] }], username: [{ required: true, message: '* 用户名/邮箱不能为空', trigger: ['change'] }],
password: [{ required: true, message: '* 密码不能为空', trigger: ['change'] }], password: [{ required: true, message: '* 密码不能为空', trigger: ['change'] }],
authcode: [{ validator: authcodeValidate, trigger: ['change'] }], authcode: [{ validator: authcodeValidate, trigger: ['change'] }]
}, },
needAuthCode: false, needAuthCode: false,
logging: false, logging: false
}, }
} }
}, },
computed: { computed: {
buttonName() { buttonName() {
return this.form.needAuthCode ? '验证' : '登录' return this.form.needAuthCode ? '验证' : '登录'
}, }
}, },
methods: { methods: {
...mapActions(['login', 'refreshUserCache', 'refreshOptionsCache']), ...mapActions(['login', 'refreshUserCache', 'refreshOptionsCache']),
handleLoginClick() { handleLoginClick() {
const _this = this const _this = this
_this.$refs.loginForm.validate((valid) => { _this.$refs.loginForm.validate(valid => {
if (valid) { if (valid) {
_this.form.logging = true _this.form.logging = true
adminApi adminApi
.loginPreCheck(_this.form.model.username, _this.form.model.password) .loginPreCheck(_this.form.model.username, _this.form.model.password)
.then((response) => { .then(response => {
const data = response.data.data const data = response.data.data
if (data && data.needMFACode) { if (data && data.needMFACode) {
_this.form.needAuthCode = true _this.form.needAuthCode = true
@ -138,11 +113,11 @@ export default {
handleLogin() { handleLogin() {
const _this = this const _this = this
_this.form.logging = true _this.form.logging = true
_this.$refs.loginForm.validate((valid) => { _this.$refs.loginForm.validate(valid => {
if (valid) { if (valid) {
_this _this
.login(_this.form.model) .login(_this.form.model)
.then((response) => { .then(() => {
_this.$emit('success') _this.$emit('success')
}) })
.finally(() => { .finally(() => {
@ -152,7 +127,7 @@ export default {
}) })
} }
}) })
}, }
}, }
} }
</script> </script>

View File

@ -34,5 +34,4 @@ export default {
} }
} }
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,6 +1,6 @@
<template> <template>
<a-layout-sider <a-layout-sider
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]" :class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null]"
width="256px" width="256px"
:collapsible="collapsible" :collapsible="collapsible"
v-model="collapsed" v-model="collapsed"

View File

@ -1,32 +1,15 @@
<template> <template>
<div <div class="setting-drawer" ref="settingDrawer">
class="setting-drawer" <a-drawer width="300" closable @close="onClose" :visible="layoutSetting">
ref="settingDrawer"
>
<a-drawer
width="300"
closable
@close="onClose"
:visible="layoutSetting"
>
<div class="setting-drawer-index-content"> <div class="setting-drawer-index-content">
<div class="mb-6"> <div class="mb-6">
<h3 class="setting-drawer-index-title">整体风格设置</h3> <h3 class="setting-drawer-index-title">整体风格设置</h3>
<div class="setting-drawer-index-blockChecbox"> <div class="setting-drawer-index-blockChecbox">
<a-tooltip> <a-tooltip>
<template slot="title">暗色菜单风格</template> <template slot="title">暗色菜单风格</template>
<div <div class="setting-drawer-index-item" @click="handleMenuTheme('dark')">
class="setting-drawer-index-item" <img src="/images/dark.svg" alt="dark" />
@click="handleMenuTheme('dark')" <div class="setting-drawer-index-selectIcon" v-if="navTheme === 'dark'">
>
<img
src="/images/dark.svg"
alt="dark"
>
<div
class="setting-drawer-index-selectIcon"
v-if="navTheme === 'dark'"
>
<a-icon type="check" /> <a-icon type="check" />
</div> </div>
</div> </div>
@ -34,18 +17,9 @@
<a-tooltip> <a-tooltip>
<template slot="title">亮色菜单风格</template> <template slot="title">亮色菜单风格</template>
<div <div class="setting-drawer-index-item" @click="handleMenuTheme('light')">
class="setting-drawer-index-item" <img src="/images/dark.svg" alt="light" />
@click="handleMenuTheme('light')" <div class="setting-drawer-index-selectIcon" v-if="navTheme !== 'dark'">
>
<img
src="/images/dark.svg"
alt="light"
>
<div
class="setting-drawer-index-selectIcon"
v-if="navTheme !== 'dark'"
>
<a-icon type="check" /> <a-icon type="check" />
</div> </div>
</div> </div>
@ -56,20 +30,10 @@
<div class="mb-6"> <div class="mb-6">
<h3 class="setting-drawer-index-title">主题色</h3> <h3 class="setting-drawer-index-title">主题色</h3>
<div class="h-5"> <div class="h-5">
<a-tooltip <a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index">
class="setting-drawer-theme-color-colorBlock"
v-for="(item, index) in colorList"
:key="index"
>
<template slot="title">{{ item.key }}</template> <template slot="title">{{ item.key }}</template>
<a-tag <a-tag :color="item.color" @click="changeColor(item.color)">
:color="item.color" <a-icon type="check" v-if="item.color === primaryColor"></a-icon>
@click="changeColor(item.color)"
>
<a-icon
type="check"
v-if="item.color === primaryColor"
></a-icon>
</a-tag> </a-tag>
</a-tooltip> </a-tooltip>
</div> </div>
@ -79,34 +43,16 @@
<h3 class="setting-drawer-index-title">导航模式</h3> <h3 class="setting-drawer-index-title">导航模式</h3>
<div class="setting-drawer-index-blockChecbox"> <div class="setting-drawer-index-blockChecbox">
<div <div class="setting-drawer-index-item" @click="handleLayout('sidemenu')">
class="setting-drawer-index-item" <img src="/images/sidemenu.svg" alt="sidemenu" />
@click="handleLayout('sidemenu')" <div class="setting-drawer-index-selectIcon" v-if="layoutMode === 'sidemenu'">
>
<img
src="/images/sidemenu.svg"
alt="sidemenu"
>
<div
class="setting-drawer-index-selectIcon"
v-if="layoutMode === 'sidemenu'"
>
<a-icon type="check" /> <a-icon type="check" />
</div> </div>
</div> </div>
<div <div class="setting-drawer-index-item" @click="handleLayout('topmenu')">
class="setting-drawer-index-item" <img src="/images/topmenu.svg" alt="topmenu" />
@click="handleLayout('topmenu')" <div class="setting-drawer-index-selectIcon" v-if="layoutMode !== 'sidemenu'">
>
<img
src="/images/topmenu.svg"
alt="topmenu"
>
<div
class="setting-drawer-index-selectIcon"
v-if="layoutMode !== 'sidemenu'"
>
<a-icon type="check" /> <a-icon type="check" />
</div> </div>
</div> </div>
@ -127,10 +73,7 @@
@change="handleContentWidthChange" @change="handleContentWidthChange"
> >
<a-select-option value="Fixed">固定</a-select-option> <a-select-option value="Fixed">固定</a-select-option>
<a-select-option <a-select-option value="Fluid" v-if="layoutMode != 'sidemenu'"></a-select-option>
value="Fluid"
v-if="layoutMode != 'sidemenu'"
>流式</a-select-option>
</a-select> </a-select>
</a-tooltip> </a-tooltip>
<a-list-item-meta> <a-list-item-meta>
@ -138,12 +81,7 @@
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-switch <a-switch slot="actions" size="small" :defaultChecked="fixedHeader" @change="handleFixedHeader" />
slot="actions"
size="small"
:defaultChecked="fixedHeader"
@change="handleFixedHeader"
/>
<a-list-item-meta> <a-list-item-meta>
<div slot="title">固定 Header</div> <div slot="title">固定 Header</div>
</a-list-item-meta> </a-list-item-meta>
@ -157,10 +95,7 @@
@change="handleFixedHeaderHidden" @change="handleFixedHeaderHidden"
/> />
<a-list-item-meta> <a-list-item-meta>
<a-tooltip <a-tooltip slot="title" placement="left">
slot="title"
placement="left"
>
<template slot="title">固定 Header 时可配置</template> <template slot="title">固定 Header 时可配置</template>
<div :style="{ opacity: !fixedHeader ? '0.5' : '1' }">下滑时隐藏 Header</div> <div :style="{ opacity: !fixedHeader ? '0.5' : '1' }">下滑时隐藏 Header</div>
</a-tooltip> </a-tooltip>
@ -170,15 +105,12 @@
<a-switch <a-switch
slot="actions" slot="actions"
size="small" size="small"
:disabled="(layoutMode === 'topmenu')" :disabled="layoutMode === 'topmenu'"
:defaultChecked="fixSiderbar" :defaultChecked="fixSiderbar"
@change="handleFixSiderbar" @change="handleFixSiderbar"
/> />
<a-list-item-meta> <a-list-item-meta>
<div <div slot="title" :style="{ opacity: layoutMode === 'topmenu' ? '0.5' : '1' }">固定侧边菜单</div>
slot="title"
:style="{ opacity: (layoutMode==='topmenu') ? '0.5' : '1' }"
>固定侧边菜单</div>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -190,16 +122,12 @@
</template> </template>
<script> <script>
import SettingItem from '@/components/SettingDrawer/SettingItem'
import config from '@/config/defaultSettings' import config from '@/config/defaultSettings'
import { updateTheme, colorList } from './setting' import { updateTheme, colorList } from './setting'
import { mixin, mixinDevice } from '@/mixins/mixin' import { mixin, mixinDevice } from '@/mixins/mixin'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
export default { export default {
components: {
SettingItem
},
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
data() { data() {
return { return {

View File

@ -1,8 +1,5 @@
<template> <template>
<div <div class="head-info" :class="center && 'center'">
class="head-info"
:class="center && 'center'"
>
<span>{{ title }}</span> <span>{{ title }}</span>
<p>{{ content }}</p> <p>{{ content }}</p>
<em v-if="bordered" /> <em v-if="bordered" />

View File

@ -10,22 +10,10 @@
> >
<template slot="content"> <template slot="content">
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs <a-tabs v-model="activeKey" @change="handleTabsChanged">
v-model="activeKey" <a-tab-pane tab="文章" key="post">
@change="handleTabsChanged" <a-list :loading="postCommentsLoading" :dataSource="converttedPostComments">
> <a-list-item slot="renderItem" slot-scope="item">
<a-tab-pane
tab="文章"
key="post"
>
<a-list
:loading="postCommentsLoading"
:dataSource="converttedPostComments"
>
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta> <a-list-item-meta>
<a-avatar <a-avatar
class="bg-white" class="bg-white"
@ -34,10 +22,8 @@
size="large" size="large"
/> />
<template slot="title"> <template slot="title">
<a <a :href="item.authorUrl" target="_blank">{{ item.author }}</a
:href="item.authorUrl" ><span v-html="item.content"></span>
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template> </template>
<template slot="description"> <template slot="description">
{{ item.createTime | timeAgo }} {{ item.createTime | timeAgo }}
@ -46,18 +32,9 @@
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane tab="页面" key="sheet">
tab="页面" <a-list :loading="sheetCommentsLoading" :dataSource="converttedSheetComments">
key="sheet" <a-list-item slot="renderItem" slot-scope="item">
>
<a-list
:loading="sheetCommentsLoading"
:dataSource="converttedSheetComments"
>
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta> <a-list-item-meta>
<a-avatar <a-avatar
class="bg-white" class="bg-white"
@ -66,10 +43,8 @@
size="large" size="large"
/> />
<template slot="title"> <template slot="title">
<a <a :href="item.authorUrl" target="_blank">{{ item.author }}</a
:href="item.authorUrl" ><span v-html="item.content"></span>
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template> </template>
<template slot="description"> <template slot="description">
{{ item.createTime | timeAgo }} {{ item.createTime | timeAgo }}
@ -82,10 +57,7 @@
</div> </div>
</template> </template>
<span class="header-comment"> <span class="header-comment">
<a-badge <a-badge dot v-if="postComments.length > 0 || sheetComments.length > 0">
dot
v-if="postComments.length > 0 || sheetComments.length > 0"
>
<a-icon type="bell" /> <a-icon type="bell" />
</a-badge> </a-badge>
<a-badge v-else> <a-badge v-else>
@ -114,13 +86,13 @@ export default {
}, },
computed: { computed: {
converttedPostComments() { converttedPostComments() {
return this.postComments.map((comment) => { return this.postComments.map(comment => {
comment.content = marked(decodeHTML(comment.content)) comment.content = marked(decodeHTML(comment.content))
return comment return comment
}) })
}, },
converttedSheetComments() { converttedSheetComments() {
return this.sheetComments.map((comment) => { return this.sheetComments.map(comment => {
comment.content = marked(decodeHTML(comment.content)) comment.content = marked(decodeHTML(comment.content))
return comment return comment
}) })
@ -148,7 +120,7 @@ export default {
} }
commentApi commentApi
.latestComment('posts', 5, 'AUDITING') .latestComment('posts', 5, 'AUDITING')
.then((response) => { .then(response => {
this.postComments = response.data.data this.postComments = response.data.data
}) })
.finally(() => { .finally(() => {
@ -163,7 +135,7 @@ export default {
} }
commentApi commentApi
.latestComment('sheets', 5, 'AUDITING') .latestComment('sheets', 5, 'AUDITING')
.then((response) => { .then(response => {
this.sheetComments = response.data.data this.sheetComments = response.data.data
}) })
.finally(() => { .finally(() => {

View File

@ -4,5 +4,4 @@
<script> <script>
export default {} export default {}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped></style>
</style>

View File

@ -1,14 +1,8 @@
<template> <template>
<div class="logo"> <div class="logo">
<a <a href="javascript:void(0);" @click="onLogoClick()">
href="javascript:void(0);"
@click="onLogoClick()"
>
<h1 class="logo-title">Halo</h1> <h1 class="logo-title">Halo</h1>
<h1 <h1 class="logo-sub-title" style="padding-left: 10px;">Dashboard</h1>
class="logo-sub-title"
style="padding-left: 10px;"
>Dashboard</h1>
</a> </a>
</div> </div>
</template> </template>
@ -34,7 +28,7 @@ export default {
onLogoClick() { onLogoClick() {
this.clickCount++ this.clickCount++
if (this.clickCount === 10) { if (this.clickCount === 10) {
optionApi.save(this.optionsToCreate).then((response) => { optionApi.save(this.optionsToCreate).then(() => {
this.refreshOptionsCache() this.refreshOptionsCache()
this.$message.success(`开发者选项已启用!`) this.$message.success(`开发者选项已启用!`)
this.clickCount = 0 this.clickCount = 0

View File

@ -1,26 +1,14 @@
<template> <template>
<div class="user-wrapper"> <div class="user-wrapper">
<a <a :href="options.blog_url" target="_blank">
:href="options.blog_url" <a-tooltip placement="bottom" title="点击跳转到首页">
target="_blank"
>
<a-tooltip
placement="bottom"
title="点击跳转到首页"
>
<span class="action"> <span class="action">
<a-icon type="link" /> <a-icon type="link" />
</span> </span>
</a-tooltip> </a-tooltip>
</a> </a>
<a <a href="javascript:void(0)" @click="handleShowLayoutSetting">
href="javascript:void(0)" <a-tooltip placement="bottom" title="后台布局设置">
@click="handleShowLayoutSetting"
>
<a-tooltip
placement="bottom"
title="后台布局设置"
>
<span class="action"> <span class="action">
<a-icon type="setting" /> <a-icon type="setting" />
</span> </span>
@ -28,20 +16,10 @@
</a> </a>
<header-comment class="action" /> <header-comment class="action" />
<a-dropdown> <a-dropdown>
<span <span class="action ant-dropdown-link user-dropdown-menu" v-if="user">
class="action ant-dropdown-link user-dropdown-menu" <a-avatar class="avatar" size="small" :src="user.avatar || '//cn.gravatar.com/avatar/?s=256&d=mm'" />
v-if="user"
>
<a-avatar
class="avatar"
size="small"
:src="user.avatar || '//cn.gravatar.com/avatar/?s=256&d=mm'"
/>
</span> </span>
<a-menu <a-menu slot="overlay" class="user-dropdown-menu-wrapper">
slot="overlay"
class="user-dropdown-menu-wrapper"
>
<a-menu-item key="0"> <a-menu-item key="0">
<router-link :to="{ name: 'Profile' }"> <router-link :to="{ name: 'Profile' }">
<a-icon type="user" /> <a-icon type="user" />
@ -50,10 +28,7 @@
</a-menu-item> </a-menu-item>
<a-menu-divider /> <a-menu-divider />
<a-menu-item key="1"> <a-menu-item key="1">
<a <a href="javascript:;" @click="handleLogout">
href="javascript:;"
@click="handleLogout"
>
<a-icon type="logout" /> <a-icon type="logout" />
<span>退出登录</span> <span>退出登录</span>
</a> </a>
@ -89,7 +64,7 @@ export default {
.then(() => { .then(() => {
window.location.reload() window.location.reload()
}) })
.catch((err) => { .catch(err => {
that.$message.error({ that.$message.error({
title: '错误', title: '错误',
description: err.message description: err.message

View File

@ -42,45 +42,45 @@ const FilePond = vueFilePond(FilePondPluginImagePreview, FilePondPluginFileValid
export default { export default {
name: 'FilePondUpload', name: 'FilePondUpload',
components: { components: {
FilePond, FilePond
}, },
props: { props: {
name: { name: {
type: String, type: String,
required: false, required: false,
default: 'file', default: 'file'
}, },
filed: { filed: {
type: String, type: String,
required: false, required: false,
default: '', default: ''
}, },
multiple: { multiple: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, },
accepts: { accepts: {
type: Array, type: Array,
required: false, required: false,
default: () => { default: () => {
return [] return []
}, }
}, },
label: { label: {
type: String, type: String,
required: false, required: false,
default: '点击选择文件或将文件拖拽到此处', default: '点击选择文件或将文件拖拽到此处'
}, },
uploadHandler: { uploadHandler: {
type: Function, type: Function,
required: true, required: true
}, },
loadOptions: { loadOptions: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options']),
@ -101,7 +101,7 @@ export default {
return this.options.attachment_upload_max_files return this.options.attachment_upload_max_files
} }
return 1 return 1
}, }
}, },
data: function() { data: function() {
return { return {
@ -115,7 +115,7 @@ export default {
this.uploadHandler( this.uploadHandler(
formData, formData,
(progressEvent) => { progressEvent => {
if (progressEvent.total > 0) { if (progressEvent.total > 0) {
progress(progressEvent.lengthComputable, progressEvent.loaded, progressEvent.total) progress(progressEvent.lengthComputable, progressEvent.loaded, progressEvent.total)
} }
@ -124,12 +124,12 @@ export default {
this.filed, this.filed,
file file
) )
.then((response) => { .then(response => {
load(response) load(response)
this.$log.debug('Uploaded successfully', response) this.$log.debug('Uploaded successfully', response)
this.$emit('success', response, file) this.$emit('success', response, file)
}) })
.catch((failure) => { .catch(failure => {
this.$log.debug('Failed to upload file', failure) this.$log.debug('Failed to upload file', failure)
this.$emit('failure', failure, file) this.$emit('failure', failure, file)
error() error()
@ -139,11 +139,11 @@ export default {
abort() abort()
this.$log.debug('Upload operation aborted by the user') this.$log.debug('Upload operation aborted by the user')
source.cancel('Upload operation canceled by the user.') source.cancel('Upload operation canceled by the user.')
}, }
} }
}, }
}, },
fileList: [], fileList: []
} }
}, },
methods: { methods: {
@ -152,7 +152,7 @@ export default {
}, },
handleClearFileList() { handleClearFileList() {
this.$refs.pond.removeFiles() this.$refs.pond.removeFiles()
}, }
}, }
} }
</script> </script>

View File

@ -96,7 +96,7 @@ export default {
computed: { computed: {
...mapState({ ...mapState({
// //
mainMenu: (state) => state.permission.addRouters mainMenu: state => state.permission.addRouters
}), }),
contentPaddingLeft() { contentPaddingLeft() {
if (!this.fixSidebar || this.isMobile()) { if (!this.fixSidebar || this.isMobile()) {
@ -114,7 +114,7 @@ export default {
} }
}, },
created() { 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.menus = this.mainMenu.find((item) => item.path === '/').children
this.collapsed = !this.sidebarOpened this.collapsed = !this.sidebarOpened
}, },

View File

@ -5,7 +5,6 @@
</template> </template>
<script> <script>
export default { export default {
name: 'BlankLayout' name: 'BlankLayout'
} }

View File

@ -1,50 +1,20 @@
<template> <template>
<div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null"> <div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null">
<a-affix v-if="affix"> <a-affix v-if="affix">
<div <div class="page-header" v-if="!$route.meta.hiddenHeaderContent">
class="page-header"
v-if="!$route.meta.hiddenHeaderContent"
>
<div class="page-header-index-wide"> <div class="page-header-index-wide">
<a-page-header <a-page-header :title="title" :sub-title="subTitle" :breadcrumb="{ props: { routes: breadList } }">
:title="title" <slot name="extra" slot="extra"> </slot>
:sub-title="subTitle" <slot name="footer" slot="footer"> </slot>
:breadcrumb="{ props: { routes:breadList } }"
>
<slot
name="extra"
slot="extra"
>
</slot>
<slot
name="footer"
slot="footer"
>
</slot>
</a-page-header> </a-page-header>
</div> </div>
</div> </div>
</a-affix> </a-affix>
<div <div class="page-header" v-if="!$route.meta.hiddenHeaderContent && !affix">
class="page-header"
v-if="!$route.meta.hiddenHeaderContent && !affix"
>
<div class="page-header-index-wide"> <div class="page-header-index-wide">
<a-page-header <a-page-header :title="title" :sub-title="subTitle" :breadcrumb="{ props: { routes: breadList } }">
:title="title" <slot name="extra" slot="extra"> </slot>
:sub-title="subTitle" <slot name="footer" slot="footer"> </slot>
:breadcrumb="{ props: { routes:breadList } }"
>
<slot
name="extra"
slot="extra"
>
</slot>
<slot
name="footer"
slot="footer"
>
</slot>
</a-page-header> </a-page-header>
</div> </div>
</div> </div>
@ -64,20 +34,20 @@ export default {
props: { props: {
title: { title: {
type: String, type: String,
default: null, default: null
}, },
subTitle: { subTitle: {
type: String, type: String,
default: null, default: null
}, },
affix: { affix: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
data() { data() {
return { return {
breadList: [], breadList: []
} }
}, },
created() { created() {
@ -86,17 +56,17 @@ export default {
watch: { watch: {
$route() { $route() {
this.getBreadcrumb() this.getBreadcrumb()
}, }
}, },
methods: { methods: {
getBreadcrumb() { getBreadcrumb() {
this.breadList = [] this.breadList = []
this.$route.matched.forEach((item) => { this.$route.matched.forEach(item => {
item.breadcrumbName = item.meta.title item.breadcrumbName = item.meta.title
this.breadList.push(item) this.breadList.push(item)
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,4 +1,3 @@
import '@babel/polyfill'
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'

View File

@ -3,14 +3,14 @@ import router from '@/router'
import store from '@/store' import store from '@/store'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import { setDocumentTitle, domTitle } from '@/utils/domUtil' import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import adminApi from '@api/admin' import adminApi from '@/api/admin'
NProgress.configure({ showSpinner: false, speed: 500 }) NProgress.configure({ showSpinner: false, speed: 500 })
const whiteList = ['Login', 'Install', 'NotFound', 'ResetPassword'] // no redirect whitelist const whiteList = ['Login', 'Install', 'NotFound', 'ResetPassword'] // no redirect whitelist
let progressTimer = null let progressTimer = null
router.beforeEach(async(to, from, next) => { router.beforeEach(async (to, from, next) => {
onProgressTimerDone() onProgressTimerDone()
progressTimer = setTimeout(() => { progressTimer = setTimeout(() => {
NProgress.start() NProgress.start()

View File

@ -1,9 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
import { import { constantRouterMap, asyncRouterMap } from '@/config/router.config'
constantRouterMap,
asyncRouterMap
} from '@/config/router.config'
Vue.use(Router) Vue.use(Router)

View File

@ -16,14 +16,8 @@ export default new Vuex.Store({
permission, permission,
option option
}, },
state: { state: {},
mutations: {},
}, actions: {},
mutations: {
},
actions: {
},
getters getters
}) })

View File

@ -1,8 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import { import { ACCESS_TOKEN, USER } from '@/store/mutation-types'
ACCESS_TOKEN,
USER
} from '@/store/mutation-types'
import adminApi from '@/api/admin' import adminApi from '@/api/admin'
import userApi from '@/api/user' import userApi from '@/api/user'
@ -26,9 +23,7 @@ const user = {
} }
}, },
actions: { actions: {
installCleanToken({ installCleanToken({ commit }, installData) {
commit
}, installData) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
adminApi adminApi
.install(installData) .install(installData)
@ -41,9 +36,7 @@ const user = {
}) })
}) })
}, },
refreshUserCache({ refreshUserCache({ commit }) {
commit
}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
userApi userApi
.getProfile() .getProfile()
@ -56,13 +49,7 @@ const user = {
}) })
}) })
}, },
login({ login({ commit }, { username, password, authcode }) {
commit
}, {
username,
password,
authcode
}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
adminApi adminApi
.login(username, password, authcode) .login(username, password, authcode)
@ -78,13 +65,11 @@ const user = {
}) })
}) })
}, },
logout({ logout({ commit }) {
commit
}) {
return new Promise(resolve => { return new Promise(resolve => {
adminApi adminApi
.logout() .logout()
.then(response => { .then(() => {
commit('CLEAR_TOKEN') commit('CLEAR_TOKEN')
resolve() resolve()
}) })
@ -93,9 +78,7 @@ const user = {
}) })
}) })
}, },
refreshToken({ refreshToken({ commit }, refreshToken) {
commit
}, refreshToken) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
adminApi adminApi
.refreshToken(refreshToken) .refreshToken(refreshToken)

View File

@ -1,36 +1,17 @@
<template> <template>
<page-view> <page-view>
<a-row <a-row :gutter="12" type="flex" align="middle">
:gutter="12" <a-col :span="24" class="pb-3">
type="flex" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
align="middle"
>
<a-col
:span="24"
class="pb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input v-model="queryParam.keyword" @keyup.enter="handleQuery()" />
v-model="queryParam.keyword"
@keyup.enter="handleQuery()"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="存储位置:"> <a-form-item label="存储位置:">
<a-select <a-select
v-model="queryParam.attachmentType" v-model="queryParam.attachmentType"
@ -38,20 +19,13 @@
:loading="typesLoading" :loading="typesLoading"
allowClear allowClear
> >
<a-select-option <a-select-option v-for="item in types" :key="item" :value="item">{{
v-for="item in types"
:key="item"
:value="item"
>{{
attachmentType[item].text attachmentType[item].text
}}</a-select-option> }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="文件类型:"> <a-form-item label="文件类型:">
<a-select <a-select
v-model="queryParam.mediaType" v-model="queryParam.mediaType"
@ -59,26 +33,16 @@
:loading="mediaTypesLoading" :loading="mediaTypesLoading"
allowClear allowClear
> >
<a-select-option <a-select-option v-for="(item, index) in mediaTypes" :key="index" :value="item">{{
v-for="(item, index) in mediaTypes"
:key="index"
:value="item"
>{{
item item
}}</a-select-option> }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -86,17 +50,9 @@
</a-row> </a-row>
</a-form> </a-form>
</div> </div>
<div class="table-operator mb-0"> <div class="mb-0 table-operator">
<a-button <a-button type="primary" icon="cloud-upload" @click="() => (uploadVisible = true)">上传</a-button>
type="primary" <a-button icon="select" v-show="!supportMultipleSelection" @click="handleMultipleSelection">
icon="cloud-upload"
@click="() => (uploadVisible = true)"
>上传</a-button>
<a-button
icon="select"
v-show="!supportMultipleSelection"
@click="handleMultipleSelection"
>
批量操作 批量操作
</a-button> </a-button>
<a-button <a-button
@ -107,11 +63,7 @@
> >
删除 删除
</a-button> </a-button>
<a-button <a-button icon="close" v-show="supportMultipleSelection" @click="handleCancelMultipleSelection">
icon="close"
v-show="supportMultipleSelection"
@click="handleCancelMultipleSelection"
>
取消 取消
</a-button> </a-button>
</div> </div>
@ -123,11 +75,7 @@
:dataSource="formattedDatas" :dataSource="formattedDatas"
:loading="listLoading" :loading="listLoading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-card <a-card
:bodyStyle="{ padding: 0 }" :bodyStyle="{ padding: 0 }"
hoverable hoverable
@ -136,18 +84,10 @@
> >
<div class="attach-thumb"> <div class="attach-thumb">
<span v-show="!handleJudgeMediaType(item)"></span> <span v-show="!handleJudgeMediaType(item)"></span>
<img <img :src="item.thumbPath" v-show="handleJudgeMediaType(item)" loading="lazy" />
:src="item.thumbPath"
v-show="handleJudgeMediaType(item)"
loading="lazy"
/>
</div> </div>
<a-card-meta class="p-3"> <a-card-meta class="p-3">
<ellipsis <ellipsis :length="isMobile() ? 12 : 16" tooltip slot="description">{{ item.name }}</ellipsis>
:length="isMobile() ? 12 : 16"
tooltip
slot="description"
>{{ item.name }}</ellipsis>
</a-card-meta> </a-card-meta>
<a-checkbox <a-checkbox
class="select-attachment-checkbox" class="select-attachment-checkbox"
@ -174,17 +114,8 @@
showLessItems showLessItems
/> />
</div> </div>
<a-modal <a-modal title="上传附件" v-model="uploadVisible" :footer="null" :afterClose="onUploadClose" destroyOnClose>
title="上传附件" <FilePondUpload ref="upload" :uploadHandler="uploadHandler"></FilePondUpload>
v-model="uploadVisible"
:footer="null"
:afterClose="onUploadClose"
destroyOnClose
>
<FilePondUpload
ref="upload"
:uploadHandler="uploadHandler"
></FilePondUpload>
</a-modal> </a-modal>
<AttachmentDetailDrawer <AttachmentDetailDrawer
v-model="drawerVisible" v-model="drawerVisible"
@ -448,7 +379,7 @@ export default {
onOk() { onOk() {
attachmentApi attachmentApi
.deleteInBatch(that.batchSelectedAttachments) .deleteInBatch(that.batchSelectedAttachments)
.then(res => { .then(() => {
that.handleCancelMultipleSelection() that.handleCancelMultipleSelection()
that.$message.success('删除成功') that.$message.success('删除成功')
}) })

View File

@ -1,36 +1,20 @@
<template> <template>
<a-drawer <a-drawer
title="附件详情" title="附件详情"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<div class="attach-detail-img"> <div class="attach-detail-img">
<div v-show="nonsupportPreviewVisible"></div> <div v-show="nonsupportPreviewVisible"></div>
<a <a :href="attachment.path" target="_blank">
:href="attachment.path" <img :src="attachment.path" v-show="photoPreviewVisible" class="w-full" loading="lazy" />
target="_blank"
>
<img
:src="attachment.path"
v-show="photoPreviewVisible"
class="w-full"
loading="lazy"
>
</a> </a>
<d-player <d-player ref="player" :options="videoOptions" v-show="videoPreviewVisible" class="w-full video-player-box">
ref="player"
:options="videoOptions"
v-show="videoPreviewVisible"
class="video-player-box w-full"
>
</d-player> </d-player>
</div> </div>
</a-col> </a-col>
@ -39,27 +23,14 @@
<a-list itemLayout="horizontal"> <a-list itemLayout="horizontal">
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<template <template slot="description" v-if="editable">
slot="description" <a-input ref="nameInput" v-model="attachment.name" @blur="doUpdateAttachment" />
v-if="editable"
>
<a-input
ref="nameInput"
v-model="attachment.name"
@blur="doUpdateAttachment"
/>
</template> </template>
<template <template slot="description" v-else>{{ attachment.name }}</template>
slot="description"
v-else
>{{ attachment.name }}</template>
<span slot="title"> <span slot="title">
附件名 附件名
<a href="javascript:void(0);"> <a href="javascript:void(0);">
<a-icon <a-icon type="edit" @click="handleEditName" />
type="edit"
@click="handleEditName"
/>
</a> </a>
</span> </span>
</a-list-item-meta> </a-list-item-meta>
@ -83,7 +54,7 @@
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<a-list-item v-if="photoPreviewVisible"> <a-list-item v-if="photoPreviewVisible">
<a-list-item-meta :description="attachment.height+'x'+attachment.width"> <a-list-item-meta :description="attachment.height + 'x' + attachment.width">
<span slot="title">图片尺寸</span> <span slot="title">图片尺寸</span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
@ -99,10 +70,7 @@
<a-list-item-meta :description="attachment.path"> <a-list-item-meta :description="attachment.path">
<span slot="title"> <span slot="title">
普通链接 普通链接
<a <a href="javascript:void(0);" @click="handleCopyNormalLink">
href="javascript:void(0);"
@click="handleCopyNormalLink"
>
<a-icon type="copy" /> <a-icon type="copy" />
</a> </a>
</span> </span>
@ -113,10 +81,7 @@
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span> <span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
<span slot="title"> <span slot="title">
Markdown 格式 Markdown 格式
<a <a href="javascript:void(0);" @click="handleCopyMarkdownLink">
href="javascript:void(0);"
@click="handleCopyMarkdownLink"
>
<a-icon type="copy" /> <a-icon type="copy" />
</a> </a>
</span> </span>
@ -137,12 +102,7 @@
> >
<a-button type="dashed">添加到图库</a-button> <a-button type="dashed">添加到图库</a-button>
</a-popconfirm> </a-popconfirm>
<a-popconfirm <a-popconfirm title="你确定要删除该附件?" @confirm="handleDeleteAttachment" okText="确定" cancelText="取消">
title="你确定要删除该附件?"
@confirm="handleDeleteAttachment"
okText="确定"
cancelText="取消"
>
<ReactiveButton <ReactiveButton
type="danger" type="danger"
@callback="handleDeletedCallback" @callback="handleDeletedCallback"
@ -171,7 +131,7 @@ export default {
name: 'AttachmentDetailDrawer', name: 'AttachmentDetailDrawer',
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
'd-player': VueDPlayer, 'd-player': VueDPlayer
}, },
data() { data() {
return { return {
@ -187,40 +147,40 @@ export default {
lang: 'zh-cn', lang: 'zh-cn',
video: { video: {
url: '', url: '',
type: 'auto', type: 'auto'
}, }
}, }
} }
}, },
model: { model: {
prop: 'visible', prop: 'visible',
event: 'close', event: 'close'
}, },
props: { props: {
attachment: { attachment: {
type: Object, type: Object,
required: true, required: true
}, },
addToPhoto: { addToPhoto: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false
}, },
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
mounted() { mounted() {
this.player = this.$refs.player this.player = this.$refs.player
}, },
watch: { watch: {
attachment: function(newValue, oldValue) { attachment(newValue) {
if (newValue) { if (newValue) {
this.handleJudgeMediaType(newValue) this.handleJudgeMediaType(newValue)
} }
}, }
}, },
methods: { methods: {
handleDeleteAttachment() { handleDeleteAttachment() {
@ -253,11 +213,11 @@ export default {
if (!this.attachment.name) { if (!this.attachment.name) {
this.$notification['error']({ this.$notification['error']({
message: '提示', message: '提示',
description: '附件名称不能为空!', description: '附件名称不能为空!'
}) })
return return
} }
attachmentApi.update(this.attachment.id, this.attachment).then((response) => { attachmentApi.update(this.attachment.id, this.attachment).then(response => {
this.$log.debug('Updated attachment', response.data.data) this.$log.debug('Updated attachment', response.data.data)
}) })
this.editable = false this.editable = false
@ -265,11 +225,11 @@ export default {
handleCopyNormalLink() { handleCopyNormalLink() {
const text = `${encodeURI(this.attachment.path)}` const text = `${encodeURI(this.attachment.path)}`
this.$copyText(text) this.$copyText(text)
.then((message) => { .then(message => {
this.$log.debug('copy', message) this.$log.debug('copy', message)
this.$message.success('复制成功!') this.$message.success('复制成功!')
}) })
.catch((err) => { .catch(err => {
this.$log.debug('copy.err', err) this.$log.debug('copy.err', err)
this.$message.error('复制失败!') this.$message.error('复制失败!')
}) })
@ -277,11 +237,11 @@ export default {
handleCopyMarkdownLink() { handleCopyMarkdownLink() {
const text = `![${this.attachment.name}](${encodeURI(this.attachment.path)})` const text = `![${this.attachment.name}](${encodeURI(this.attachment.path)})`
this.$copyText(text) this.$copyText(text)
.then((message) => { .then(message => {
this.$log.debug('copy', message) this.$log.debug('copy', message)
this.$message.success('复制成功!') this.$message.success('复制成功!')
}) })
.catch((err) => { .catch(err => {
this.$log.debug('copy.err', err) this.$log.debug('copy.err', err)
this.$message.error('复制失败!') this.$message.error('复制失败!')
}) })
@ -291,7 +251,7 @@ export default {
this.photo['thumbnail'] = encodeURI(this.attachment.thumbPath) this.photo['thumbnail'] = encodeURI(this.attachment.thumbPath)
this.photo['url'] = encodeURI(this.attachment.path) this.photo['url'] = encodeURI(this.attachment.path)
this.photo['takeTime'] = new Date().getTime() this.photo['takeTime'] = new Date().getTime()
photoApi.create(this.photo).then((response) => { photoApi.create(this.photo).then(() => {
this.$message.success('添加成功!') this.$message.success('添加成功!')
this.photo = {} this.photo = {}
}) })
@ -328,7 +288,7 @@ export default {
this.$set(this, 'photoPreviewVisible', photo) this.$set(this, 'photoPreviewVisible', photo)
this.$set(this, 'videoPreviewVisible', video) this.$set(this, 'videoPreviewVisible', video)
this.$set(this, 'nonsupportPreviewVisible', nonsupport) this.$set(this, 'nonsupportPreviewVisible', nonsupport)
}, }
}, }
} }
</script> </script>

View File

@ -2,32 +2,21 @@
<div> <div>
<a-drawer <a-drawer
title="附件库" title="附件库"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex" <a-input-search placeholder="搜索附件" v-model="queryParam.keyword" @search="handleQuery()" enterButton />
align="middle"
>
<a-input-search
placeholder="搜索附件"
v-model="queryParam.keyword"
@search="handleQuery()"
enterButton
/>
</a-row> </a-row>
<a-divider /> <a-divider />
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<a-empty v-if="formattedDatas.length==0" /> <a-empty v-if="formattedDatas.length == 0" />
<div <div
v-else v-else
class="attach-item" class="attach-item"
@ -37,11 +26,7 @@
@contextmenu.prevent="handleContextMenu($event, item)" @contextmenu.prevent="handleContextMenu($event, item)"
> >
<span v-show="!handleJudgeMediaType(item)"></span> <span v-show="!handleJudgeMediaType(item)"></span>
<img <img :src="item.thumbPath" v-show="handleJudgeMediaType(item)" loading="lazy" />
:src="item.thumbPath"
v-show="handleJudgeMediaType(item)"
loading="lazy"
>
</div> </div>
</a-spin> </a-spin>
</a-col> </a-col>
@ -65,24 +50,12 @@
/> />
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-button <a-button @click="uploadVisible = true" type="primary">上传附件</a-button>
@click="uploadVisible = true"
type="primary"
>上传附件</a-button>
</div> </div>
</a-drawer> </a-drawer>
<a-modal <a-modal title="上传附件" v-model="uploadVisible" :footer="null" :afterClose="onUploadClose" destroyOnClose>
title="上传附件" <FilePondUpload ref="upload" :uploadHandler="uploadHandler"></FilePondUpload>
v-model="uploadVisible"
:footer="null"
:afterClose="onUploadClose"
destroyOnClose
>
<FilePondUpload
ref="upload"
:uploadHandler="uploadHandler"
></FilePondUpload>
</a-modal> </a-modal>
</div> </div>
</template> </template>

View File

@ -2,16 +2,10 @@
<page-view> <page-view>
<div class="card-container"> <div class="card-container">
<a-tabs type="card"> <a-tabs type="card">
<a-tab-pane <a-tab-pane key="1" tab="文章">
key="1"
tab="文章"
>
<comment-tab type="posts"></comment-tab> <comment-tab type="posts"></comment-tab>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="2" tab="页面">
key="2"
tab="页面"
>
<comment-tab type="sheets"></comment-tab> <comment-tab type="sheets"></comment-tab>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>

View File

@ -1,16 +1,13 @@
<template> <template>
<a-drawer <a-drawer
title="评论详情" title="评论详情"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-list itemLayout="horizontal"> <a-list itemLayout="horizontal">
<a-list-item> <a-list-item>
@ -30,66 +27,36 @@
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<a <a slot="description" target="_blank" :href="comment.authorUrl">{{ comment.authorUrl }}</a>
slot="description"
target="_blank"
:href="comment.authorUrl"
>{{ comment.authorUrl }}</a>
<span slot="title">评论者网址</span> <span slot="title">评论者网址</span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<span slot="description"> <span slot="description">
<a-badge <a-badge :status="comment.statusProperty.status" :text="comment.statusProperty.text" />
:status="comment.statusProperty.status"
:text="comment.statusProperty.text"
/>
</span> </span>
<span slot="title">评论状态</span> <span slot="title">评论状态</span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<a <a slot="description" target="_blank" :href="comment.post.fullPath" v-if="this.type == 'posts'">{{
slot="description" comment.post.title
target="_blank" }}</a>
:href="comment.post.fullPath" <a slot="description" target="_blank" :href="comment.sheet.fullPath" v-else-if="this.type == 'sheets'">{{
v-if="this.type=='posts'" comment.sheet.title
>{{ comment.post.title }}</a> }}</a>
<a <span slot="title" v-if="this.type == 'posts'"></span>
slot="description" <span slot="title" v-else-if="this.type == 'sheets'">评论页面</span>
target="_blank"
:href="comment.sheet.fullPath"
v-else-if="this.type=='sheets'"
>{{ comment.sheet.title }}</a>
<span
slot="title"
v-if="this.type=='posts'"
>评论文章</span>
<span
slot="title"
v-else-if="this.type=='sheets'"
>评论页面</span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<template <template slot="description" v-if="editable">
slot="description" <a-input type="textarea" :autoSize="{ minRows: 5 }" v-model="comment.content" />
v-if="editable"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="comment.content"
/>
</template> </template>
<span <span slot="description" v-html="comment.content" v-else></span>
slot="description"
v-html="comment.content"
v-else
></span>
<span slot="title">评论内容</span> <span slot="title">评论内容</span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
@ -99,21 +66,9 @@
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-space> <a-space>
<a-button <a-button type="dashed" @click="handleEditComment" v-if="!editable"></a-button>
type="dashed" <a-button type="primary" @click="handleUpdateComment" v-if="editable"></a-button>
@click="handleEditComment" <a-popconfirm title="你确定要将此评论者加入黑名单?" okText="确定" cancelText="取消">
v-if="!editable"
>编辑</a-button>
<a-button
type="primary"
@click="handleUpdateComment"
v-if="editable"
>保存</a-button>
<a-popconfirm
title="你确定要将此评论者加入黑名单?"
okText="确定"
cancelText="取消"
>
<a-button type="danger">加入黑名单</a-button> <a-button type="danger">加入黑名单</a-button>
</a-popconfirm> </a-popconfirm>
</a-space> </a-space>

View File

@ -1,53 +1,28 @@
<template> <template>
<div class="comment-tab-wrapper"> <div class="comment-tab-wrapper">
<a-card <a-card :bordered="false" :bodyStyle="{ padding: 0 }">
:bordered="false"
:bodyStyle="{ padding: 0 }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input v-model="queryParam.keyword" @keyup.enter="handleQuery()" />
v-model="queryParam.keyword"
@keyup.enter="handleQuery()"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="评论状态:"> <a-form-item label="评论状态:">
<a-select <a-select v-model="queryParam.status" placeholder="请选择评论状态" @change="handleQuery()" allowClear>
v-model="queryParam.status" <a-select-option v-for="status in Object.keys(commentStatus)" :key="status" :value="status">{{
placeholder="请选择评论状态" commentStatus[status].text
@change="handleQuery()" }}</a-select-option>
allowClear
>
<a-select-option
v-for="status in Object.keys(commentStatus)"
:key="status"
:value="status"
>{{ commentStatus[status].text }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="12" :sm="24">
:md="12"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -57,38 +32,20 @@
</div> </div>
<div class="table-operator"> <div class="table-operator">
<a-dropdown v-show="queryParam.status!=null && queryParam.status!='' && !isMobile()"> <a-dropdown v-show="queryParam.status != null && queryParam.status != '' && !isMobile()">
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item key="1" v-if="queryParam.status === 'AUDITING'">
key="1" <a href="javascript:void(0);" @click="handleEditStatusMore(commentStatus.PUBLISHED.value)">
v-if="queryParam.status ==='AUDITING'"
>
<a
href="javascript:void(0);"
@click="handleEditStatusMore(commentStatus.PUBLISHED.value)"
>
通过 通过
</a> </a>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="2" v-if="queryParam.status === 'PUBLISHED' || queryParam.status === 'AUDITING'">
key="2" <a href="javascript:void(0);" @click="handleEditStatusMore(commentStatus.RECYCLE.value)">
v-if="queryParam.status === 'PUBLISHED' || queryParam.status ==='AUDITING'"
>
<a
href="javascript:void(0);"
@click="handleEditStatusMore(commentStatus.RECYCLE.value)"
>
移到回收站 移到回收站
</a> </a>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="3" v-if="queryParam.status === 'RECYCLE'">
key="3" <a href="javascript:void(0);" @click="handleDeleteMore">
v-if="queryParam.status === 'RECYCLE'"
>
<a
href="javascript:void(0);"
@click="handleDeleteMore"
>
永久删除 永久删除
</a> </a>
</a-menu-item> </a-menu-item>
@ -109,42 +66,26 @@
:dataSource="formattedComments" :dataSource="formattedComments"
:loading="loading" :loading="loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<a-dropdown <a-dropdown placement="topLeft" :trigger="['click']">
placement="topLeft"
:trigger="['click']"
>
<span> <span>
<a-icon type="bars" /> <a-icon type="bars" />
</span> </span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item v-if="item.status === 'AUDITING'"> <a-menu-item v-if="item.status === 'AUDITING'">
<a <a href="javascript:;" @click="handleEditStatusClick(item.id, 'PUBLISHED')">通过</a>
href="javascript:;"
@click="handleEditStatusClick(item.id,'PUBLISHED')"
>通过</a>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="item.status === 'AUDITING'"> <a-menu-item v-if="item.status === 'AUDITING'">
<a <a href="javascript:;" @click="handleReplyAndPassClick(item)"></a>
href="javascript:;"
@click="handleReplyAndPassClick(item)"
>通过并回复</a>
</a-menu-item> </a-menu-item>
<a-menu-item v-else-if="item.status === 'PUBLISHED'"> <a-menu-item v-else-if="item.status === 'PUBLISHED'">
<a <a href="javascript:;" @click="handleReplyClick(item)"></a>
href="javascript:;"
@click="handleReplyClick(item)"
>回复</a>
</a-menu-item> </a-menu-item>
<a-menu-item v-else-if="item.status === 'RECYCLE'"> <a-menu-item v-else-if="item.status === 'RECYCLE'">
<a-popconfirm <a-popconfirm
:title="'你确定要还原该评论?'" :title="'你确定要还原该评论?'"
@confirm="handleEditStatusClick(item.id,'PUBLISHED')" @confirm="handleEditStatusClick(item.id, 'PUBLISHED')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
@ -154,7 +95,7 @@
<a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'AUDITING'"> <a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'AUDITING'">
<a-popconfirm <a-popconfirm
:title="'你确定要将该评论移到回收站?'" :title="'你确定要将该评论移到回收站?'"
@confirm="handleEditStatusClick(item.id,'RECYCLE')" @confirm="handleEditStatusClick(item.id, 'RECYCLE')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
@ -176,45 +117,23 @@
</template> </template>
<template slot="extra"> <template slot="extra">
<span> <span>
<a-badge <a-badge :status="item.statusProperty.status" :text="item.statusProperty.text" />
:status="item.statusProperty.status"
:text="item.statusProperty.text"
/>
</span> </span>
</template> </template>
<a-list-item-meta> <a-list-item-meta>
<template slot="description"> <template slot="description">
发表在 发表在
<a <a v-if="type === 'posts'" :href="item.post.fullPath" target="_blank">{{ item.post.title }}</a>
v-if="type==='posts'" <a v-if="type === 'sheets'" :href="item.sheet.fullPath" target="_blank">{{ item.sheet.title }}</a>
:href="item.post.fullPath"
target="_blank"
>{{ item.post.title }}</a>
<a
v-if="type === 'sheets'"
:href="item.sheet.fullPath"
target="_blank"
>{{ item.sheet.title }}</a>
</template> </template>
<a-avatar <a-avatar slot="avatar" size="large" :src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'" />
slot="avatar"
size="large"
:src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'"
/>
<span <span
slot="title" slot="title"
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;" style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
v-if="item.authorUrl" v-if="item.authorUrl"
> >
<a-icon <a-icon type="user" v-if="item.isAdmin" style="margin-right: 3px;" />&nbsp;
type="user" <a :href="item.authorUrl" target="_blank">{{ item.author }}</a>
v-if="item.isAdmin"
style="margin-right: 3px;"
/>&nbsp;
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a>
&nbsp;<small style="color:rgba(0, 0, 0, 0.45)">{{ item.createTime | timeAgo }}</small> &nbsp;<small style="color:rgba(0, 0, 0, 0.45)">{{ item.createTime | timeAgo }}</small>
</span> </span>
<span <span
@ -222,11 +141,10 @@
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;" style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
v-else v-else
> >
<a-icon <a-icon type="user" v-if="item.isAdmin" style="margin-right: 3px;" />&nbsp;{{ item.author }}&nbsp;<small
type="user" style="color:rgba(0, 0, 0, 0.45)"
v-if="item.isAdmin" >{{ item.createTime | timeAgo }}</small
style="margin-right: 3px;" >
/>&nbsp;{{ item.author }}&nbsp;<small style="color:rgba(0, 0, 0, 0.45)">{{ item.createTime | timeAgo }}</small>
</span> </span>
</a-list-item-meta> </a-list-item-meta>
<p v-html="item.content"></p> <p v-html="item.content"></p>
@ -247,56 +165,22 @@
:pagination="false" :pagination="false"
scrollToFirstRowOnChange scrollToFirstRowOnChange
> >
<template <template slot="author" slot-scope="text, record">
slot="author" <a-icon type="user" v-if="record.isAdmin" style="margin-right: 3px;" />
slot-scope="text,record" <a :href="record.authorUrl" target="_blank" v-if="record.authorUrl">{{ text }}</a>
>
<a-icon
type="user"
v-if="record.isAdmin"
style="margin-right: 3px;"
/>
<a
:href="record.authorUrl"
target="_blank"
v-if="record.authorUrl"
>{{ text }}</a>
<span v-else>{{ text }}</span> <span v-else>{{ text }}</span>
</template> </template>
<p <p class="comment-content-wrapper" slot="content" slot-scope="content" v-html="content"></p>
class="comment-content-wrapper" <span slot="status" slot-scope="statusProperty">
slot="content" <a-badge :status="statusProperty.status" :text="statusProperty.text" />
slot-scope="content"
v-html="content"
>
</p>
<span
slot="status"
slot-scope="statusProperty"
>
<a-badge
:status="statusProperty.status"
:text="statusProperty.text"
/>
</span> </span>
<a <a v-if="type === 'posts'" slot="post" slot-scope="post" :href="post.fullPath" target="_blank">{{
v-if="type==='posts'" post.title
slot="post" }}</a>
slot-scope="post" <a v-if="type === 'sheets'" slot="sheet" slot-scope="sheet" :href="sheet.fullPath" target="_blank">{{
:href="post.fullPath" sheet.title
target="_blank" }}</a>
>{{ post.title }}</a> <span slot="createTime" slot-scope="createTime">
<a
v-if="type === 'sheets'"
slot="sheet"
slot-scope="sheet"
:href="sheet.fullPath"
target="_blank"
>{{ sheet.title }}</a>
<span
slot="createTime"
slot-scope="createTime"
>
<a-tooltip placement="top"> <a-tooltip placement="top">
<template slot="title"> <template slot="title">
{{ createTime | moment }} {{ createTime | moment }}
@ -304,44 +188,26 @@
{{ createTime | timeAgo }} {{ createTime | timeAgo }}
</a-tooltip> </a-tooltip>
</span> </span>
<span <span slot="action" slot-scope="text, record">
slot="action" <a-dropdown :trigger="['click']" v-if="record.status === 'AUDITING'">
slot-scope="text, record" <a href="javascript:void(0);" class="ant-dropdown-link">通过</a>
>
<a-dropdown
:trigger="['click']"
v-if="record.status === 'AUDITING'"
>
<a
href="javascript:void(0);"
class="ant-dropdown-link"
>通过</a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item key="1"> <a-menu-item key="1">
<a <a href="javascript:void(0);" @click="handleEditStatusClick(record.id, 'PUBLISHED')">通过</a>
href="javascript:void(0);"
@click="handleEditStatusClick(record.id,'PUBLISHED')"
>通过</a>
</a-menu-item> </a-menu-item>
<a-menu-item key="2"> <a-menu-item key="2">
<a <a href="javascript:void(0);" @click="handleReplyAndPassClick(record)"></a>
href="javascript:void(0);"
@click="handleReplyAndPassClick(record)"
>通过并回复</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
<a <a href="javascript:void(0);" v-else-if="record.status === 'PUBLISHED'" @click="handleReplyClick(record)"
href="javascript:void(0);" >回复</a
v-else-if="record.status === 'PUBLISHED'" >
@click="handleReplyClick(record)"
>回复</a>
<a-popconfirm <a-popconfirm
:title="'你确定要还原该评论?'" :title="'你确定要还原该评论?'"
@confirm="handleEditStatusClick(record.id,'PUBLISHED')" @confirm="handleEditStatusClick(record.id, 'PUBLISHED')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
v-else-if="record.status === 'RECYCLE'" v-else-if="record.status === 'RECYCLE'"
@ -353,7 +219,7 @@
<a-popconfirm <a-popconfirm
:title="'你确定要将该评论移到回收站?'" :title="'你确定要将该评论移到回收站?'"
@confirm="handleEditStatusClick(record.id,'RECYCLE')" @confirm="handleEditStatusClick(record.id, 'RECYCLE')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
v-if="record.status === 'PUBLISHED' || record.status === 'AUDITING'" v-if="record.status === 'PUBLISHED' || record.status === 'AUDITING'"
@ -370,13 +236,6 @@
> >
<a href="javascript:;">删除</a> <a href="javascript:;">删除</a>
</a-popconfirm> </a-popconfirm>
<!-- <a-divider type="vertical" />
<a
href="javascript:;"
@click="handleShowDetailDrawer(record)"
>详情</a> -->
</span> </span>
</a-table> </a-table>
<div class="page-wrapper"> <div class="page-wrapper">
@ -397,7 +256,7 @@
<a-modal <a-modal
v-if="selectedComment" v-if="selectedComment"
:title="'回复给:'+selectedComment.author" :title="'回复给:' + selectedComment.author"
v-model="replyCommentVisible" v-model="replyCommentVisible"
@close="onReplyClose" @close="onReplyClose"
destroyOnClose destroyOnClose
@ -414,33 +273,16 @@
erroredText="回复失败" erroredText="回复失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form-model <a-form-model ref="replyCommentForm" :model="replyComment" :rules="replyCommentRules" layout="vertical">
ref="replyCommentForm"
:model="replyComment"
:rules="replyCommentRules"
layout="vertical"
>
<a-form-model-item prop="content"> <a-form-model-item prop="content">
<a-input <a-input ref="contentInput" type="textarea" :autoSize="{ minRows: 8 }" v-model.trim="replyComment.content" />
ref="contentInput"
type="textarea"
:autoSize="{ minRows: 8 }"
v-model.trim="replyComment.content"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
<!-- <CommentDetail
v-model="commentDetailVisible"
v-if="selectedComment"
:comment="selectedComment"
:type="this.type"
/> -->
</div> </div>
</template> </template>
<script> <script>
import { mixin, mixinDevice } from '@/mixins/mixin.js' import { mixin, mixinDevice } from '@/mixins/mixin.js'
import CommentDetail from './CommentDetail'
import marked from 'marked' import marked from 'marked'
import commentApi from '@/api/comment' import commentApi from '@/api/comment'
import { decodeHTML } from '@/utils/util' import { decodeHTML } from '@/utils/util'
@ -451,39 +293,39 @@ const postColumns = [
dataIndex: 'author', dataIndex: 'author',
width: '150px', width: '150px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'author' }, scopedSlots: { customRender: 'author' }
}, },
{ {
title: '内容', title: '内容',
dataIndex: 'content', dataIndex: 'content',
scopedSlots: { customRender: 'content' }, scopedSlots: { customRender: 'content' }
}, },
{ {
title: '状态', title: '状态',
className: 'status', className: 'status',
dataIndex: 'statusProperty', dataIndex: 'statusProperty',
width: '100px', width: '100px',
scopedSlots: { customRender: 'status' }, scopedSlots: { customRender: 'status' }
}, },
{ {
title: '评论文章', title: '评论文章',
dataIndex: 'post', dataIndex: 'post',
width: '200px', width: '200px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'post' }, scopedSlots: { customRender: 'post' }
}, },
{ {
title: '日期', title: '日期',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '170px', width: '170px',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '180px', width: '180px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
const sheetColumns = [ const sheetColumns = [
{ {
@ -491,46 +333,43 @@ const sheetColumns = [
dataIndex: 'author', dataIndex: 'author',
width: '150px', width: '150px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'author' }, scopedSlots: { customRender: 'author' }
}, },
{ {
title: '内容', title: '内容',
dataIndex: 'content', dataIndex: 'content',
scopedSlots: { customRender: 'content' }, scopedSlots: { customRender: 'content' }
}, },
{ {
title: '状态', title: '状态',
className: 'status', className: 'status',
dataIndex: 'statusProperty', dataIndex: 'statusProperty',
width: '100px', width: '100px',
scopedSlots: { customRender: 'status' }, scopedSlots: { customRender: 'status' }
}, },
{ {
title: '评论页面', title: '评论页面',
dataIndex: 'sheet', dataIndex: 'sheet',
width: '200px', width: '200px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'sheet' }, scopedSlots: { customRender: 'sheet' }
}, },
{ {
title: '日期', title: '日期',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '170px', width: '170px',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '180px', width: '180px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
name: 'CommentTab', name: 'CommentTab',
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: {
CommentDetail,
},
props: { props: {
type: { type: {
type: String, type: String,
@ -538,8 +377,8 @@ export default {
default: 'posts', default: 'posts',
validator: function(value) { validator: function(value) {
return ['posts', 'sheets', 'journals'].indexOf(value) !== -1 return ['posts', 'sheets', 'journals'].indexOf(value) !== -1
}, }
}, }
}, },
data() { data() {
return { return {
@ -549,14 +388,14 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null,
status: null, status: null
}, },
selectedRowKeys: [], selectedRowKeys: [],
selectedRows: [], selectedRows: [],
@ -564,13 +403,12 @@ export default {
selectedComment: {}, selectedComment: {},
replyComment: {}, replyComment: {},
replyCommentRules: { replyCommentRules: {
content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }], content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }]
}, },
loading: false, loading: false,
commentStatus: commentApi.commentStatus, commentStatus: commentApi.commentStatus,
commentDetailVisible: false,
replying: false, replying: false,
replyErrored: false, replyErrored: false
} }
}, },
created() { created() {
@ -578,12 +416,12 @@ export default {
}, },
computed: { computed: {
formattedComments() { formattedComments() {
return this.comments.map((comment) => { return this.comments.map(comment => {
comment.statusProperty = this.commentStatus[comment.status] comment.statusProperty = this.commentStatus[comment.status]
comment.content = marked(decodeHTML(comment.content)) comment.content = marked(decodeHTML(comment.content))
return comment return comment
}) })
}, }
}, },
methods: { methods: {
handleListComments() { handleListComments() {
@ -593,7 +431,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
commentApi commentApi
.queryComment(this.type, this.queryParam) .queryComment(this.type, this.queryParam)
.then((response) => { .then(response => {
this.comments = response.data.data.content this.comments = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -610,7 +448,7 @@ export default {
handleEditStatusClick(commentId, status) { handleEditStatusClick(commentId, status) {
commentApi commentApi
.updateStatus(this.type, commentId, status) .updateStatus(this.type, commentId, status)
.then((response) => { .then(() => {
this.$message.success('操作成功!') this.$message.success('操作成功!')
}) })
.finally(() => { .finally(() => {
@ -620,7 +458,7 @@ export default {
handleDeleteClick(commentId) { handleDeleteClick(commentId) {
commentApi commentApi
.delete(this.type, commentId) .delete(this.type, commentId)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -646,7 +484,7 @@ export default {
}, },
handleCreateClick() { handleCreateClick() {
const _this = this const _this = this
_this.$refs.replyCommentForm.validate((valid) => { _this.$refs.replyCommentForm.validate(valid => {
if (valid) { if (valid) {
_this.replying = true _this.replying = true
commentApi commentApi
@ -691,7 +529,7 @@ export default {
} }
commentApi commentApi
.updateStatusInBatch(this.type, this.selectedRowKeys, status) .updateStatusInBatch(this.type, this.selectedRowKeys, status)
.then((response) => { .then(() => {
this.$log.debug(`commentIds: ${this.selectedRowKeys}, status: ${status}`) this.$log.debug(`commentIds: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = [] this.selectedRowKeys = []
}) })
@ -706,7 +544,7 @@ export default {
} }
commentApi commentApi
.deleteInBatch(this.type, this.selectedRowKeys) .deleteInBatch(this.type, this.selectedRowKeys)
.then((response) => { .then(() => {
this.$log.debug(`delete: ${this.selectedRowKeys}`) this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = [] this.selectedRowKeys = []
}) })
@ -730,14 +568,10 @@ export default {
return { return {
props: { props: {
disabled: this.queryParam.status == null || this.queryParam.status === '', disabled: this.queryParam.status == null || this.queryParam.status === '',
name: comment.author, name: comment.author
}, }
} }
}, }
handleShowDetailDrawer(comment) { }
this.selectedComment = comment
this.commentDetailVisible = true
},
},
} }
</script> </script>

View File

@ -8,19 +8,13 @@
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-list itemLayout="horizontal"> <a-list itemLayout="horizontal">
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
<template slot="description"> <template slot="description">
<p <p v-html="description" class="comment-drawer-content"></p>
v-html="description"
class="comment-drawer-content"
></p>
</template> </template>
<h3 slot="title">{{ title }}</h3> <h3 slot="title">{{ title }}</h3>
</a-list-item-meta> </a-list-item-meta>
@ -55,17 +49,9 @@
</div> </div>
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-button <a-button type="primary" @click="handleCommentReply({})"></a-button>
type="primary"
@click="handleCommentReply({})"
>评论</a-button>
</div> </div>
<a-modal <a-modal :title="replyModalTitle" v-model="replyModal.visible" @close="onReplyModalClose" destroyOnClose>
:title="replyModalTitle"
v-model="replyModal.visible"
@close="onReplyModalClose"
destroyOnClose
>
<template slot="footer"> <template slot="footer">
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@ -78,19 +64,9 @@
erroredText="回复失败" erroredText="回复失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form-model <a-form-model ref="replyCommentForm" :model="replyModal.model" :rules="replyModal.rules" layout="vertical">
ref="replyCommentForm"
:model="replyModal.model"
:rules="replyModal.rules"
layout="vertical"
>
<a-form-model-item prop="content"> <a-form-model-item prop="content">
<a-input <a-input ref="contentInput" type="textarea" :autoSize="{ minRows: 8 }" v-model="replyModal.model.content" />
ref="contentInput"
type="textarea"
:autoSize="{ minRows: 8 }"
v-model="replyModal.model.content"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
@ -114,14 +90,14 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null
}, }
}, },
replyModal: { replyModal: {
model: {}, model: {},
@ -129,42 +105,42 @@ export default {
saving: false, saving: false,
saveErrored: false, saveErrored: false,
rules: { rules: {
content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }], content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }]
}, }
}, }
} }
}, },
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false
}, },
title: { title: {
type: String, type: String,
required: false, required: false,
default: '', default: ''
}, },
description: { description: {
type: String, type: String,
required: false, required: false,
default: '', default: ''
}, },
target: { target: {
type: String, type: String,
required: false, required: false,
default: '', default: ''
}, },
id: { id: {
type: Number, type: Number,
required: false, required: false,
default: 0, default: 0
}, }
}, },
computed: { computed: {
replyModalTitle() { replyModalTitle() {
return this.list.selected.id ? `回复给:${this.list.selected.author}` : '评论' return this.list.selected.id ? `回复给:${this.list.selected.author}` : '评论'
}, }
}, },
methods: { methods: {
handleListComments() { handleListComments() {
@ -174,7 +150,7 @@ export default {
this.list.queryParam.sort = this.list.pagination.sort this.list.queryParam.sort = this.list.pagination.sort
commentApi commentApi
.commentTree(this.target, this.id, this.list.queryParam) .commentTree(this.target, this.id, this.list.queryParam)
.then((response) => { .then(response => {
this.list.data = response.data.data.content this.list.data = response.data.data.content
this.list.pagination.total = response.data.data.total this.list.pagination.total = response.data.data.total
}) })
@ -200,7 +176,7 @@ export default {
}, },
handleReplyClick() { handleReplyClick() {
const _this = this const _this = this
_this.$refs.replyCommentForm.validate((valid) => { _this.$refs.replyCommentForm.validate(valid => {
if (valid) { if (valid) {
_this.replyModal.saving = true _this.replyModal.saving = true
commentApi commentApi
@ -229,7 +205,7 @@ export default {
handleEditStatusClick(comment, status) { handleEditStatusClick(comment, status) {
commentApi commentApi
.updateStatus(this.target, comment.id, status) .updateStatus(this.target, comment.id, status)
.then((response) => { .then(() => {
this.$message.success('操作成功!') this.$message.success('操作成功!')
}) })
.finally(() => { .finally(() => {
@ -239,7 +215,7 @@ export default {
handleCommentDelete(comment) { handleCommentDelete(comment) {
commentApi commentApi
.delete(this.target, comment.id) .delete(this.target, comment.id)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -256,7 +232,7 @@ export default {
this.list.pagination = { this.list.pagination = {
page: 1, page: 1,
size: 10, size: 10,
sort: '', sort: ''
} }
this.$emit('close', false) this.$emit('close', false)
}, },
@ -264,7 +240,7 @@ export default {
if (visible) { if (visible) {
this.handleListComments() this.handleListComments()
} }
}, }
}, }
} }
</script> </script>

View File

@ -2,17 +2,11 @@
<div> <div>
<a-comment> <a-comment>
<template slot="actions"> <template slot="actions">
<a-dropdown <a-dropdown :trigger="['click']" v-if="comment.status === 'AUDITING'">
:trigger="['click']"
v-if="comment.status === 'AUDITING'"
>
<span href="javascript:void(0);">通过</span> <span href="javascript:void(0);">通过</span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item key="1"> <a-menu-item key="1">
<span <span href="javascript:void(0);" @click="handleEditStatusClick('PUBLISHED')"></span>
href="javascript:void(0);"
@click="handleEditStatusClick('PUBLISHED')"
>通过</span>
</a-menu-item> </a-menu-item>
<a-menu-item key="2"> <a-menu-item key="2">
<span href="javascript:void(0);">通过并回复</span> <span href="javascript:void(0);">通过并回复</span>
@ -20,10 +14,7 @@
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
<span <span v-else-if="comment.status === 'PUBLISHED'" @click="handleReplyClick"></span>
v-else-if="comment.status === 'PUBLISHED'"
@click="handleReplyClick"
>回复</span>
<a-popconfirm <a-popconfirm
v-else-if="comment.status === 'RECYCLE'" v-else-if="comment.status === 'RECYCLE'"
@ -45,37 +36,16 @@
<span>回收站</span> <span>回收站</span>
</a-popconfirm> </a-popconfirm>
<a-popconfirm <a-popconfirm :title="'你确定要永久删除该评论?'" @confirm="handleDeleteClick" okText="确定" cancelText="取消">
:title="'你确定要永久删除该评论?'"
@confirm="handleDeleteClick"
okText="确定"
cancelText="取消"
>
<span>删除</span> <span>删除</span>
</a-popconfirm> </a-popconfirm>
</template> </template>
<a <a slot="author" :href="comment.authorUrl" target="_blank">
slot="author" <a-icon type="user" v-if="comment.isAdmin" style="margin-right: 3px;" />
:href="comment.authorUrl"
target="_blank"
>
<a-icon
type="user"
v-if="comment.isAdmin"
style="margin-right: 3px;"
/>
{{ comment.author }} {{ comment.author }}
</a> </a>
<a-avatar <a-avatar size="large" slot="avatar" :src="avatar" :alt="comment.author" />
size="large" <p slot="content" v-html="content"></p>
slot="avatar"
:src="avatar"
:alt="comment.author"
/>
<p
slot="content"
v-html="content"
></p>
<a-tooltip slot="datetime"> <a-tooltip slot="datetime">
<span slot="title">{{ comment.createTime | moment }}</span> <span slot="title">{{ comment.createTime | moment }}</span>
<span>{{ comment.createTime | timeAgo }}</span> <span>{{ comment.createTime | timeAgo }}</span>

View File

@ -1,165 +1,69 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="12" :xs="12" class="mb-3">
:xl="6" <analysis-card title="文章" :number="statisticsData.postCount">
:lg="6" <router-link :to="{ name: 'PostWrite' }" slot="action">
:md="12" <a-icon v-if="statisticsLoading" type="loading" />
:sm="12" <a-icon v-else type="plus" />
:xs="12"
class="mb-3"
>
<analysis-card
title="文章"
:number="statisticsData.postCount"
>
<router-link
:to="{ name:'PostWrite' }"
slot="action"
>
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="plus"
/>
</router-link> </router-link>
</analysis-card> </analysis-card>
</a-col> </a-col>
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="12" :xs="12" class="mb-3">
:xl="6" <analysis-card title="评论" :number="statisticsData.commentCount">
:lg="6" <router-link :to="{ name: 'Comments' }" slot="action">
:md="12" <a-icon v-if="statisticsLoading" type="loading" />
:sm="12" <a-icon v-else type="unordered-list" />
:xs="12"
class="mb-3"
>
<analysis-card
title="评论"
:number="statisticsData.commentCount"
>
<router-link
:to="{ name:'Comments' }"
slot="action"
>
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="unordered-list"
/>
</router-link> </router-link>
</analysis-card> </analysis-card>
</a-col> </a-col>
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="12" :xs="12" class="mb-3">
:xl="6" <analysis-card title="阅读量" :number="statisticsData.visitCount">
:lg="6"
:md="12"
:sm="12"
:xs="12"
class="mb-3"
>
<analysis-card
title="阅读量"
:number="statisticsData.visitCount"
>
<a-tooltip slot="action"> <a-tooltip slot="action">
<template slot="title"> <template slot="title"> 文章阅读共 {{ statisticsData.visitCount }} </template>
文章阅读共 {{ statisticsData.visitCount }}
</template>
<a href="javascript:void(0);"> <a href="javascript:void(0);">
<a-icon <a-icon v-if="statisticsLoading" type="loading" />
v-if="statisticsLoading" <a-icon v-else type="info-circle-o" />
type="loading"
/>
<a-icon
v-else
type="info-circle-o"
/>
</a> </a>
</a-tooltip> </a-tooltip>
</analysis-card> </analysis-card>
</a-col> </a-col>
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="12" :xs="12" class="mb-3">
:xl="6" <analysis-card title="建立天数" :number="statisticsData.establishDays">
:lg="6"
:md="12"
:sm="12"
:xs="12"
class="mb-3"
>
<analysis-card
title="建立天数"
:number="statisticsData.establishDays"
>
<a-tooltip slot="action"> <a-tooltip slot="action">
<template slot="title">博客建立于 {{ statisticsData.birthday | moment }}</template> <template slot="title">博客建立于 {{ statisticsData.birthday | moment }}</template>
<a href="javascript:void(0);"> <a href="javascript:void(0);">
<a-icon <a-icon v-if="statisticsLoading" type="loading" />
v-if="statisticsLoading" <a-icon v-else type="info-circle-o" />
type="loading"
/>
<a-icon
v-else
type="info-circle-o"
/>
</a> </a>
</a-tooltip> </a-tooltip>
</analysis-card> </analysis-card>
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="24" :xs="24" class="mb-3">
:xl="8" <a-card :bordered="false" title="新动态" :bodyStyle="{ padding: 0 }">
:lg="8"
:md="12"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
:bordered="false"
title="新动态"
:bodyStyle="{ padding: 0 }"
>
<div class="card-container"> <div class="card-container">
<a-tabs type="card"> <a-tabs type="card">
<a-tab-pane <a-tab-pane key="1" tab="最近文章">
key="1" <a-list :loading="activityLoading" :dataSource="latestPosts">
tab="最近文章" <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
>
<a-list
:loading="activityLoading"
:dataSource="latestPosts"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<a <a
v-if="item.status=='PUBLISHED' || item.status == 'INTIMATE'" v-if="item.status == 'PUBLISHED' || item.status == 'INTIMATE'"
slot="title" slot="title"
:href="item.fullPath" :href="item.fullPath"
target="_blank" target="_blank"
>{{ item.title }}</a> >{{ item.title }}</a
>
<a <a
v-else-if="item.status=='DRAFT'" v-else-if="item.status == 'DRAFT'"
slot="title" slot="title"
href="javascript:void(0)" href="javascript:void(0)"
@click="handlePostPreview(item.id)" @click="handlePostPreview(item.id)"
>{{ item.title }}</a> >{{ item.title }}</a
<a
v-else-if="item.status=='RECYCLE'"
slot="title"
href="javascript:void(0);"
disabled
> >
<a v-else-if="item.status == 'RECYCLE'" slot="title" href="javascript:void(0);" disabled>
{{ item.title }} {{ item.title }}
</a> </a>
</a-list-item-meta> </a-list-item-meta>
@ -167,22 +71,13 @@
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="2" tab="最近评论">
key="2"
tab="最近评论"
>
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs> <a-tabs>
<a-tab-pane <a-tab-pane tab="文章" key="1">
tab="文章"
key="1"
>
<recent-comment-tab type="posts"></recent-comment-tab> <recent-comment-tab type="posts"></recent-comment-tab>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane tab="页面" key="2">
tab="页面"
key="2"
>
<recent-comment-tab type="sheets"></recent-comment-tab> <recent-comment-tab type="sheets"></recent-comment-tab>
</a-tab-pane> </a-tab-pane>
<!-- <a-tab-pane <!-- <a-tab-pane
@ -198,68 +93,32 @@
</div> </div>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="24" :xs="24" class="mb-3">
:xl="8"
:lg="8"
:md="12"
:sm="24"
:xs="24"
class="mb-3"
>
<JournalPublishCard /> <JournalPublishCard />
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="24" :xs="24" class="mb-3">
:xl="8" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:lg="8"
:md="12"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<template slot="title"> <template slot="title">
操作记录 操作记录
<a-tooltip <a-tooltip slot="action" title="更多">
slot="action" <a href="javascript:void(0);" @click="logListDrawerVisible = true">
title="更多"
>
<a
href="javascript:void(0);"
@click="logListDrawerVisible = true"
>
<a-icon type="ellipsis" /> <a-icon type="ellipsis" />
</a> </a>
</a-tooltip> </a-tooltip>
</template> </template>
<a-list <a-list :dataSource="formattedLogDatas" :loading="logLoading">
:dataSource="formattedLogDatas" <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
:loading="logLoading"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-list-item-meta :description="item.createTime | timeAgo"> <a-list-item-meta :description="item.createTime | timeAgo">
<span slot="title">{{ item.type }}</span> <span slot="title">{{ item.type }}</span>
</a-list-item-meta> </a-list-item-meta>
<ellipsis <ellipsis :length="35" tooltip>{{ item.content }}</ellipsis>
:length="35"
tooltip
>{{ item.content }}</ellipsis>
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
<LogListDrawer <LogListDrawer :visible="logListDrawerVisible" @close="handleLogListClose" />
:visible="logListDrawerVisible"
@close="handleLogListClose"
/>
</page-view> </page-view>
</template> </template>
@ -269,7 +128,6 @@ import AnalysisCard from './components/AnalysisCard'
import JournalPublishCard from './components/JournalPublishCard' import JournalPublishCard from './components/JournalPublishCard'
import RecentCommentTab from './components/RecentCommentTab' import RecentCommentTab from './components/RecentCommentTab'
import LogListDrawer from './components/LogListDrawer' import LogListDrawer from './components/LogListDrawer'
import countTo from 'vue-count-to'
import postApi from '@/api/post' import postApi from '@/api/post'
import logApi from '@/api/log' import logApi from '@/api/log'
@ -281,7 +139,6 @@ export default {
AnalysisCard, AnalysisCard,
JournalPublishCard, JournalPublishCard,
RecentCommentTab, RecentCommentTab,
countTo,
LogListDrawer LogListDrawer
}, },
data() { data() {
@ -307,7 +164,7 @@ export default {
}, },
computed: { computed: {
formattedLogDatas() { formattedLogDatas() {
return this.latestLogs.map((log) => { return this.latestLogs.map(log => {
log.type = this.logTypes[log.type].text log.type = this.logTypes[log.type].text
return log return log
}) })
@ -319,7 +176,7 @@ export default {
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
next((vm) => { next(vm => {
vm.interval = setInterval(() => { vm.interval = setInterval(() => {
vm.handleLoadStatistics() vm.handleLoadStatistics()
}, 5000) }, 5000)
@ -341,7 +198,7 @@ export default {
this.activityLoading = true this.activityLoading = true
postApi postApi
.listLatest(5) .listLatest(5)
.then((response) => { .then(response => {
this.latestPosts = response.data.data this.latestPosts = response.data.data
}) })
.finally(() => { .finally(() => {
@ -354,7 +211,7 @@ export default {
this.logLoading = true this.logLoading = true
logApi logApi
.listLatest(5) .listLatest(5)
.then((response) => { .then(response => {
this.latestLogs = response.data.data this.latestLogs = response.data.data
}) })
.finally(() => { .finally(() => {
@ -366,7 +223,7 @@ export default {
handleLoadStatistics() { handleLoadStatistics() {
statisticsApi statisticsApi
.statistics() .statistics()
.then((response) => { .then(response => {
this.statisticsData = response.data.data this.statisticsData = response.data.data
}) })
.catch(() => { .catch(() => {
@ -379,7 +236,7 @@ export default {
}) })
}, },
handlePostPreview(postId) { handlePostPreview(postId) {
postApi.preview(postId).then((response) => { postApi.preview(postId).then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
}) })
}, },

View File

@ -1,8 +1,5 @@
<template> <template>
<a-card <a-card :body-style="{ padding: '24px' }" :bordered="false">
:body-style="{ padding: '24px' }"
:bordered="false"
>
<div class="analysis-card-container"> <div class="analysis-card-container">
<div class="meta"> <div class="meta">
<span class="analysis-card-title"> <span class="analysis-card-title">
@ -16,7 +13,7 @@
<slot name="number"> <slot name="number">
<countTo <countTo
:startVal="startNumber" :startVal="startNumber"
:endVal="typeof number === 'function' && number() || number" :endVal="(typeof number === 'function' && number()) || number"
:duration="3000" :duration="3000"
:autoplay="true" :autoplay="true"
></countTo> ></countTo>

View File

@ -1,23 +1,12 @@
<template> <template>
<a-card <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<template slot="title"> <template slot="title">
速记 速记
<a-tooltip <a-tooltip slot="action" title="内容将保存到页面/所有页面/日志页面">
slot="action" <a-icon type="info-circle-o" class="cursor-pointer" />
title="内容将保存到页面/所有页面/日志页面"
>
<a-icon type="info-circle-o" class="cursor-pointer"/>
</a-tooltip> </a-tooltip>
</template> </template>
<a-form-model <a-form-model ref="journalForm" :model="form.model" :rules="form.rules" layout="vertical">
ref="journalForm"
:model="form.model"
:rules="form.rules"
layout="vertical"
>
<a-form-model-item prop="sourceContent"> <a-form-model-item prop="sourceContent">
<a-input <a-input
type="textarea" type="textarea"
@ -29,10 +18,12 @@
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton
@click="handleCreateJournalClick" @click="handleCreateJournalClick"
@callback="() => { @callback="
if(!form.errored) form.model = {} () => {
form.errored = false if (!form.errored) form.model = {}
}" form.errored = false
}
"
:loading="form.saving" :loading="form.saving"
:errored="form.errored" :errored="form.errored"
text="发布" text="发布"

View File

@ -2,34 +2,21 @@
<div> <div>
<a-drawer <a-drawer
title="操作日志" title="操作日志"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-list <a-list :loading="loading" :dataSource="formattedLogsDatas">
:loading="loading" <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
:dataSource="formattedLogsDatas"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-list-item-meta :description="item.createTime | timeAgo"> <a-list-item-meta :description="item.createTime | timeAgo">
<span slot="title">{{ item.type }}</span> <span slot="title">{{ item.type }}</span>
</a-list-item-meta> </a-list-item-meta>
<ellipsis <ellipsis :length="35" tooltip>{{ item.content }}</ellipsis>
:length="35"
tooltip
>{{ item.content }}</ellipsis>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -39,7 +26,7 @@
:current="pagination.page" :current="pagination.page"
:total="pagination.total" :total="pagination.total"
:defaultPageSize="pagination.size" :defaultPageSize="pagination.size"
:pageSizeOptions="['50', '100','150','200']" :pageSizeOptions="['50', '100', '150', '200']"
showSizeChanger showSizeChanger
@showSizeChange="handlePaginationChange" @showSizeChange="handlePaginationChange"
@change="handlePaginationChange" @change="handlePaginationChange"
@ -50,12 +37,7 @@
</a-row> </a-row>
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-popconfirm <a-popconfirm title="你确定要清空所有操作日志?" okText="确定" @confirm="handleClearLogs" cancelText="取消">
title="你确定要清空所有操作日志?"
okText="确定"
@confirm="handleClearLogs"
cancelText="取消"
>
<a-button type="danger">清空操作日志</a-button> <a-button type="danger">清空操作日志</a-button>
</a-popconfirm> </a-popconfirm>
</div> </div>
@ -123,7 +105,7 @@ export default {
handleClearLogs() { handleClearLogs() {
logApi logApi
.clear() .clear()
.then(response => { .then(() => {
this.$message.success('清除成功!') this.$message.success('清除成功!')
}) })
.finally(() => { .finally(() => {

View File

@ -1,68 +1,40 @@
<template> <template>
<a-list <a-list itemLayout="horizontal" :dataSource="formmatedCommentData" :loading="loading">
itemLayout="horizontal" <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
:dataSource="formmatedCommentData" <a-comment :avatar="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '/?s=256&d=mp'">
:loading="loading" <template slot="author" v-if="type === 'posts'">
> <a :href="item.authorUrl" target="_blank">{{ item.author }}</a> 发表在 <a
<a-list-item v-if="item.post.status == 'PUBLISHED' || item.post.status == 'INTIMATE'"
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-comment :avatar="'//cn.gravatar.com/avatar/'+item.gravatarMd5+'/?s=256&d=mp'">
<template
slot="author"
v-if="type==='posts'"
>
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a> 发表在 <a
v-if="item.post.status=='PUBLISHED' || item.post.status=='INTIMATE'"
:href="item.post.fullPath" :href="item.post.fullPath"
target="_blank" target="_blank"
>{{ item.post.title }}</a><a >{{ item.post.title }}</a
v-else-if="item.post.status=='DRAFT'" ><a
v-else-if="item.post.status == 'DRAFT'"
href="javascript:void(0)" href="javascript:void(0)"
@click="handlePostPreview(item.post.id)" @click="handlePostPreview(item.post.id)"
>{{ item.post.title }}</a><a >{{ item.post.title }}</a
v-else ><a v-else href="javascript:void(0)">{{ item.post.title }}</a>
href="javascript:void(0)"
>{{ item.post.title }}</a>
</template> </template>
<template <template slot="author" v-else-if="type === 'sheets'">
slot="author" <a :href="item.authorUrl" target="_blank">{{ item.author }}</a> 发表在 <a
v-else-if="type==='sheets'" v-if="item.sheet.status == 'PUBLISHED'"
>
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a> 发表在 <a
v-if="item.sheet.status=='PUBLISHED'"
:href="item.sheet.fullPath" :href="item.sheet.fullPath"
target="_blank" target="_blank"
>{{ item.sheet.title }}</a><a >{{ item.sheet.title }}</a
v-else-if="item.sheet.status=='DRAFT'" ><a
v-else-if="item.sheet.status == 'DRAFT'"
href="javascript:void(0)" href="javascript:void(0)"
@click="handleSheetPreview(item.sheet.id)" @click="handleSheetPreview(item.sheet.id)"
>{{ item.sheet.title }}</a><a >{{ item.sheet.title }}</a
v-else ><a v-else href="javascript:void(0)">{{ item.sheet.title }}</a
href="javascript:void(0)" >
>{{ item.sheet.title }}</a>
</template> </template>
<!-- <template slot="actions"> <!-- <template slot="actions">
<span>回复</span> <span>回复</span>
</template> --> </template> -->
<p <p class="comment-content-wrapper" slot="content" v-html="item.content"></p>
class="comment-content-wrapper" <a-tooltip slot="datetime" :title="item.createTime | moment">
slot="content"
v-html="item.content"
></p>
<a-tooltip
slot="datetime"
:title="item.createTime | moment"
>
<span>{{ item.createTime | timeAgo }}</span> <span>{{ item.createTime | timeAgo }}</span>
</a-tooltip> </a-tooltip>
</a-comment> </a-comment>

View File

@ -1,10 +1,6 @@
<template> <template>
<div class="exception"> <div class="exception">
<a-result <a-result :status="type" :title="type" :subTitle="config[type].desc">
:status="type"
:title="type"
:subTitle="config[type].desc"
>
<template v-slot:extra> <template v-slot:extra>
<a-button type="primary" @click="handleToHome"></a-button> <a-button type="primary" @click="handleToHome"></a-button>
</template> </template>

View File

@ -1,18 +1,8 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="6" :lg="6" :md="6" :sm="24" :xs="24" class="mb-3">
:xl="6" <a-card :bodyStyle="{ padding: '16px' }" title="分组">
:lg="6"
:md="6"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
:bodyStyle="{ padding: '16px' }"
title="分组"
>
<template slot="extra"> <template slot="extra">
<ReactiveButton <ReactiveButton
type="default" type="default"
@ -27,19 +17,16 @@
</template> </template>
<div class="menu-teams"> <div class="menu-teams">
<a-spin :spinning="teams.loading"> <a-spin :spinning="teams.loading">
<a-empty v-if="teams.data.length===0 && !teams.loading" /> <a-empty v-if="teams.data.length === 0 && !teams.loading" />
<a-menu <a-menu
class="w-full" class="w-full"
mode="inline" mode="inline"
v-model="selectedTeam" v-model="selectedTeam"
v-if="teams.data.length>0" v-if="teams.data.length > 0"
@select="handleSelectedTeam" @select="handleSelectedTeam"
> >
<a-menu-item <a-menu-item v-for="team in teams.data" :key="team">
v-for="(team) in teams.data" {{ team === '' ? '未分组' : team }}{{ defaultMenuTeam === team ? '(默认)' : '' }}
:key="team"
>
{{ team===''?'未分组':team }}{{ defaultMenuTeam===team?'(默认)':'' }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-spin> </a-spin>
@ -60,39 +47,22 @@
@keyup.enter.native="handleCreateTeam" @keyup.enter.native="handleCreateTeam"
> >
<a-form-model-item prop="team"> <a-form-model-item prop="team">
<a-input <a-input v-model="teams.form.model.team" autoFocus />
v-model="teams.form.model.team"
autoFocus
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item style="margin-bottom:0"> <a-form-model-item style="margin-bottom:0">
<a-button <a-button type="primary" @click="handleCreateTeam">
type="primary"
@click="handleCreateTeam"
>
新增 新增
</a-button> </a-button>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</template> </template>
<a-button <a-button type="primary" block class="mt-3">
type="primary"
block
class="mt-3"
>
新增分组 新增分组
</a-button> </a-button>
</a-popover> </a-popover>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="18" :lg="18" :md="18" :sm="24" :xs="24" class="pb-3">
:xl="18"
:lg="18"
:md="18"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card :bodyStyle="{ padding: '16px' }"> <a-card :bodyStyle="{ padding: '16px' }">
<template slot="title"> <template slot="title">
<span> <span>
@ -103,37 +73,25 @@
title="分组下的菜单为空时,该分组也不会保存" title="分组下的菜单为空时,该分组也不会保存"
v-if="list.data.length <= 0 && !list.loading" v-if="list.data.length <= 0 && !list.loading"
> >
<a-icon <a-icon type="info-circle-o" class="cursor-pointer" />
type="info-circle-o"
class="cursor-pointer"
/>
</a-tooltip> </a-tooltip>
</template> </template>
<template slot="extra"> <template slot="extra">
<a-space> <a-space>
<ReactiveButton <ReactiveButton
@click="handleUpdateBatch" @click="handleUpdateBatch"
@callback="formBatch.errored=false" @callback="formBatch.errored = false"
:loading="formBatch.saving" :loading="formBatch.saving"
:errored="formBatch.errored" :errored="formBatch.errored"
text="保存" text="保存"
loadedText="保存成功" loadedText="保存成功"
erroredText="保存失败" erroredText="保存失败"
:disabled="list.data.length<=0" :disabled="list.data.length <= 0"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button v-if="!form.visible" @click="handleOpenCreateMenuForm()" type="primary" ghost>
v-if="!form.visible"
@click="handleOpenCreateMenuForm()"
type="primary"
ghost
>
新增 新增
</a-button> </a-button>
<a-button <a-button v-else @click="handleCloseCreateMenuForm()" type="default">
v-else
@click="handleCloseCreateMenuForm()"
type="default"
>
取消新增 取消新增
</a-button> </a-button>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
@ -145,7 +103,8 @@
删除当前组 删除当前组
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<a-button> 其他 <a-button>
其他
<a-icon type="down" /> <a-icon type="down" />
</a-button> </a-button>
</a-dropdown> </a-dropdown>
@ -158,12 +117,8 @@
@succeed="handleCreateMenuSucceed()" @succeed="handleCreateMenuSucceed()"
@cancel="handleCloseCreateMenuForm()" @cancel="handleCloseCreateMenuForm()"
/> />
<a-empty v-if="list.data.length===0 && !list.loading && !form.visible" /> <a-empty v-if="list.data.length === 0 && !list.loading && !form.visible" />
<MenuTreeNode <MenuTreeNode v-model="list.data" :excludedTeams="excludedTeams" @reload="handleListMenus" />
v-model="list.data"
:excludedTeams="excludedTeams"
@reload="handleListMenus"
/>
</a-spin> </a-spin>
</a-card> </a-card>
</a-col> </a-col>
@ -179,7 +134,6 @@
<script> <script>
// components // components
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
import draggable from 'vuedraggable'
import MenuTreeNode from './components/MenuTreeNode' import MenuTreeNode from './components/MenuTreeNode'
import MenuForm from './components/MenuForm' import MenuForm from './components/MenuForm'
import MenuInternalLinkSelector from './components/MenuInternalLinkSelector' import MenuInternalLinkSelector from './components/MenuInternalLinkSelector'
@ -191,20 +145,20 @@ import { mapActions, mapGetters } from 'vuex'
import menuApi from '@/api/menu' import menuApi from '@/api/menu'
import optionApi from '@/api/option' import optionApi from '@/api/option'
export default { export default {
components: { PageView, draggable, MenuTreeNode, MenuForm, MenuInternalLinkSelector }, components: { PageView, MenuTreeNode, MenuForm, MenuInternalLinkSelector },
data() { data() {
return { return {
list: { list: {
data: [], data: [],
loading: false, loading: false
}, },
form: { form: {
visible: false, visible: false,
model: {}, model: {}
}, },
formBatch: { formBatch: {
saving: false, saving: false,
errored: false, errored: false
}, },
teams: { teams: {
data: [], data: [],
@ -213,20 +167,20 @@ export default {
form: { form: {
visible: false, visible: false,
model: { model: {
team: null, team: null
}, },
rules: { rules: {
team: [{ required: true, message: '分组名称不能为空', trigger: ['change'] }], team: [{ required: true, message: '分组名称不能为空', trigger: ['change'] }]
}, }
}, },
default: { default: {
saving: false, saving: false,
errored: false, errored: false
}, }
}, },
menuInternalLinkSelector: { menuInternalLinkSelector: {
visible: false, visible: false
}, }
} }
}, },
computed: { computed: {
@ -239,7 +193,7 @@ export default {
return this.handleGetMenusWithoutLevel(this.computedMenusMoved, []) return this.handleGetMenusWithoutLevel(this.computedMenusMoved, [])
}, },
computedMenuIds() { computedMenuIds() {
return this.computedMenusWithoutLevel.map((menu) => { return this.computedMenusWithoutLevel.map(menu => {
return menu.id return menu.id
}) })
}, },
@ -249,19 +203,19 @@ export default {
}, },
set(value) { set(value) {
this.teams.selected = value[0] this.teams.selected = value[0]
}, }
}, },
menuListTitle() { menuListTitle() {
return this.teams.selected === '' ? '未分组' : this.teams.selected return this.teams.selected === '' ? '未分组' : this.teams.selected
}, },
excludedTeams() { excludedTeams() {
return this.teams.data.filter((item) => { return this.teams.data.filter(item => {
return item !== this.teams.selected return item !== this.teams.selected
}) })
}, },
defaultMenuTeam() { defaultMenuTeam() {
return this.options.default_menu_team ? this.options.default_menu_team : '' return this.options.default_menu_team ? this.options.default_menu_team : ''
}, }
}, },
created() { created() {
this.handleListTeams() this.handleListTeams()
@ -272,7 +226,7 @@ export default {
this.teams.loading = true this.teams.loading = true
menuApi menuApi
.listTeams() .listTeams()
.then((response) => { .then(response => {
this.teams.data = response.data.data this.teams.data = response.data.data
if (!this.teams.selected || autoSelectTeam) { if (!this.teams.selected || autoSelectTeam) {
this.teams.selected = this.teams.data[0] this.teams.selected = this.teams.data[0]
@ -290,7 +244,7 @@ export default {
this.list.loading = true this.list.loading = true
menuApi menuApi
.listTreeByTeam(this.teams.selected) .listTreeByTeam(this.teams.selected)
.then((response) => { .then(response => {
this.list.data = response.data.data this.list.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -320,7 +274,7 @@ export default {
} }
return result return result
}, },
handleSelectedTeam({ item, key, selectedKeys }) { handleSelectedTeam({ key }) {
this.teams.selected = key this.teams.selected = key
this.handleCloseCreateMenuForm() this.handleCloseCreateMenuForm()
this.handleListMenus() this.handleListMenus()
@ -348,7 +302,7 @@ export default {
menuApi.deleteBatch(_this.computedMenuIds).finally(() => { menuApi.deleteBatch(_this.computedMenuIds).finally(() => {
_this.handleListTeams(true) _this.handleListTeams(true)
}) })
}, }
}) })
}, },
handleTeamFormVisibleChange(visible) { handleTeamFormVisibleChange(visible) {
@ -358,7 +312,7 @@ export default {
}, },
handleCreateTeam() { handleCreateTeam() {
const _this = this const _this = this
_this.$refs.teamForm.validate((valid) => { _this.$refs.teamForm.validate(valid => {
if (valid) { if (valid) {
if (!_this.teams.data.includes(_this.teams.form.model.team)) { if (!_this.teams.data.includes(_this.teams.form.model.team)) {
_this.teams.data.push(_this.teams.form.model.team) _this.teams.data.push(_this.teams.form.model.team)
@ -373,7 +327,7 @@ export default {
this.form.visible = true this.form.visible = true
this.form.model = { this.form.model = {
team: this.teams.selected, team: this.teams.selected,
target: '_self', target: '_self'
} }
}, },
handleCloseCreateMenuForm() { handleCloseCreateMenuForm() {
@ -388,7 +342,7 @@ export default {
this.teams.default.saving = true this.teams.default.saving = true
optionApi optionApi
.save({ .save({
default_menu_team: this.teams.selected, default_menu_team: this.teams.selected
}) })
.catch(() => { .catch(() => {
this.teams.default.errored = true this.teams.default.errored = true
@ -405,7 +359,7 @@ export default {
} else { } else {
this.refreshOptionsCache() this.refreshOptionsCache()
} }
}, }
}, }
} }
</script> </script>

View File

@ -1,63 +1,31 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="6" :lg="6" :md="6" :sm="24" :xs="24" class="pb-3">
:xl="6"
:lg="6"
:md="6"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card :bodyStyle="{ padding: '16px' }"> <a-card :bodyStyle="{ padding: '16px' }">
<template slot="title"> <template slot="title">
<a-select <a-select class="w-full" @change="onSelectTheme" v-model="selectedTheme.id" :loading="themesLoading">
class="w-full" <a-select-option v-for="(theme, index) in themes" :key="index" :value="theme.id"
@change="onSelectTheme" >{{ theme.name }}
v-model="selectedTheme.id" <a-icon v-if="theme.activated" type="check" />
:loading="themesLoading"
>
<a-select-option
v-for="(theme,index) in themes"
:key="index"
:value="theme.id"
>{{ theme.name }}
<a-icon
v-if="theme.activated"
type="check"
/>
</a-select-option> </a-select-option>
</a-select> </a-select>
</template> </template>
<a-spin :spinning="filesLoading"> <a-spin :spinning="filesLoading">
<theme-file <theme-file v-if="files" :files="files" @listenToSelect="handleSelectFile" />
v-if="files"
:files="files"
@listenToSelect="handleSelectFile"
/>
</a-spin> </a-spin>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="18" :lg="18" :md="18" :sm="24" :xs="24" class="pb-3">
:xl="18"
:lg="18"
:md="18"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card :bodyStyle="{ padding: '16px' }"> <a-card :bodyStyle="{ padding: '16px' }">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item> <a-form-item>
<codemirror <codemirror v-model="content" :options="codemirrorOptions"></codemirror>
v-model="content"
:options="codemirrorOptions"
></codemirror>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<ReactiveButton <ReactiveButton
@click="handlerSaveContent" @click="handlerSaveContent"
@callback="saveErrored=false" @callback="saveErrored = false"
:loading="saving" :loading="saving"
:errored="saveErrored" :errored="saveErrored"
:disabled="buttonDisabled" :disabled="buttonDisabled"
@ -83,7 +51,7 @@ export default {
components: { components: {
codemirror, codemirror,
ThemeFile, ThemeFile,
PageView, PageView
}, },
data() { data() {
return { return {
@ -92,7 +60,7 @@ export default {
tabSize: 4, tabSize: 4,
mode: 'text/html', mode: 'text/html',
lineNumbers: true, lineNumbers: true,
line: true, line: true
}, },
files: [], files: [],
filesLoading: false, filesLoading: false,
@ -102,7 +70,7 @@ export default {
themesLoading: false, themesLoading: false,
selectedTheme: {}, selectedTheme: {},
saving: false, saving: false,
saveErrored: false, saveErrored: false
} }
}, },
created() { created() {
@ -112,7 +80,7 @@ export default {
}, },
methods: { methods: {
handleGetActivatedTheme() { handleGetActivatedTheme() {
themeApi.getActivatedTheme().then((response) => { themeApi.getActivatedTheme().then(response => {
this.selectedTheme = response.data.data this.selectedTheme = response.data.data
}) })
}, },
@ -120,7 +88,7 @@ export default {
this.filesLoading = true this.filesLoading = true
themeApi themeApi
.listFilesActivated() .listFilesActivated()
.then((response) => { .then(response => {
this.files = response.data.data this.files = response.data.data
}) })
.finally(() => { .finally(() => {
@ -133,7 +101,7 @@ export default {
this.themesLoading = true this.themesLoading = true
themeApi themeApi
.list() .list()
.then((response) => { .then(response => {
this.themes = response.data.data this.themes = response.data.data
}) })
.finally(() => { .finally(() => {
@ -147,7 +115,7 @@ export default {
this.filesLoading = true this.filesLoading = true
themeApi themeApi
.listFiles(themeId) .listFiles(themeId)
.then((response) => { .then(response => {
this.files = response.data.data this.files = response.data.data
this.content = '' this.content = ''
this.file = {} this.file = {}
@ -180,10 +148,10 @@ export default {
_this.content = '' _this.content = ''
_this.file = {} _this.file = {}
_this.buttonDisabled = true _this.buttonDisabled = true
}, }
}) })
} }
themeApi.getContent(this.selectedTheme.id, file.path).then((response) => { themeApi.getContent(this.selectedTheme.id, file.path).then(response => {
this.content = response.data.data this.content = response.data.data
this.file = file this.file = file
this.buttonDisabled = false this.buttonDisabled = false
@ -201,7 +169,7 @@ export default {
this.saving = false this.saving = false
}, 400) }, 400)
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,121 +1,42 @@
<template> <template>
<page-view <page-view affix :title="activatedTheme ? activatedTheme.name : '无'" subTitle="当前启用">
affix
:title="activatedTheme ? activatedTheme.name : '无'"
subTitle="当前启用"
>
<template slot="extra"> <template slot="extra">
<a-button <a-button icon="reload" :loading="list.loading" @click="handleRefreshThemesCache">
icon="reload"
:loading="list.loading"
@click="handleRefreshThemesCache"
>
刷新 刷新
</a-button> </a-button>
<a-button <a-button type="primary" icon="plus" @click="installModal.visible = true">
type="primary"
icon="plus"
@click="installModal.visible = true"
>
安装 安装
</a-button> </a-button>
</template> </template>
<a-row <a-row :gutter="12" type="flex" align="middle">
:gutter="12"
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-list <a-list
:grid="{ gutter: 12, xs: 1, sm: 1, md: 2, lg: 4, xl: 4, xxl: 4 }" :grid="{ gutter: 12, xs: 1, sm: 1, md: 2, lg: 4, xl: 4, xxl: 4 }"
:dataSource="sortedThemes" :dataSource="sortedThemes"
:loading="list.loading" :loading="list.loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem" <a-card hoverable :title="item.name" :bodyStyle="{ padding: 0 }">
slot-scope="item, index"
:key="index"
>
<a-card
hoverable
:title="item.name"
:bodyStyle="{ padding: 0 }"
>
<div class="theme-screenshot"> <div class="theme-screenshot">
<img <img :alt="item.name" :src="item.screenshots || '/images/placeholder.jpg'" loading="lazy" />
:alt="item.name"
:src="item.screenshots || '/images/placeholder.jpg'"
loading="lazy"
/>
</div> </div>
<template <template class="ant-card-actions" slot="actions">
class="ant-card-actions" <div v-if="item.activated"><a-icon type="unlock" theme="twoTone" style="margin-right:3px" />已启用</div>
slot="actions" <div v-else @click="handleActiveTheme(item)"><a-icon type="lock" style="margin-right:3px" />启用</div>
>
<div v-if="item.activated">
<a-icon
type="unlock"
theme="twoTone"
style="margin-right:3px"
/>
</div>
<div
v-else
@click="handleActiveTheme(item)"
>
<a-icon
type="lock"
style="margin-right:3px"
/>
</div>
<div @click="handleOpenThemeSettingDrawer(item)"> <div @click="handleOpenThemeSettingDrawer(item)">
<a-icon <a-icon type="setting" style="margin-right:3px" />设置
type="setting"
style="margin-right:3px"
/>
</div> </div>
<a-dropdown <a-dropdown placement="topCenter" :trigger="['click']">
placement="topCenter" <a class="ant-dropdown-link" href="#"> <a-icon type="ellipsis" style="margin-right:3px" />更多 </a>
:trigger="['click']"
>
<a
class="ant-dropdown-link"
href="#"
>
<a-icon
type="ellipsis"
style="margin-right:3px"
/>
</a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item :key="1" :disabled="item.activated" @click="handleOpenThemeDeleteModal(item)">
:key="1" <a-icon type="delete" style="margin-right:3px" />删除
:disabled="item.activated"
@click="handleOpenThemeDeleteModal(item)"
>
<a-icon
type="delete"
style="margin-right:3px"
/>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item :key="2" v-if="item.repo" @click="handleConfirmRemoteUpdate(item)">
:key="2" <a-icon type="cloud" style="margin-right:3px" />在线更新
v-if="item.repo"
@click="handleConfirmRemoteUpdate(item)"
>
<a-icon
type="cloud"
style="margin-right:3px"
/>线
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item :key="3" @click="handleOpenLocalUpdateModal(item)">
:key="3" <a-icon type="file" style="margin-right:3px" />从主题包更新
@click="handleOpenLocalUpdateModal(item)"
>
<a-icon
type="file"
style="margin-right:3px"
/>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
@ -142,10 +63,7 @@
> >
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs> <a-tabs>
<a-tab-pane <a-tab-pane tab="本地上传" key="1">
tab="本地上传"
key="1"
>
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
@ -154,34 +72,21 @@
:uploadHandler="installModal.local.uploadHandler" :uploadHandler="installModal.local.uploadHandler"
@success="handleUploadSucceed" @success="handleUploadSucceed"
></FilePondUpload> ></FilePondUpload>
<a-alert <a-alert type="info" closable>
type="info"
closable
>
<template slot="message"> <template slot="message">
更多主题请访问 更多主题请访问
<a <a target="_blank" href="https://halo.run/themes.html">https://halo.run/themes</a>
target="_blank"
href="https://halo.run/themes.html"
>https://halo.run/themes</a>
</template> </template>
</a-alert> </a-alert>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane tab="远程下载" key="2">
tab="远程下载"
key="2"
>
<a-form-model <a-form-model
ref="remoteInstallForm" ref="remoteInstallForm"
:model="installModal.remote" :model="installModal.remote"
:rules="installModal.remote.rules" :rules="installModal.remote.rules"
layout="vertical" layout="vertical"
> >
<a-form-model-item <a-form-model-item prop="url" label="远程地址:" help="* 支持 Git 仓库地址ZIP 链接。">
prop="url"
label="远程地址:"
help="* 支持 Git 仓库地址ZIP 链接。"
>
<a-input v-model="installModal.remote.url" /> <a-input v-model="installModal.remote.url" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
@ -197,16 +102,10 @@
></ReactiveButton> ></ReactiveButton>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
<a-alert <a-alert type="info" closable>
type="info"
closable
>
<template slot="message"> <template slot="message">
目前仅支持远程 Git 仓库和 ZIP 下载链接更多主题请访问 目前仅支持远程 Git 仓库和 ZIP 下载链接更多主题请访问
<a <a target="_blank" href="https://halo.run/themes.html">https://halo.run/themes</a>
target="_blank"
href="https://halo.run/themes.html"
>https://halo.run/themes</a>
</template> </template>
</a-alert> </a-alert>
</a-tab-pane> </a-tab-pane>
@ -269,19 +168,19 @@ import themeApi from '@/api/theme'
export default { export default {
components: { components: {
PageView, PageView,
ThemeSettingDrawer, ThemeSettingDrawer
}, },
data() { data() {
return { return {
list: { list: {
loading: false, loading: false,
data: [], data: []
}, },
installModal: { installModal: {
visible: false, visible: false,
local: { local: {
uploadHandler: themeApi.upload, uploadHandler: themeApi.upload
}, },
remote: { remote: {
@ -291,15 +190,15 @@ export default {
fetchErrored: false, fetchErrored: false,
rules: { rules: {
url: [{ required: true, message: '* 远程地址不能为空', trigger: ['change'] }], url: [{ required: true, message: '* 远程地址不能为空', trigger: ['change'] }]
}, }
}, }
}, },
localUpdateModel: { localUpdateModel: {
visible: false, visible: false,
uploadHandler: themeApi.updateByUpload, uploadHandler: themeApi.updateByUpload,
selected: {}, selected: {}
}, },
themeDeleteModal: { themeDeleteModal: {
@ -307,13 +206,13 @@ export default {
deleteSettings: false, deleteSettings: false,
selected: {}, selected: {},
deleting: false, deleting: false,
deleteErrored: false, deleteErrored: false
}, },
themeSettingDrawer: { themeSettingDrawer: {
visible: false, visible: false,
selected: {}, selected: {}
}, }
} }
}, },
computed: { computed: {
@ -328,7 +227,7 @@ export default {
return this.sortedThemes[0] return this.sortedThemes[0]
} }
return null return null
}, }
}, },
beforeMount() { beforeMount() {
this.handleListThemes() this.handleListThemes()
@ -352,7 +251,7 @@ export default {
this.list.loading = true this.list.loading = true
themeApi themeApi
.list() .list()
.then((response) => { .then(response => {
this.list.data = response.data.data this.list.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -398,7 +297,7 @@ export default {
this.handleListThemes() this.handleListThemes()
}, },
handleRemoteFetching() { handleRemoteFetching() {
this.$refs.remoteInstallForm.validate((valid) => { this.$refs.remoteInstallForm.validate(valid => {
if (valid) { if (valid) {
this.installModal.remote.fetching = true this.installModal.remote.fetching = true
themeApi themeApi
@ -444,7 +343,7 @@ export default {
const hide = _this.$message.loading('更新中...', 0) const hide = _this.$message.loading('更新中...', 0)
themeApi themeApi
.update(item.id) .update(item.id)
.then((response) => { .then(() => {
_this.$message.success('更新成功!') _this.$message.success('更新成功!')
}) })
.finally(() => { .finally(() => {
@ -452,7 +351,7 @@ export default {
_this.handleListThemes() _this.handleListThemes()
}) })
}, },
onCancel() {}, onCancel() {}
}) })
}, },
onThemeInstallModalClose() { onThemeInstallModalClose() {
@ -473,7 +372,7 @@ export default {
this.themeDeleteModal.visible = false this.themeDeleteModal.visible = false
this.themeDeleteModal.deleteSettings = false this.themeDeleteModal.deleteSettings = false
this.themeDeleteModal.selected = {} this.themeDeleteModal.selected = {}
}, }
}, }
} }
</script> </script>

View File

@ -1,6 +1,5 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model
labelAlign="left" labelAlign="left"
ref="menuForm" ref="menuForm"
@ -9,82 +8,28 @@
@keyup.enter.native="handleCreateOrUpdateMenu" @keyup.enter.native="handleCreateOrUpdateMenu"
> >
<a-row :gutter="24"> <a-row :gutter="24">
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="12" :xs="12">
:xl="8" <a-form-model-item label="名称" prop="name" help="* 页面上所显示的名称">
:lg="8" <a-input v-model="menuModel.name" autoFocus />
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="名称"
prop="name"
help="* 页面上所显示的名称"
>
<a-input
v-model="menuModel.name"
autoFocus
/>
</a-form-model-item> </a-form-model-item>
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="12" :xs="12">
:xl="8" <a-form-model-item label="地址" prop="url" help="* 菜单的地址">
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="地址"
prop="url"
help="* 菜单的地址"
>
<a-input v-model="menuModel.url" /> <a-input v-model="menuModel.url" />
</a-form-model-item> </a-form-model-item>
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="12" :xs="12">
:xl="8" <a-form-model-item label="图标" prop="icon" help="* 请根据主题的支持情况选填">
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="图标"
prop="icon"
help="* 请根据主题的支持情况选填"
>
<a-input v-model="menuModel.icon" /> <a-input v-model="menuModel.icon" />
</a-form-model-item> </a-form-model-item>
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="12" :xs="12">
:xl="8" <a-form-model-item label="打开方式" prop="target">
:lg="8" <a-radio-group v-model="menuModel.target" :options="targets" />
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="打开方式"
prop="target"
>
<a-radio-group
v-model="menuModel.target"
:options="targets"
/>
</a-form-model-item> </a-form-model-item>
</a-col> </a-col>
<a-col <a-col :xl="8" :lg="8" :md="12" :sm="12" :xs="12">
:xl="8" <a-form-model-item label=" " :colon="false">
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label=" "
:colon="false"
>
<a-space> <a-space>
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@ -109,27 +54,27 @@ import menuApi from '@/api/menu'
const targets = [ const targets = [
{ {
value: '_self', value: '_self',
label: '当前窗口', label: '当前窗口'
}, },
{ {
value: '_blank', value: '_blank',
label: '新窗口', label: '新窗口'
}, }
] ]
export default { export default {
name: 'MenuForm', name: 'MenuForm',
model: { model: {
prop: 'menu', prop: 'menu',
event: 'input', event: 'input'
}, },
props: { props: {
menu: { menu: {
type: Object, type: Object,
default: () => { default: () => {
return {} return {}
}, }
}, }
}, },
computed: { computed: {
menuModel: { menuModel: {
@ -138,11 +83,11 @@ export default {
}, },
set(value) { set(value) {
this.$emit('input', value) this.$emit('input', value)
}, }
}, },
isUpdateMode() { isUpdateMode() {
return !!this.menuModel.id return !!this.menuModel.id
}, }
}, },
data() { data() {
return { return {
@ -151,23 +96,23 @@ export default {
rules: { rules: {
name: [ name: [
{ required: true, message: '* 菜单名称不能为空', trigger: ['change'] }, { required: true, message: '* 菜单名称不能为空', trigger: ['change'] },
{ max: 50, message: '* 菜单名称的字符长度不能超过 50', trigger: ['change'] }, { max: 50, message: '* 菜单名称的字符长度不能超过 50', trigger: ['change'] }
], ],
url: [ url: [
{ required: true, message: '* 菜单地址不能为空', trigger: ['change'] }, { required: true, message: '* 菜单地址不能为空', trigger: ['change'] },
{ max: 1023, message: '* 菜单地址的字符长度不能超过 1023', trigger: ['change'] }, { max: 1023, message: '* 菜单地址的字符长度不能超过 1023', trigger: ['change'] }
], ],
icon: [{ max: 50, message: '* 菜单图标的字符长度不能超过 50', trigger: ['change'] }], icon: [{ max: 50, message: '* 菜单图标的字符长度不能超过 50', trigger: ['change'] }]
}, },
saving: false, saving: false,
errored: false, errored: false
}, }
} }
}, },
methods: { methods: {
handleCreateOrUpdateMenu() { handleCreateOrUpdateMenu() {
const _this = this const _this = this
_this.$refs.menuForm.validate((valid) => { _this.$refs.menuForm.validate(valid => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.isUpdateMode) { if (_this.isUpdateMode) {
@ -207,7 +152,7 @@ export default {
}, },
handleCancel() { handleCancel() {
this.$emit('cancel') this.$emit('cancel')
}, }
}, }
} }
</script> </script>

View File

@ -1,10 +1,5 @@
<template> <template>
<a-modal <a-modal v-model="visible" title="从系统预设链接添加菜单" :width="1024" :bodyStyle="{ padding: '0 24px 24px' }">
v-model="visible"
title="从系统预设链接添加菜单"
:width="1024"
:bodyStyle="{ padding: '0 24px 24px' }"
>
<template slot="footer"> <template slot="footer">
<a-button @click="handleCancel"> <a-button @click="handleCancel">
取消 取消
@ -25,110 +20,61 @@
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs default-active-key="1"> <a-tabs default-active-key="1">
<a-tab-pane <a-tab-pane key="1" tab="分类目录" force-render>
key="1"
tab="分类目录"
force-render
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(category, index) in categories" :key="index">
v-for="(category,index) in categories"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ category.name }}</span> <span slot="title">{{ category.name }}</span>
<span slot="description">{{ category.fullPath }}</span> <span slot="description">{{ category.fullPath }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base">
href="javascript:void(0);" <a-icon type="plus-circle" @click="handleInsertPre(category.name, category.fullPath)" />
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(category.name,category.fullPath)"
/>
</a> </a>
</template> </template>
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="2" tab="标签">
key="2"
tab="标签"
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(tag, index) in tags" :key="index">
v-for="(tag,index) in tags"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ tag.name }}</span> <span slot="title">{{ tag.name }}</span>
<span slot="description">{{ tag.fullPath }}</span> <span slot="description">{{ tag.fullPath }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base">
href="javascript:void(0);" <a-icon type="plus-circle" @click="handleInsertPre(tag.name, tag.fullPath)" />
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(tag.name,tag.fullPath)"
/>
</a> </a>
</template> </template>
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="3" tab="独立页面">
key="3"
tab="独立页面"
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(item, index) in sheet.independents" :key="index">
v-for="(item,index) in sheet.independents"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ item.title }}</span> <span slot="title">{{ item.title }}</span>
<span slot="description">{{ item.fullPath }}</span> <span slot="description">{{ item.fullPath }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base">
href="javascript:void(0);" <a-icon type="plus-circle" @click="handleInsertPre(item.title, item.fullPath)" />
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.title,item.fullPath)"
/>
</a> </a>
</template> </template>
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="4" tab="自定义页面">
key="4"
tab="自定义页面"
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(item, index) in sheet.customs.data" :key="index">
v-for="(item,index) in sheet.customs.data"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ item.title }}</span> <span slot="title">{{ item.title }}</span>
<span slot="description">{{ item.fullPath }}</span> <span slot="description">{{ item.fullPath }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base">
href="javascript:void(0);" <a-icon type="plus-circle" @click="handleInsertPre(item.title, item.fullPath)" />
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.title,item.fullPath)"
/>
</a> </a>
</template> </template>
</a-list-item> </a-list-item>
@ -147,28 +93,16 @@
/> />
</div> </div>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane key="5" tab="其他">
key="5"
tab="其他"
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(item, index) in otherInternalLinks" :key="index">
v-for="(item,index) in otherInternalLinks"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ item.name }}</span> <span slot="title">{{ item.name }}</span>
<span slot="description">{{ item.url }}</span> <span slot="description">{{ item.url }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base">
href="javascript:void(0);" <a-icon type="plus-circle" @click="handleInsertPre(item.name, item.url)" />
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.name,item.url)"
/>
</a> </a>
</template> </template>
</a-list-item> </a-list-item>
@ -181,26 +115,15 @@
<a-col :span="12"> <a-col :span="12">
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs default-active-key="1"> <a-tabs default-active-key="1">
<a-tab-pane <a-tab-pane key="1" tab="备选" force-render>
key="1"
tab="备选"
force-render
>
<a-list item-layout="horizontal"> <a-list item-layout="horizontal">
<a-list-item <a-list-item v-for="(menu, index) in menus" :key="index">
v-for="(menu,index) in menus"
:key="index"
>
<a-list-item-meta> <a-list-item-meta>
<span slot="title">{{ menu.name }}</span> <span slot="title">{{ menu.name }}</span>
<span slot="description">{{ menu.url }}</span> <span slot="description">{{ menu.url }}</span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" class="text-base" @click="handleRemovePre(index)">
href="javascript:void(0);"
class="text-base"
@click="handleRemovePre(index)"
>
<a-icon type="close-circle" /> <a-icon type="close-circle" />
</a> </a>
</template> </template>
@ -225,12 +148,12 @@ export default {
props: { props: {
value: { value: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
team: { team: {
type: String, type: String,
default: '', default: ''
}, }
}, },
data() { data() {
return { return {
@ -246,18 +169,18 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null
}, }
}, }
}, },
loading: false, loading: false,
saving: false, saving: false,
saveErrored: false, saveErrored: false
} }
}, },
computed: { computed: {
@ -267,7 +190,7 @@ export default {
}, },
set(value) { set(value) {
this.$emit('input', value) this.$emit('input', value)
}, }
}, },
otherInternalLinks() { otherInternalLinks() {
const options = this.options const options = this.options
@ -275,30 +198,30 @@ export default {
return [ return [
{ {
name: '分类目录', name: '分类目录',
url: `${options.blog_url}/${options.categories_prefix}${pathSuffix}`, url: `${options.blog_url}/${options.categories_prefix}${pathSuffix}`
}, },
{ {
name: '标签', name: '标签',
url: `${options.blog_url}/${options.tags_prefix}${pathSuffix}`, url: `${options.blog_url}/${options.tags_prefix}${pathSuffix}`
}, },
{ {
name: '文章归档', name: '文章归档',
url: `${options.blog_url}/${options.archives_prefix}${pathSuffix}`, url: `${options.blog_url}/${options.archives_prefix}${pathSuffix}`
}, },
{ {
name: 'RSS', name: 'RSS',
url: `${options.blog_url}/atom.xml`, url: `${options.blog_url}/atom.xml`
}, },
{ {
name: '网站地图', name: '网站地图',
url: `${options.blog_url}/sitemap.xml`, url: `${options.blog_url}/sitemap.xml`
}, },
{ {
name: '网站地图', name: '网站地图',
url: `${options.blog_url}/sitemap.html`, url: `${options.blog_url}/sitemap.html`
}, }
] ]
}, }
}, },
watch: { watch: {
visible(value) { visible(value) {
@ -306,13 +229,13 @@ export default {
this.handleFetchAll() this.handleFetchAll()
this.handleListSheets() this.handleListSheets()
} }
}, }
}, },
methods: { methods: {
handleFetchAll() { handleFetchAll() {
this.loading = true this.loading = true
Promise.all([optionApi.listAll(), categoryApi.listAll(true), tagApi.listAll(true), sheetApi.listIndependent()]) Promise.all([optionApi.listAll(), categoryApi.listAll(true), tagApi.listAll(true), sheetApi.listIndependent()])
.then((response) => { .then(response => {
this.options = response[0].data.data this.options = response[0].data.data
this.categories = response[1].data.data this.categories = response[1].data.data
this.tags = response[2].data.data this.tags = response[2].data.data
@ -328,7 +251,7 @@ export default {
this.sheet.customs.queryParam.page = this.sheet.customs.pagination.page - 1 this.sheet.customs.queryParam.page = this.sheet.customs.pagination.page - 1
this.sheet.customs.queryParam.size = this.sheet.customs.pagination.size this.sheet.customs.queryParam.size = this.sheet.customs.pagination.size
this.sheet.customs.queryParam.sort = this.sheet.customs.pagination.sort this.sheet.customs.queryParam.sort = this.sheet.customs.pagination.sort
sheetApi.list(this.sheet.customs.queryParam).then((response) => { sheetApi.list(this.sheet.customs.queryParam).then(response => {
this.sheet.customs.data = response.data.data.content this.sheet.customs.data = response.data.data.content
this.sheet.customs.pagination.total = response.data.data.total this.sheet.customs.pagination.total = response.data.data.total
}) })
@ -342,7 +265,7 @@ export default {
this.menus.push({ this.menus.push({
name: name, name: name,
url: url, url: url,
team: this.team, team: this.team
}) })
}, },
handleRemovePre(index) { handleRemovePre(index) {
@ -372,7 +295,7 @@ export default {
} else { } else {
this.handleCancel() this.handleCancel()
} }
}, }
}, }
} }
</script> </script>

View File

@ -12,83 +12,53 @@
handle=".title" handle=".title"
> >
<transition-group> <transition-group>
<div <div :key="item.id" v-for="item in realValue">
:key="item.id"
v-for="(item) in realValue"
>
<a-list-item class="cursor-pointer menu-item"> <a-list-item class="cursor-pointer menu-item">
<a-list-item-meta> <a-list-item-meta>
<span <span slot="title" class="inline-block font-bold cursor-move title"
slot="title" >{{ item.name }}
class="title cursor-move inline-block font-bold" <a-tooltip title="外部链接" v-if="item.target === '_blank'">
>{{ item.name }}
<a-tooltip
title="外部链接"
v-if="item.target==='_blank'"
>
<a-icon type="link" /> <a-icon type="link" />
</a-tooltip> </a-tooltip>
{{ item.formVisible?'(正在编辑)':'' }} {{ item.formVisible ? '(正在编辑)' : '' }}
</span> </span>
<span <span slot="description" class="inline-block">
slot="description" <a :href="item.url" target="_blank" class="ant-anchor-link-title"> {{ item.url }} </a>
class="inline-block"
>
<a
:href="item.url"
target="_blank"
class="ant-anchor-link-title"
> {{ item.url }} </a>
</span> </span>
</a-list-item-meta> </a-list-item-meta>
<template slot="actions"> <template slot="actions">
<a <a v-if="!item.formVisible" href="javascript:void(0);" @click="handleOpenEditForm(item)">
v-if="!item.formVisible"
href="javascript:void(0);"
@click="handleOpenEditForm(item)"
>
编辑 编辑
</a> </a>
<a <a v-else href="javascript:void(0);" @click="handleCloseCreateMenuForm(item)">
v-else
href="javascript:void(0);"
@click="handleCloseCreateMenuForm(item)"
>
取消编辑 取消编辑
</a> </a>
</template> </template>
<template slot="actions"> <template slot="actions">
<a <a href="javascript:void(0);" @click="handleDelete(item.id)"></a>
href="javascript:void(0);"
@click="handleDelete(item.id)"
>删除</a>
</template> </template>
<template <template slot="actions" v-if="excludedTeams && excludedTeams.length > 0">
slot="actions"
v-if="excludedTeams && excludedTeams.length>0"
>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
<a <a class="ant-dropdown-link" @click="e => e.preventDefault()">
class="ant-dropdown-link"
@click="e => e.preventDefault()"
>
更多 更多
<a-icon type="down" /> <a-icon type="down" />
</a> </a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-sub-menu title="移动到分组"> <a-sub-menu title="移动到分组">
<a-menu-item <a-menu-item
v-for="(team,index) in excludedTeams" v-for="(team, index) in excludedTeams"
:key="index" :key="index"
@click="handleMoveMenu(item,team)" @click="handleMoveMenu(item, team)"
>{{ team===''?'未分组':team }}</a-menu-item> >{{ team === '' ? '未分组' : team }}</a-menu-item
>
</a-sub-menu> </a-sub-menu>
<a-sub-menu title="复制到分组"> <a-sub-menu title="复制到分组">
<a-menu-item <a-menu-item
v-for="(team,index) in excludedTeams" v-for="(team, index) in excludedTeams"
:key="index" :key="index"
@click="handleCopyMenu(item,team)" @click="handleCopyMenu(item, team)"
>{{ team===''?'未分组':team }}</a-menu-item> >{{ team === '' ? '未分组' : team }}</a-menu-item
>
</a-sub-menu> </a-sub-menu>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
@ -100,15 +70,8 @@
@succeed="handleUpdateMenuSucceed(item)" @succeed="handleUpdateMenuSucceed(item)"
@cancel="handleCloseCreateMenuForm(item)" @cancel="handleCloseCreateMenuForm(item)"
/> />
<div <div class="a-list-nested" style="margin-left: 44px;">
class="a-list-nested" <MenuTreeNode :list="item.children" :excludedTeams="excludedTeams" @reload="onReloadEmit" />
style="margin-left: 44px;"
>
<MenuTreeNode
:list="item.children"
:excludedTeams="excludedTeams"
@reload="onReloadEmit"
/>
</div> </div>
</div> </div>
</transition-group> </transition-group>
@ -127,28 +90,28 @@ export default {
name: 'MenuTreeNode', name: 'MenuTreeNode',
components: { components: {
draggable, draggable,
MenuForm, MenuForm
}, },
props: { props: {
value: { value: {
required: false, required: false,
type: Array, type: Array,
default: null, default: null
}, },
list: { list: {
required: false, required: false,
type: Array, type: Array,
default: null, default: null
}, },
excludedTeams: { excludedTeams: {
required: false, required: false,
type: Array, type: Array,
default: null, default: null
}, }
}, },
data() { data() {
return { return {
isDragging: false, isDragging: false
} }
}, },
computed: { computed: {
@ -159,12 +122,12 @@ export default {
ghostClass: 'ghost', ghostClass: 'ghost',
chosenClass: 'chosen', chosenClass: 'chosen',
dragClass: 'drag', dragClass: 'drag',
emptyInsertThreshold: 20, emptyInsertThreshold: 20
} }
}, },
realValue() { realValue() {
return this.value ? this.value : this.list return this.value ? this.value : this.list
}, }
}, },
methods: { methods: {
emitter(value) { emitter(value) {
@ -179,7 +142,7 @@ export default {
menuApi.delete(id).finally(() => { menuApi.delete(id).finally(() => {
_this.onReloadEmit() _this.onReloadEmit()
}) })
}, }
}) })
}, },
handleOpenEditForm(item) { handleOpenEditForm(item) {
@ -198,7 +161,7 @@ export default {
menu.parentId = 0 menu.parentId = 0
menu.priority = 0 menu.priority = 0
menu.id = null menu.id = null
menuApi.create(menu).then((response) => { menuApi.create(menu).then(() => {
this.$emit('reload') this.$emit('reload')
}) })
}, },
@ -207,14 +170,14 @@ export default {
menu.team = team menu.team = team
menu.parentId = 0 menu.parentId = 0
menu.priority = 0 menu.priority = 0
menuApi.update(menu.id, menu).then((response) => { menuApi.update(menu.id, menu).then(() => {
this.$emit('reload') this.$emit('reload')
}) })
}, },
onReloadEmit() { onReloadEmit() {
this.$emit('reload') this.$emit('reload')
}, }
}, }
} }
</script> </script>
<style scoped> <style scoped>

View File

@ -9,81 +9,27 @@
:visible="visible" :visible="visible"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row :gutter="12" type="flex">
:gutter="12" <a-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24" v-if="!viewMode">
type="flex"
>
<a-col
:xl="12"
:lg="12"
:md="12"
:sm="24"
:xs="24"
v-if="!viewMode"
>
<a-card :bordered="false"> <a-card :bordered="false">
<img <img :alt="theme.name" :src="theme.screenshots" slot="cover" />
:alt="theme.name"
:src="theme.screenshots"
slot="cover"
>
<a-card-meta :description="theme.description"> <a-card-meta :description="theme.description">
<template slot="title"> <template slot="title">
<a <a :href="author.website" target="_blank">{{ author.name }}</a>
:href="author.website"
target="_blank"
>{{ author.name }}</a>
</template> </template>
<a-avatar <a-avatar v-if="theme.logo" :src="theme.logo" size="large" slot="avatar" />
v-if="theme.logo" <a-avatar v-else size="large" slot="avatar">{{ author.name }}</a-avatar>
:src="theme.logo"
size="large"
slot="avatar"
/>
<a-avatar
v-else
size="large"
slot="avatar"
>{{ author.name }}</a-avatar>
</a-card-meta> </a-card-meta>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="formColValue" :lg="formColValue" :md="formColValue" :sm="24" :xs="24" style="padding-bottom: 50px;">
:xl="formColValue"
:lg="formColValue"
:md="formColValue"
:sm="24"
:xs="24"
style="padding-bottom: 50px;"
>
<a-spin :spinning="settingLoading"> <a-spin :spinning="settingLoading">
<div <div class="card-container" v-if="themeConfigurations.length > 0">
class="card-container" <a-tabs type="card" defaultActiveKey="0">
v-if="themeConfigurations.length>0" <a-tab-pane v-for="(group, index) in themeConfigurations" :key="index.toString()" :tab="group.label">
> <a-form layout="vertical" :wrapperCol="wrapperCol">
<a-tabs <a-form-item v-for="(item, index1) in group.items" :label="item.label + ''" :key="index1">
type="card" <p v-if="item.description && item.description != ''" slot="help" v-html="item.description"></p>
defaultActiveKey="0"
>
<a-tab-pane
v-for="(group, index) in themeConfigurations"
:key="index.toString()"
:tab="group.label"
>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item
v-for="(item, index1) in group.items"
:label="item.label + ''"
:key="index1"
>
<p
v-if="item.description && item.description!=''"
slot="help"
v-html="item.description"
></p>
<a-input <a-input
v-model="themeSettings[item.name]" v-model="themeSettings[item.name]"
:defaultValue="item.defaultValue" :defaultValue="item.defaultValue"
@ -103,22 +49,18 @@
v-model="themeSettings[item.name]" v-model="themeSettings[item.name]"
v-else-if="item.type == 'RADIO'" v-else-if="item.type == 'RADIO'"
> >
<a-radio <a-radio v-for="(option, index2) in item.options" :key="index2" :value="option.value">{{
v-for="(option, index2) in item.options" option.label
:key="index2" }}</a-radio>
:value="option.value"
>{{ option.label }}</a-radio>
</a-radio-group> </a-radio-group>
<a-select <a-select
v-model="themeSettings[item.name]" v-model="themeSettings[item.name]"
:defaultValue="item.defaultValue" :defaultValue="item.defaultValue"
v-else-if="item.type == 'SELECT'" v-else-if="item.type == 'SELECT'"
> >
<a-select-option <a-select-option v-for="option in item.options" :key="option.value" :value="option.value">{{
v-for="option in item.options" option.label
:key="option.value" }}</a-select-option>
:value="option.value"
>{{ option.label }}</a-select-option>
</a-select> </a-select>
<verte <verte
picker="square" picker="square"
@ -133,11 +75,7 @@
:defaultValue="item.defaultValue" :defaultValue="item.defaultValue"
v-else-if="item.type == 'ATTACHMENT'" v-else-if="item.type == 'ATTACHMENT'"
> >
<a <a href="javascript:void(0);" slot="addonAfter" @click="handleShowSelectAttachment(item.name)">
href="javascript:void(0);"
slot="addonAfter"
@click="handleShowSelectAttachment(item.name)"
>
<a-icon type="picture" /> <a-icon type="picture" />
</a> </a>
</a-input> </a-input>
@ -163,26 +101,12 @@
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</div> </div>
<a-empty <a-empty v-if="themeConfigurations.length <= 0 && !settingLoading" description="当前主题暂无设置选项" />
v-if="themeConfigurations.length <=0 && !settingLoading"
description="当前主题暂无设置选项"
/>
</a-spin> </a-spin>
</a-col> </a-col>
<a-col <a-col :xl="20" :lg="20" :md="20" :sm="24" :xs="24" v-if="viewMode" style="padding-bottom: 50px;">
:xl="20" <a-card :bordered="true" :bodyStyle="{ padding: 0 }">
:lg="20"
:md="20"
:sm="24"
:xs="24"
v-if="viewMode"
style="padding-bottom: 50px;"
>
<a-card
:bordered="true"
:bodyStyle="{ padding: 0}"
>
<iframe <iframe
id="themeViewIframe" id="themeViewIframe"
title="主题预览" title="主题预览"
@ -191,8 +115,9 @@
border="0" border="0"
:src="options.blog_url" :src="options.blog_url"
width="100%" width="100%"
:height="clientHeight-165" :height="clientHeight - 165"
> </iframe> >
</iframe>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
@ -203,26 +128,18 @@
title="选择附件" title="选择附件"
/> />
<footer-tool-bar <footer-tool-bar v-if="themeConfigurations.length > 0" class="w-full">
v-if="themeConfigurations.length>0"
class="w-full"
>
<a-space> <a-space>
<a-button <a-button v-if="!this.isMobile() && theme.activated && viewMode" type="primary" @click="toggleViewMode" ghost
v-if="!this.isMobile() && theme.activated && viewMode" >普通模式</a-button
type="primary" >
@click="toggleViewMode" <a-button v-else-if="!this.isMobile() && theme.activated && !viewMode" type="dashed" @click="toggleViewMode"
ghost >预览模式</a-button
>普通模式</a-button> >
<a-button
v-else-if="!this.isMobile() && theme.activated && !viewMode"
type="dashed"
@click="toggleViewMode"
>预览模式</a-button>
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@click="handleSaveSettings" @click="handleSaveSettings"
@callback="saveErrored=false" @callback="saveErrored = false"
:loading="saving" :loading="saving"
:errored="saveErrored" :errored="saveErrored"
text="保存" text="保存"
@ -316,7 +233,7 @@ export default {
this.saving = true this.saving = true
themeApi themeApi
.saveSettings(this.theme.id, this.themeSettings) .saveSettings(this.theme.id, this.themeSettings)
.then(response => { .then(() => {
if (this.viewMode) { if (this.viewMode) {
document.getElementById('themeViewIframe').contentWindow.location.reload(true) document.getElementById('themeViewIframe').contentWindow.location.reload(true)
} }

View File

@ -1,82 +1,30 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24" class="pb-3">
:xl="10" <a-card :title="title" :bodyStyle="{ padding: '16px' }">
:lg="10" <a-form-model ref="categoryForm" :model="form.model" :rules="form.rules" layout="horizontal">
:md="10" <a-form-model-item label="名称:" help="* 页面上所显示的名称" prop="name">
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
:title="title"
:bodyStyle="{ padding: '16px' }"
>
<a-form-model
ref="categoryForm"
:model="form.model"
:rules="form.rules"
layout="horizontal"
>
<a-form-model-item
label="名称:"
help="* 页面上所显示的名称"
prop="name"
>
<a-input v-model="form.model.name" /> <a-input v-model="form.model.name" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="别名:" help="* 一般为单个分类页面的标识,最好为英文" prop="slug">
label="别名:"
help="* 一般为单个分类页面的标识,最好为英文"
prop="slug"
>
<a-input v-model="form.model.slug" /> <a-input v-model="form.model.slug" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="上级目录:" prop="parentId">
label="上级目录:" <category-select-tree :categories="table.data" v-model="form.model.parentId" />
prop="parentId"
>
<category-select-tree
:categories="table.data"
v-model="form.model.parentId"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="封面图:" help="* 在分类页面可展示,需要主题支持" prop="thumbnail">
label="封面图:"
help="* 在分类页面可展示,需要主题支持"
prop="thumbnail"
>
<a-input v-model="form.model.thumbnail"> <a-input v-model="form.model.thumbnail">
<a <a href="javascript:void(0);" slot="addonAfter" @click="thumbnailDrawer.visible = true">
href="javascript:void(0);"
slot="addonAfter"
@click="thumbnailDrawer.visible = true"
>
<a-icon type="picture" /> <a-icon type="picture" />
</a> </a>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="密码:" help="* 分类密码" prop="password">
label="密码:" <a-input-password v-model="form.model.password" autocomplete="new-password" />
help="* 分类密码"
prop="password"
>
<a-input-password
v-model="form.model.password"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="描述:" help="* 分类描述,需要主题支持" prop="description">
label="描述:" <a-input type="textarea" v-model="form.model.description" :autoSize="{ minRows: 3 }" />
help="* 分类描述,需要主题支持"
prop="description"
>
<a-input
type="textarea"
v-model="form.model.description"
:autoSize="{ minRows: 3 }"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton
@ -101,27 +49,14 @@
loadedText="更新成功" loadedText="更新成功"
erroredText="更新失败" erroredText="更新失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button type="dashed" @click="form.model = {}">返回添加</a-button>
type="dashed"
@click="form.model = {}"
>返回添加</a-button>
</a-button-group> </a-button-group>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24" class="pb-3">
:xl="14" <a-card title="分类列表" :bodyStyle="{ padding: '16px' }">
:lg="14"
:md="14"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
title="分类列表"
:bodyStyle="{ padding: '16px' }"
>
<!-- Mobile --> <!-- Mobile -->
<a-list <a-list
v-if="isMobile()" v-if="isMobile()"
@ -131,29 +66,19 @@
:dataSource="table.data" :dataSource="table.data"
:loading="table.loading" :loading="table.loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<span> <span>
<a-icon type="form" /> <a-icon type="form" />
{{ item.postCount }} {{ item.postCount }}
</span> </span>
<a-dropdown <a-dropdown placement="topLeft" :trigger="['click']">
placement="topLeft"
:trigger="['click']"
>
<span> <span>
<a-icon type="bars" /> <a-icon type="bars" />
</span> </span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item> <a-menu-item>
<a <a href="javascript:void(0);" @click="form.model = item">编辑</a>
href="javascript:void(0);"
@click="form.model = item"
>编辑</a>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a-popconfirm <a-popconfirm
@ -176,9 +101,8 @@
slot="title" slot="title"
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;" style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
> >
{{ item.name }}{{ item.password?'(加密)':'' }} {{ item.name }}{{ item.password ? '(加密)' : '' }}
</span> </span>
</a-list-item-meta> </a-list-item-meta>
<span> <span>
{{ item.description }} {{ item.description }}
@ -194,34 +118,24 @@
:loading="table.loading" :loading="table.loading"
:scrollToFirstRowOnChange="true" :scrollToFirstRowOnChange="true"
> >
<span <span slot="name" slot-scope="text, record" class="cursor-pointer">
slot="name" {{ record.name }}{{ record.password ? '(加密)' : '' }}
slot-scope="text,record"
class="cursor-pointer"
>
{{ record.name }}{{ record.password?'(加密)':'' }}
</span> </span>
<span <span
slot="postCount" slot="postCount"
slot-scope="text,record" slot-scope="text, record"
class="cursor-pointer" class="cursor-pointer"
@click="handleQueryCategoryPosts(record)" @click="handleQueryCategoryPosts(record)"
> >
<a-badge <a-badge
:count="record.postCount" :count="record.postCount"
:numberStyle="{backgroundColor: '#00e0ff'} " :numberStyle="{ backgroundColor: '#00e0ff' }"
:showZero="true" :showZero="true"
:overflowCount="9999" :overflowCount="9999"
/> />
</span> </span>
<span <span slot="action" slot-scope="text, record">
slot="action" <a href="javascript:void(0);" @click="form.model = record">编辑</a>
slot-scope="text, record"
>
<a
href="javascript:void(0);"
@click="form.model = record"
>编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
:title="'你确定要删除【' + record.name + '】分类?'" :title="'你确定要删除【' + record.name + '】分类?'"
@ -256,23 +170,23 @@ const columns = [
title: '名称', title: '名称',
ellipsis: true, ellipsis: true,
dataIndex: 'name', dataIndex: 'name',
scopedSlots: { customRender: 'name' }, scopedSlots: { customRender: 'name' }
}, },
{ {
title: '别名', title: '别名',
ellipsis: true, ellipsis: true,
dataIndex: 'slug', dataIndex: 'slug'
}, },
{ {
title: '文章数', title: '文章数',
dataIndex: 'postCount', dataIndex: 'postCount',
scopedSlots: { customRender: 'postCount' }, scopedSlots: { customRender: 'postCount' }
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
@ -283,7 +197,7 @@ export default {
table: { table: {
columns, columns,
data: [], data: [],
loading: false, loading: false
}, },
form: { form: {
model: {}, model: {},
@ -292,16 +206,16 @@ export default {
rules: { rules: {
name: [ name: [
{ required: true, message: '* 分类名称不能为空', trigger: ['change'] }, { required: true, message: '* 分类名称不能为空', trigger: ['change'] },
{ max: 255, message: '* 分类名称的字符长度不能超过 255', trigger: ['change'] }, { max: 255, message: '* 分类名称的字符长度不能超过 255', trigger: ['change'] }
], ],
slug: [{ max: 255, message: '* 分类别名的字符长度不能超过 255', trigger: ['change'] }], slug: [{ max: 255, message: '* 分类别名的字符长度不能超过 255', trigger: ['change'] }],
thumbnail: [{ max: 1023, message: '* 封面图链接的字符长度不能超过 1023', trigger: ['change'] }], thumbnail: [{ max: 1023, message: '* 封面图链接的字符长度不能超过 1023', trigger: ['change'] }],
description: [{ max: 100, message: '* 分类描述的字符长度不能超过 100', trigger: ['change'] }], description: [{ max: 100, message: '* 分类描述的字符长度不能超过 100', trigger: ['change'] }]
}, }
}, },
thumbnailDrawer: { thumbnailDrawer: {
visible: false, visible: false
}, }
} }
}, },
computed: { computed: {
@ -313,7 +227,7 @@ export default {
}, },
isUpdateMode() { isUpdateMode() {
return !!this.form.model.id return !!this.form.model.id
}, }
}, },
created() { created() {
this.handleListCategories() this.handleListCategories()
@ -323,7 +237,7 @@ export default {
this.table.loading = true this.table.loading = true
categoryApi categoryApi
.listAll(true) .listAll(true)
.then((response) => { .then(response => {
this.table.data = response.data.data this.table.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -335,7 +249,7 @@ export default {
handleDeleteCategory(id) { handleDeleteCategory(id) {
categoryApi categoryApi
.delete(id) .delete(id)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
this.form.model = {} this.form.model = {}
}) })
@ -349,7 +263,7 @@ export default {
*/ */
handleCreateOrUpdateCategory() { handleCreateOrUpdateCategory() {
const _this = this const _this = this
_this.$refs.categoryForm.validate((valid) => { _this.$refs.categoryForm.validate(valid => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.isUpdateMode) { if (_this.isUpdateMode) {
@ -393,7 +307,7 @@ export default {
}, },
handleQueryCategoryPosts(category) { handleQueryCategoryPosts(category) {
this.$router.push({ name: 'PostList', query: { categoryId: category.id } }) this.$router.push({ name: 'PostList', query: { categoryId: category.id } })
}, }
}, }
} }
</script> </script>

View File

@ -1,8 +1,5 @@
<template> <template>
<page-view <page-view affix :title="postToStage.title ? postToStage.title : '新文章'">
affix
:title="postToStage.title?postToStage.title:'新文章'"
>
<template slot="extra"> <template slot="extra">
<a-space> <a-space>
<ReactiveButton <ReactiveButton
@ -15,28 +12,15 @@
loadedText="保存成功" loadedText="保存成功"
erroredText="保存失败" erroredText="保存失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button @click="handlePreview" :loading="previewSaving">预览</a-button>
@click="handlePreview" <a-button type="primary" @click="postSettingVisible = true">发布</a-button>
:loading="previewSaving" <a-button type="dashed" @click="attachmentDrawerVisible = true">附件库</a-button>
>预览</a-button>
<a-button
type="primary"
@click="postSettingVisible = true"
>发布</a-button>
<a-button
type="dashed"
@click="attachmentDrawerVisible = true"
>附件库</a-button>
</a-space> </a-space>
</template> </template>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col :span="24"> <a-col :span="24">
<div class="mb-4"> <div class="mb-4">
<a-input <a-input v-model="postToStage.title" size="large" placeholder="请输入文章标题" />
v-model="postToStage.title"
size="large"
placeholder="请输入文章标题"
/>
</div> </div>
<div id="editor"> <div id="editor">
@ -91,7 +75,7 @@ export default {
PostSettingDrawer, PostSettingDrawer,
AttachmentDrawer, AttachmentDrawer,
MarkdownEditor, MarkdownEditor,
PageView, PageView
// RichTextEditor // RichTextEditor
}, },
data() { data() {
@ -105,15 +89,15 @@ export default {
contentChanges: 0, contentChanges: 0,
draftSaving: false, draftSaving: false,
previewSaving: false, previewSaving: false,
draftSavederrored: false, draftSavederrored: false
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
// Get post id from query // Get post id from query
const postId = to.query.postId const postId = to.query.postId
next((vm) => { next(vm => {
if (postId) { if (postId) {
postApi.get(postId).then((response) => { postApi.get(postId).then(response => {
const post = response.data.data const post = response.data.data
vm.postToStage = post vm.postToStage = post
vm.selectedTagIds = post.tagIds vm.selectedTagIds = post.tagIds
@ -147,13 +131,13 @@ export default {
} else { } else {
this.$confirm({ this.$confirm({
title: '当前页面数据未保存,确定要离开吗?', title: '当前页面数据未保存,确定要离开吗?',
content: (h) => <div style="color:red;">如果离开当面页面你的数据很可能会丢失</div>, content: () => <div style="color:red;">如果离开当面页面你的数据很可能会丢失</div>,
onOk() { onOk() {
next() next()
}, },
onCancel() { onCancel() {
next(false) next(false)
}, }
}) })
} }
}, },
@ -170,16 +154,16 @@ export default {
// } // }
}, },
watch: { watch: {
temporaryContent: function(newValue, oldValue) { temporaryContent(newValue) {
if (newValue) { if (newValue) {
this.contentChanges++ this.contentChanges++
} }
}, }
}, },
computed: { computed: {
temporaryContent() { temporaryContent() {
return this.postToStage.originalContent return this.postToStage.originalContent
}, }
// ...mapGetters(['options']) // ...mapGetters(['options'])
}, },
methods: { methods: {
@ -195,7 +179,7 @@ export default {
if (draftOnly) { if (draftOnly) {
postApi postApi
.updateDraft(this.postToStage.id, this.postToStage.originalContent) .updateDraft(this.postToStage.id, this.postToStage.originalContent)
.then((response) => { .then(() => {
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
.catch(() => { .catch(() => {
@ -209,7 +193,7 @@ export default {
} else { } else {
postApi postApi
.update(this.postToStage.id, this.postToStage, false) .update(this.postToStage.id, this.postToStage, false)
.then((response) => { .then(response => {
this.postToStage = response.data.data this.postToStage = response.data.data
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -226,7 +210,7 @@ export default {
// Create the post // Create the post
postApi postApi
.create(this.postToStage, false) .create(this.postToStage, false)
.then((response) => { .then(response => {
this.postToStage = response.data.data this.postToStage = response.data.data
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -248,11 +232,11 @@ export default {
this.previewSaving = true this.previewSaving = true
if (this.postToStage.id) { if (this.postToStage.id) {
// Update the post // Update the post
postApi.update(this.postToStage.id, this.postToStage, false).then((response) => { postApi.update(this.postToStage.id, this.postToStage, false).then(response => {
this.$log.debug('Updated post', response.data.data) this.$log.debug('Updated post', response.data.data)
postApi postApi
.preview(this.postToStage.id) .preview(this.postToStage.id)
.then((response) => { .then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -264,12 +248,12 @@ export default {
}) })
} else { } else {
// Create the post // Create the post
postApi.create(this.postToStage, false).then((response) => { postApi.create(this.postToStage, false).then(response => {
this.$log.debug('Created post', response.data.data) this.$log.debug('Created post', response.data.data)
this.postToStage = response.data.data this.postToStage = response.data.data
postApi postApi
.preview(this.postToStage.id) .preview(this.postToStage.id)
.then((response) => { .then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -298,7 +282,7 @@ export default {
}, },
onRefreshPostMetasFromSetting(metas) { onRefreshPostMetasFromSetting(metas) {
this.selectedMetas = metas this.selectedMetas = metas
}, }
}, }
} }
</script> </script>

View File

@ -1,48 +1,24 @@
<template> <template>
<page-view> <page-view>
<a-card <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input v-model="queryParam.keyword" @keyup.enter="handleQuery()" />
v-model="queryParam.keyword"
@keyup.enter="handleQuery()"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="文章状态:"> <a-form-item label="文章状态:">
<a-select <a-select v-model="queryParam.status" placeholder="请选择文章状态" @change="handleQuery()" allowClear>
v-model="queryParam.status" <a-select-option v-for="status in Object.keys(postStatus)" :key="status" :value="status">{{
placeholder="请选择文章状态"
@change="handleQuery()"
allowClear
>
<a-select-option
v-for="status in Object.keys(postStatus)"
:key="status"
:value="status"
>{{
postStatus[status].text postStatus[status].text
}}</a-select-option> }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="分类目录:"> <a-form-item label="分类目录:">
<a-select <a-select
v-model="queryParam.categoryId" v-model="queryParam.categoryId"
@ -51,24 +27,17 @@
:loading="categoriesLoading" :loading="categoriesLoading"
allowClear allowClear
> >
<a-select-option <a-select-option v-for="category in categories" :key="category.id"
v-for="category in categories" >{{ category.name }}({{ category.postCount }})</a-select-option
:key="category.id" >
>{{ category.name }}({{ category.postCount }})</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -79,21 +48,12 @@
<div class="table-operator"> <div class="table-operator">
<router-link :to="{ name: 'PostWrite' }"> <router-link :to="{ name: 'PostWrite' }">
<a-button <a-button type="primary" icon="plus">写文章</a-button>
type="primary"
icon="plus"
>写文章</a-button>
</router-link> </router-link>
<a-dropdown v-show="queryParam.status != null && queryParam.status != '' && !isMobile()"> <a-dropdown v-show="queryParam.status != null && queryParam.status != '' && !isMobile()">
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item key="1" v-if="queryParam.status === 'DRAFT' || queryParam.status === 'RECYCLE'">
key="1" <a href="javascript:void(0);" @click="handleEditStatusMore(postStatus.PUBLISHED.value)">
v-if="queryParam.status === 'DRAFT' || queryParam.status === 'RECYCLE'"
>
<a
href="javascript:void(0);"
@click="handleEditStatusMore(postStatus.PUBLISHED.value)"
>
<span>发布</span> <span>发布</span>
</a> </a>
</a-menu-item> </a-menu-item>
@ -103,10 +63,7 @@
queryParam.status === 'PUBLISHED' || queryParam.status === 'DRAFT' || queryParam.status === 'INTIMATE' queryParam.status === 'PUBLISHED' || queryParam.status === 'DRAFT' || queryParam.status === 'INTIMATE'
" "
> >
<a <a href="javascript:void(0);" @click="handleEditStatusMore(postStatus.RECYCLE.value)">
href="javascript:void(0);"
@click="handleEditStatusMore(postStatus.RECYCLE.value)"
>
<span>移到回收站</span> <span>移到回收站</span>
</a> </a>
</a-menu-item> </a-menu-item>
@ -116,21 +73,12 @@
queryParam.status === 'RECYCLE' || queryParam.status === 'PUBLISHED' || queryParam.status === 'INTIMATE' queryParam.status === 'RECYCLE' || queryParam.status === 'PUBLISHED' || queryParam.status === 'INTIMATE'
" "
> >
<a <a href="javascript:void(0);" @click="handleEditStatusMore(postStatus.DRAFT.value)">
href="javascript:void(0);"
@click="handleEditStatusMore(postStatus.DRAFT.value)"
>
<span>草稿</span> <span>草稿</span>
</a> </a>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="4" v-if="queryParam.status === 'RECYCLE' || queryParam.status === 'DRAFT'">
key="4" <a href="javascript:void(0);" @click="handleDeleteMore">
v-if="queryParam.status === 'RECYCLE' || queryParam.status === 'DRAFT'"
>
<a
href="javascript:void(0);"
@click="handleDeleteMore"
>
<span>永久删除</span> <span>永久删除</span>
</a> </a>
</a-menu-item> </a-menu-item>
@ -151,11 +99,7 @@
:dataSource="formattedPosts" :dataSource="formattedPosts"
:loading="postsLoading" :loading="postsLoading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<span> <span>
<a-icon type="eye" /> <a-icon type="eye" />
@ -165,19 +109,15 @@
<a-icon type="message" /> <a-icon type="message" />
{{ item.commentCount }} {{ item.commentCount }}
</span> </span>
<a-dropdown <a-dropdown placement="topLeft" :trigger="['click']">
placement="topLeft"
:trigger="['click']"
>
<span> <span>
<a-icon type="bars" /> <a-icon type="bars" />
</span> </span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT' || item.status === 'INTIMATE'"> <a-menu-item
<a v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT' || item.status === 'INTIMATE'"
href="javascript:;" >
@click="handleEditClick(item)" <a href="javascript:;" @click="handleEditClick(item)"></a>
>编辑</a>
</a-menu-item> </a-menu-item>
<a-menu-item v-else-if="item.status === 'RECYCLE'"> <a-menu-item v-else-if="item.status === 'RECYCLE'">
<a-popconfirm <a-popconfirm
@ -189,7 +129,9 @@
<a href="javascript:;">还原</a> <a href="javascript:;">还原</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT' || item.status === 'INTIMATE'"> <a-menu-item
v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT' || item.status === 'INTIMATE'"
>
<a-popconfirm <a-popconfirm
:title="'你确定要将【' + item.title + '】文章移到回收站?'" :title="'你确定要将【' + item.title + '】文章移到回收站?'"
@confirm="handleEditStatusClick(item.id, 'RECYCLE')" @confirm="handleEditStatusClick(item.id, 'RECYCLE')"
@ -210,21 +152,16 @@
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a <a rel="noopener noreferrer" href="javascript:void(0);" @click="handleShowPostSettings(item)"
rel="noopener noreferrer" >设置</a
href="javascript:void(0);" >
@click="handleShowPostSettings(item)"
>设置</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
</template> </template>
<template slot="extra"> <template slot="extra">
<span> <span>
<a-badge <a-badge :status="item.statusProperty.status" :text="item.statusProperty.text" />
:status="item.statusProperty.status"
:text="item.statusProperty.text"
/>
</span> </span>
</template> </template>
<a-list-item-meta> <a-list-item-meta>
@ -248,10 +185,7 @@
target="_blank" target="_blank"
class="no-underline" class="no-underline"
> >
<a-tooltip <a-tooltip placement="top" :title="'点击访问【' + item.title + '】'">{{ item.title }}</a-tooltip>
placement="top"
:title="'点击访问【' + item.title + '】'"
>{{ item.title }}</a-tooltip>
</a> </a>
<a <a
v-else-if="item.status == 'DRAFT'" v-else-if="item.status == 'DRAFT'"
@ -259,17 +193,9 @@
class="no-underline" class="no-underline"
@click="handlePreview(item.id)" @click="handlePreview(item.id)"
> >
<a-tooltip <a-tooltip placement="topLeft" :title="'点击预览【' + item.title + '】'">{{ item.title }}</a-tooltip>
placement="topLeft"
:title="'点击预览【' + item.title + '】'"
>{{ item.title }}</a-tooltip>
</a> </a>
<a <a v-else href="javascript:void(0);" class="no-underline" disabled>
v-else
href="javascript:void(0);"
class="no-underline"
disabled
>
{{ item.title }} {{ item.title }}
</a> </a>
</span> </span>
@ -283,14 +209,16 @@
color="blue" color="blue"
@click="handleSelectCategory(category)" @click="handleSelectCategory(category)"
style="margin-bottom: 8px" style="margin-bottom: 8px"
>{{ category.name }}</a-tag> >{{ category.name }}</a-tag
>
<br /> <br />
<a-tag <a-tag
v-for="(tag, tagIndex) in item.tags" v-for="(tag, tagIndex) in item.tags"
:key="'tag_' + tagIndex" :key="'tag_' + tagIndex"
color="green" color="green"
style="margin-bottom: 8px" style="margin-bottom: 8px"
>{{ tag.name }}</a-tag> >{{ tag.name }}</a-tag
>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -309,10 +237,7 @@
:pagination="false" :pagination="false"
:scrollToFirstRowOnChange="true" :scrollToFirstRowOnChange="true"
> >
<span <span slot="postTitle" slot-scope="text, record">
slot="postTitle"
slot-scope="text, record"
>
<a-icon <a-icon
type="pushpin" type="pushpin"
v-if="record.topPriority != 0" v-if="record.topPriority != 0"
@ -326,10 +251,7 @@
target="_blank" target="_blank"
class="no-underline" class="no-underline"
> >
<a-tooltip <a-tooltip placement="top" :title="'点击访问【' + text + '】'">{{ text }}</a-tooltip>
placement="top"
:title="'点击访问【' + text + '】'"
>{{ text }}</a-tooltip>
</a> </a>
<a <a
v-else-if="record.status == 'DRAFT'" v-else-if="record.status == 'DRAFT'"
@ -337,55 +259,29 @@
class="no-underline" class="no-underline"
@click="handlePreview(record.id)" @click="handlePreview(record.id)"
> >
<a-tooltip <a-tooltip placement="topLeft" :title="'点击预览【' + text + '】'">{{ text }}</a-tooltip>
placement="topLeft"
:title="'点击预览【' + text + '】'"
>{{ text }}</a-tooltip>
</a> </a>
<a <a v-else href="javascript:void(0);" class="no-underline" disabled>
v-else
href="javascript:void(0);"
class="no-underline"
disabled
>
{{ text }} {{ text }}
</a> </a>
</span> </span>
<span <span slot="status" slot-scope="statusProperty">
slot="status" <a-badge :status="statusProperty.status" :text="statusProperty.text" />
slot-scope="statusProperty"
>
<a-badge
:status="statusProperty.status"
:text="statusProperty.text"
/>
</span> </span>
<span <span slot="categories" slot-scope="categoriesOfPost">
slot="categories"
slot-scope="categoriesOfPost"
>
<a-tag <a-tag
v-for="(category, index) in categoriesOfPost" v-for="(category, index) in categoriesOfPost"
:key="index" :key="index"
color="blue" color="blue"
@click="handleSelectCategory(category)" @click="handleSelectCategory(category)"
style="margin-bottom: 8px;cursor:pointer" style="margin-bottom: 8px;cursor:pointer"
>{{ >{{ category.name }}</a-tag
category.name >
}}</a-tag>
</span> </span>
<span <span slot="tags" slot-scope="tags">
slot="tags" <a-tag v-for="(tag, index) in tags" :key="index" color="green" style="margin-bottom: 8px">{{
slot-scope="tags"
>
<a-tag
v-for="(tag, index) in tags"
:key="index"
color="green"
style="margin-bottom: 8px"
>{{
tag.name tag.name
}}</a-tag> }}</a-tag>
</span> </span>
@ -404,10 +300,7 @@
/> />
</span> </span>
<span <span slot="visits" slot-scope="visits">
slot="visits"
slot-scope="visits"
>
<a-badge <a-badge
:count="visits" :count="visits"
:numberStyle="{ backgroundColor: '#00e0ff' }" :numberStyle="{ backgroundColor: '#00e0ff' }"
@ -416,10 +309,7 @@
/> />
</span> </span>
<span <span slot="createTime" slot-scope="createTime">
slot="createTime"
slot-scope="createTime"
>
<a-tooltip placement="top"> <a-tooltip placement="top">
<template slot="title"> <template slot="title">
{{ createTime | moment }} {{ createTime | moment }}
@ -428,15 +318,13 @@
</a-tooltip> </a-tooltip>
</span> </span>
<span <span slot="action" slot-scope="text, post">
slot="action"
slot-scope="text, post"
>
<a <a
href="javascript:;" href="javascript:;"
@click="handleEditClick(post)" @click="handleEditClick(post)"
v-if="post.status === 'PUBLISHED' || post.status === 'DRAFT' || post.status === 'INTIMATE'" v-if="post.status === 'PUBLISHED' || post.status === 'DRAFT' || post.status === 'INTIMATE'"
>编辑</a> >编辑</a
>
<a-popconfirm <a-popconfirm
:title="'你确定要发布【' + post.title + '】文章?'" :title="'你确定要发布【' + post.title + '】文章?'"
@confirm="handleEditStatusClick(post.id, 'PUBLISHED')" @confirm="handleEditStatusClick(post.id, 'PUBLISHED')"
@ -471,10 +359,7 @@
<a-divider type="vertical" /> <a-divider type="vertical" />
<a <a href="javascript:;" @click="handleShowPostSettings(post)"></a>
href="javascript:;"
@click="handleShowPostSettings(post)"
>设置</a>
</span> </span>
</a-table> </a-table>
<div class="page-wrapper"> <div class="page-wrapper">
@ -525,8 +410,6 @@ import { mixin, mixinDevice } from '@/mixins/mixin.js'
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
import PostSettingDrawer from './components/PostSettingDrawer' import PostSettingDrawer from './components/PostSettingDrawer'
import TargetCommentDrawer from '../comment/components/TargetCommentDrawer' import TargetCommentDrawer from '../comment/components/TargetCommentDrawer'
import TagSelect from './components/TagSelect'
import CategoryTree from './components/CategoryTree'
import categoryApi from '@/api/category' import categoryApi from '@/api/category'
import postApi from '@/api/post' import postApi from '@/api/post'
@ -536,57 +419,55 @@ const columns = [
dataIndex: 'title', dataIndex: 'title',
width: '150px', width: '150px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'postTitle' }, scopedSlots: { customRender: 'postTitle' }
}, },
{ {
title: '状态', title: '状态',
className: 'status', className: 'status',
dataIndex: 'statusProperty', dataIndex: 'statusProperty',
width: '100px', width: '100px',
scopedSlots: { customRender: 'status' }, scopedSlots: { customRender: 'status' }
}, },
{ {
title: '分类', title: '分类',
dataIndex: 'categories', dataIndex: 'categories',
scopedSlots: { customRender: 'categories' }, scopedSlots: { customRender: 'categories' }
}, },
{ {
title: '标签', title: '标签',
dataIndex: 'tags', dataIndex: 'tags',
scopedSlots: { customRender: 'tags' }, scopedSlots: { customRender: 'tags' }
}, },
{ {
title: '评论', title: '评论',
width: '70px', width: '70px',
dataIndex: 'commentCount', dataIndex: 'commentCount',
scopedSlots: { customRender: 'commentCount' }, scopedSlots: { customRender: 'commentCount' }
}, },
{ {
title: '访问', title: '访问',
width: '70px', width: '70px',
dataIndex: 'visits', dataIndex: 'visits',
scopedSlots: { customRender: 'visits' }, scopedSlots: { customRender: 'visits' }
}, },
{ {
title: '发布时间', title: '发布时间',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '170px', width: '170px',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '操作', title: '操作',
width: '180px', width: '180px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
name: 'PostList', name: 'PostList',
components: { components: {
PageView, PageView,
TagSelect,
CategoryTree,
PostSettingDrawer, PostSettingDrawer,
TargetCommentDrawer, TargetCommentDrawer
}, },
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
data() { data() {
@ -596,7 +477,7 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
@ -604,7 +485,7 @@ export default {
sort: null, sort: null,
keyword: null, keyword: null,
categoryId: null, categoryId: null,
status: null, status: null
}, },
// //
columns, columns,
@ -613,8 +494,8 @@ export default {
selectedMetas: [ selectedMetas: [
{ {
key: '', key: '',
value: '', value: ''
}, }
], ],
posts: [], posts: [],
postsLoading: false, postsLoading: false,
@ -623,16 +504,16 @@ export default {
postCommentVisible: false, postCommentVisible: false,
selectedPost: {}, selectedPost: {},
selectedTagIds: [], selectedTagIds: [],
selectedCategoryIds: [], selectedCategoryIds: []
} }
}, },
computed: { computed: {
formattedPosts() { formattedPosts() {
return this.posts.map((post) => { return this.posts.map(post => {
post.statusProperty = this.postStatus[post.status] post.statusProperty = this.postStatus[post.status]
return post return post
}) })
}, }
}, },
beforeMount() { beforeMount() {
this.handleListCategories() this.handleListCategories()
@ -643,7 +524,7 @@ export default {
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
next((vm) => { next(vm => {
if (to.query.page) { if (to.query.page) {
vm.pagination.page = Number(to.query.page) + 1 vm.pagination.page = Number(to.query.page) + 1
} }
@ -668,14 +549,14 @@ export default {
watch: { watch: {
queryParam: { queryParam: {
deep: true, deep: true,
handler: function(newVal, oldVal) { handler: function(newVal) {
if (newVal) { if (newVal) {
const params = JSON.parse(JSON.stringify(this.queryParam)) const params = JSON.parse(JSON.stringify(this.queryParam))
const path = this.$router.history.current.path const path = this.$router.history.current.path
this.$router.push({ path, query: params }).catch((err) => err) this.$router.push({ path, query: params }).catch(err => err)
} }
}, }
}, }
}, },
methods: { methods: {
handleListPosts(enableLoading = true) { handleListPosts(enableLoading = true) {
@ -688,7 +569,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
postApi postApi
.query(this.queryParam) .query(this.queryParam)
.then((response) => { .then(response => {
this.posts = response.data.data.content this.posts = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -702,7 +583,7 @@ export default {
this.categoriesLoading = true this.categoriesLoading = true
categoryApi categoryApi
.listAll(true) .listAll(true)
.then((response) => { .then(response => {
this.categories = response.data.data this.categories = response.data.data
}) })
.finally(() => { .finally(() => {
@ -722,8 +603,8 @@ export default {
return { return {
props: { props: {
disabled: this.queryParam.status == null || this.queryParam.status === '', disabled: this.queryParam.status == null || this.queryParam.status === '',
name: post.title, name: post.title
}, }
} }
}, },
handlePaginationChange(page, pageSize) { handlePaginationChange(page, pageSize) {
@ -751,7 +632,7 @@ export default {
handleEditStatusClick(postId, status) { handleEditStatusClick(postId, status) {
postApi postApi
.updateStatus(postId, status) .updateStatus(postId, status)
.then((response) => { .then(() => {
this.$message.success('操作成功!') this.$message.success('操作成功!')
}) })
.finally(() => { .finally(() => {
@ -761,7 +642,7 @@ export default {
handleDeleteClick(postId) { handleDeleteClick(postId) {
postApi postApi
.delete(postId) .delete(postId)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -775,7 +656,7 @@ export default {
} }
postApi postApi
.updateStatusInBatch(this.selectedRowKeys, status) .updateStatusInBatch(this.selectedRowKeys, status)
.then((response) => { .then(() => {
this.$log.debug(`postId: ${this.selectedRowKeys}, status: ${status}`) this.$log.debug(`postId: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = [] this.selectedRowKeys = []
}) })
@ -790,7 +671,7 @@ export default {
} }
postApi postApi
.deleteInBatch(this.selectedRowKeys) .deleteInBatch(this.selectedRowKeys)
.then((response) => { .then(() => {
this.$log.debug(`delete: ${this.selectedRowKeys}`) this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = [] this.selectedRowKeys = []
}) })
@ -799,7 +680,7 @@ export default {
}) })
}, },
handleShowPostSettings(post) { handleShowPostSettings(post) {
postApi.get(post.id).then((response) => { postApi.get(post.id).then(response => {
this.selectedPost = response.data.data this.selectedPost = response.data.data
this.selectedTagIds = this.selectedPost.tagIds this.selectedTagIds = this.selectedPost.tagIds
this.selectedCategoryIds = this.selectedPost.categoryIds this.selectedCategoryIds = this.selectedPost.categoryIds
@ -808,13 +689,13 @@ export default {
}) })
}, },
handleShowPostComments(post) { handleShowPostComments(post) {
postApi.get(post.id).then((response) => { postApi.get(post.id).then(response => {
this.selectedPost = response.data.data this.selectedPost = response.data.data
this.postCommentVisible = true this.postCommentVisible = true
}) })
}, },
handlePreview(postId) { handlePreview(postId) {
postApi.preview(postId).then((response) => { postApi.preview(postId).then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
}) })
}, },
@ -847,7 +728,7 @@ export default {
}, },
onRefreshPostMetasFromSetting(metas) { onRefreshPostMetasFromSetting(metas) {
this.selectedMetas = metas this.selectedMetas = metas
}, }
}, }
} }
</script> </script>

View File

@ -1,49 +1,18 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24" class="pb-3">
:xl="10" <a-card :title="title" :bodyStyle="{ padding: '16px' }">
:lg="10" <a-form-model ref="tagForm" :model="form.model" :rules="form.rules" layout="horizontal">
:md="10" <a-form-model-item label="名称:" help="* 页面上所显示的名称" prop="name">
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
:title="title"
:bodyStyle="{ padding: '16px' }"
>
<a-form-model
ref="tagForm"
:model="form.model"
:rules="form.rules"
layout="horizontal"
>
<a-form-model-item
label="名称:"
help="* 页面上所显示的名称"
prop="name"
>
<a-input v-model="form.model.name" /> <a-input v-model="form.model.name" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="别名:" help="* 一般为单个标签页面的标识,最好为英文" prop="slug">
label="别名:"
help="* 一般为单个标签页面的标识,最好为英文"
prop="slug"
>
<a-input v-model="form.model.slug" /> <a-input v-model="form.model.slug" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="封面图:" help="* 在标签页面可展示,需要主题支持" prop="thumbnail">
label="封面图:"
help="* 在标签页面可展示,需要主题支持"
prop="thumbnail"
>
<a-input v-model="form.model.thumbnail"> <a-input v-model="form.model.thumbnail">
<a <a href="javascript:void(0);" slot="addonAfter" @click="thumbnailDrawer.visible = true">
href="javascript:void(0);"
slot="addonAfter"
@click="thumbnailDrawer.visible = true"
>
<a-icon type="picture" /> <a-icon type="picture" />
</a> </a>
</a-input> </a-input>
@ -71,10 +40,7 @@
loadedText="更新成功" loadedText="更新成功"
erroredText="更新失败" erroredText="更新失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button type="dashed" @click="form.model = {}">返回添加</a-button>
type="dashed"
@click="form.model = {}"
>返回添加</a-button>
</a-button-group> </a-button-group>
<a-popconfirm <a-popconfirm
:title="'你确定要删除【' + form.model.name + '】标签?'" :title="'你确定要删除【' + form.model.name + '】标签?'"
@ -83,43 +49,23 @@
cancelText="取消" cancelText="取消"
v-if="isUpdateMode" v-if="isUpdateMode"
> >
<a-button <a-button type="danger" class="float-right">删除</a-button>
type="danger"
class="float-right"
>删除</a-button>
</a-popconfirm> </a-popconfirm>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24" class="pb-3">
:xl="14" <a-card title="所有标签" :bodyStyle="{ padding: '16px' }">
:lg="14"
:md="14"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
title="所有标签"
:bodyStyle="{ padding: '16px' }"
>
<a-spin :spinning="list.loading"> <a-spin :spinning="list.loading">
<a-empty v-if="list.data.length==0" /> <a-empty v-if="list.data.length == 0" />
<a-tooltip <a-tooltip placement="topLeft" v-for="tag in list.data" :key="tag.id" v-else>
placement="topLeft"
v-for="tag in list.data"
:key="tag.id"
v-else
>
<template slot="title"> <template slot="title">
<span>{{ tag.postCount }} 篇文章</span> <span>{{ tag.postCount }} 篇文章</span>
</template> </template>
<a-tag <a-tag color="blue" style="margin-bottom: 8px;cursor:pointer;" @click="form.model = tag">{{
color="blue" tag.name
style="margin-bottom: 8px;cursor:pointer;" }}</a-tag>
@click="form.model = tag"
>{{ tag.name }}</a-tag>
</a-tooltip> </a-tooltip>
</a-spin> </a-spin>
</a-card> </a-card>

View File

@ -1,11 +1,5 @@
<template> <template>
<a-tree <a-tree checkable :treeData="categoryTree" :defaultExpandAll="true" :checkedKeys="categoryIds" @check="onCheck">
checkable
:treeData="categoryTree"
:defaultExpandAll="true"
:checkedKeys="categoryIds"
@check="onCheck"
>
</a-tree> </a-tree>
</template> </template>

View File

@ -1,7 +1,7 @@
<template> <template>
<a-drawer <a-drawer
title="文章设置" title="文章设置"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
placement="right" placement="right"
closable closable
destroyOnClose destroyOnClose
@ -14,16 +14,10 @@
<h3 class="post-setting-drawer-title">基本设置</h3> <h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item"> <div class="post-setting-drawer-item">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item label="文章标题:" v-if="needTitle">
label="文章标题:"
v-if="needTitle"
>
<a-input v-model="selectedPost.title" /> <a-input v-model="selectedPost.title" />
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="文章别名:" :help="fullPath">
label="文章别名:"
:help="fullPath"
>
<a-input v-model="selectedPost.slug" /> <a-input v-model="selectedPost.slug" />
</a-form-item> </a-form-item>
@ -38,37 +32,21 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="开启评论:"> <a-form-item label="开启评论:">
<a-radio-group <a-radio-group v-model="selectedPost.disallowComment" :defaultValue="false">
v-model="selectedPost.disallowComment"
:defaultValue="false"
>
<a-radio :value="false">开启</a-radio> <a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio> <a-radio :value="true">关闭</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item label="是否置顶:"> <a-form-item label="是否置顶:">
<a-radio-group <a-radio-group v-model="selectedPost.topPriority" :defaultValue="0">
v-model="selectedPost.topPriority"
:defaultValue="0"
>
<a-radio :value="1"></a-radio> <a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio> <a-radio :value="0"></a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="自定义模板:" v-if="customTpls.length > 0">
label="自定义模板:"
v-if="customTpls.length > 0"
>
<a-select v-model="selectedPost.template"> <a-select v-model="selectedPost.template">
<a-select-option <a-select-option key="" value=""></a-select-option>
key="" <a-select-option v-for="tpl in customTpls" :key="tpl" :value="tpl">{{ tpl }}</a-select-option>
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -81,45 +59,22 @@
<div class="post-setting-drawer-item"> <div class="post-setting-drawer-item">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item> <a-form-item>
<category-tree <category-tree v-model="selectedCategoryIds" :categories="categories" />
v-model="selectedCategoryIds"
:categories="categories"
/>
</a-form-item> </a-form-item>
<a-form-item v-if="categoryFormVisible"> <a-form-item v-if="categoryFormVisible">
<category-select-tree <category-select-tree :categories="categories" v-model="categoryToCreate.parentId" />
:categories="categories"
v-model="categoryToCreate.parentId"
/>
</a-form-item> </a-form-item>
<a-form-item v-if="categoryFormVisible"> <a-form-item v-if="categoryFormVisible">
<a-input <a-input placeholder="分类名称" v-model="categoryToCreate.name" />
placeholder="分类名称"
v-model="categoryToCreate.name"
/>
</a-form-item> </a-form-item>
<a-form-item v-if="categoryFormVisible"> <a-form-item v-if="categoryFormVisible">
<a-input <a-input placeholder="分类路径" v-model="categoryToCreate.slug" />
placeholder="分类路径"
v-model="categoryToCreate.slug"
/>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-space> <a-space>
<a-button <a-button type="primary" v-if="categoryFormVisible" @click="handlerCreateCategory"></a-button>
type="primary" <a-button type="dashed" v-if="!categoryFormVisible" @click="categoryFormVisible = true"></a-button>
v-if="categoryFormVisible" <a-button v-if="categoryFormVisible" @click="categoryFormVisible = false"></a-button>
@click="handlerCreateCategory"
>保存</a-button>
<a-button
type="dashed"
v-if="!categoryFormVisible"
@click="categoryFormVisible = true"
>新增</a-button>
<a-button
v-if="categoryFormVisible"
@click="categoryFormVisible = false"
>取消</a-button>
</a-space> </a-space>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -163,37 +118,26 @@
<img <img
class="img" class="img"
:src="selectedPost.thumbnail || '/images/placeholder.jpg'" :src="selectedPost.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible=true" @click="thumbDrawerVisible = true"
> />
<a-form layout="vertial"> <a-form layout="vertial">
<a-form-item> <a-form-item>
<a-input <a-input v-model="selectedPost.thumbnail" placeholder="点击封面图选择图片,或者输入外部链接"></a-input>
v-model="selectedPost.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-button <a-button class="post-thumb-remove" type="dashed" @click="selectedPost.thumbnail = null">移除</a-button>
class="post-thumb-remove"
type="dashed"
@click="selectedPost.thumbnail = null"
>移除</a-button>
</div> </div>
</div> </div>
</div> </div>
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
</div> </div>
<AttachmentSelectDrawer <AttachmentSelectDrawer v-model="thumbDrawerVisible" @listenToSelect="handleSelectPostThumb" :drawerWidth="480" />
v-model="thumbDrawerVisible"
@listenToSelect="handleSelectPostThumb"
:drawerWidth="480"
/>
<a-drawer <a-drawer
title="高级设置" title="高级设置"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
placement="right" placement="right"
closable closable
destroyOnClose destroyOnClose
@ -206,10 +150,7 @@
<div class="post-setting-drawer-item"> <div class="post-setting-drawer-item">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item label="访问密码:"> <a-form-item label="访问密码:">
<a-input-password <a-input-password v-model="selectedPost.password" autocomplete="new-password" />
v-model="selectedPost.password"
autocomplete="new-password"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
@ -240,11 +181,7 @@
<div class="mb-4"> <div class="mb-4">
<h3 class="post-setting-drawer-title">元数据</h3> <h3 class="post-setting-drawer-title">元数据</h3>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item v-for="(meta, index) in selectedMetas" :key="index" :prop="'metas.' + index + '.value'">
v-for="(meta, index) in selectedMetas"
:key="index"
:prop="'metas.' + index + '.value'"
>
<a-row :gutter="5"> <a-row :gutter="5">
<a-col :span="12"> <a-col :span="12">
<a-input v-model="meta.key"><i slot="addonBefore">K</i></a-input> <a-input v-model="meta.key"><i slot="addonBefore">K</i></a-input>
@ -252,11 +189,7 @@
<a-col :span="12"> <a-col :span="12">
<a-input v-model="meta.value"> <a-input v-model="meta.value">
<i slot="addonBefore">V</i> <i slot="addonBefore">V</i>
<a <a href="javascript:void(0);" slot="addonAfter" @click.prevent="handleRemovePostMeta(meta)">
href="javascript:void(0);"
slot="addonAfter"
@click.prevent="handleRemovePostMeta(meta)"
>
<a-icon type="close" /> <a-icon type="close" />
</a> </a>
</a-input> </a-input>
@ -264,10 +197,7 @@
</a-row> </a-row>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button <a-button type="dashed" @click="handleInsertPostMeta"></a-button>
type="dashed"
@click="handleInsertPostMeta"
>新增</a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
@ -282,10 +212,7 @@
<div class="bottom-control"> <div class="bottom-control">
<a-space> <a-space>
<a-button <a-button type="dashed" @click="advancedVisible = true">高级</a-button>
type="dashed"
@click="advancedVisible = true"
>高级</a-button>
<ReactiveButton <ReactiveButton
type="danger" type="danger"
v-if="saveDraftButton" v-if="saveDraftButton"
@ -302,9 +229,9 @@
@callback="handleSavedCallback" @callback="handleSavedCallback"
:loading="saving" :loading="saving"
:errored="savedErrored" :errored="savedErrored"
:text="`${selectedPost.id?'保存':'发布'}`" :text="`${selectedPost.id ? '保存' : '发布'}`"
:loadedText="`${selectedPost.id?'保存':'发布'}成功`" :loadedText="`${selectedPost.id ? '保存' : '发布'}成功`"
:erroredText="`${selectedPost.id?'保存':'发布'}失败`" :erroredText="`${selectedPost.id ? '保存' : '发布'}失败`"
></ReactiveButton> ></ReactiveButton>
</a-space> </a-space>
</div> </div>
@ -333,7 +260,7 @@ export default {
components: { components: {
CategoryTree, CategoryTree,
CategorySelectTree, CategorySelectTree,
TagSelect, TagSelect
}, },
data() { data() {
return { return {
@ -349,41 +276,41 @@ export default {
saving: false, saving: false,
savedErrored: false, savedErrored: false,
draftSaving: false, draftSaving: false,
draftSavedErrored: false, draftSavedErrored: false
} }
}, },
props: { props: {
post: { post: {
type: Object, type: Object,
required: true, required: true
}, },
tagIds: { tagIds: {
type: Array, type: Array,
required: true, required: true
}, },
categoryIds: { categoryIds: {
type: Array, type: Array,
required: true, required: true
}, },
metas: { metas: {
type: Array, type: Array,
required: true, required: true
}, },
needTitle: { needTitle: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false
}, },
saveDraftButton: { saveDraftButton: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, },
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
watch: { watch: {
post(val) { post(val) {
@ -406,7 +333,7 @@ export default {
}, },
selectedMetas(val) { selectedMetas(val) {
this.$emit('onRefreshPostMetas', val) this.$emit('onRefreshPostMetas', val)
}, }
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options']),
@ -452,7 +379,7 @@ export default {
default: default:
return '' return ''
} }
}, }
}, },
methods: { methods: {
handleAfterVisibleChanged(visible) { handleAfterVisibleChanged(visible) {
@ -464,19 +391,19 @@ export default {
} }
}, },
handleListCategories() { handleListCategories() {
categoryApi.listAll().then((response) => { categoryApi.listAll().then(response => {
this.categories = response.data.data this.categories = response.data.data
}) })
}, },
handleListPresetMetasField() { handleListPresetMetasField() {
if (this.metas.length <= 0) { if (this.metas.length <= 0) {
themeApi.getActivatedTheme().then((response) => { themeApi.getActivatedTheme().then(response => {
const fields = response.data.data.postMetaField const fields = response.data.data.postMetaField
if (fields && fields.length > 0) { if (fields && fields.length > 0) {
for (let i = 0, len = fields.length; i < len; i++) { for (let i = 0, len = fields.length; i < len; i++) {
this.selectedMetas.push({ this.selectedMetas.push({
value: '', value: '',
key: fields[i], key: fields[i]
}) })
} }
} }
@ -484,7 +411,7 @@ export default {
} }
}, },
handleListCustomTpls() { handleListCustomTpls() {
themeApi.customPostTpls().then((response) => { themeApi.customPostTpls().then(response => {
this.customTpls = response.data.data this.customTpls = response.data.data
}) })
}, },
@ -496,13 +423,13 @@ export default {
if (!this.categoryToCreate.name) { if (!this.categoryToCreate.name) {
this.$notification['error']({ this.$notification['error']({
message: '提示', message: '提示',
description: '分类名称不能为空!', description: '分类名称不能为空!'
}) })
return return
} }
categoryApi categoryApi
.create(this.categoryToCreate) .create(this.categoryToCreate)
.then((response) => { .then(() => {
this.categoryToCreate = {} this.categoryToCreate = {}
this.categoryFormVisible = false this.categoryFormVisible = false
}) })
@ -522,7 +449,7 @@ export default {
if (!this.selectedPost.title) { if (!this.selectedPost.title) {
this.$notification['error']({ this.$notification['error']({
message: '提示', message: '提示',
description: '文章标题不能为空!', description: '文章标题不能为空!'
}) })
return return
} }
@ -565,7 +492,7 @@ export default {
this.savedErrored = true this.savedErrored = true
} }
}) })
.then((response) => { .then(response => {
this.selectedPost = response.data.data this.selectedPost = response.data.data
}) })
.finally(() => { .finally(() => {
@ -588,7 +515,7 @@ export default {
onClose() { onClose() {
this.$emit('close', false) this.$emit('close', false)
}, },
onPostDateChange(value, dateString) { onPostDateChange(value) {
this.selectedPost.createTime = value.valueOf() this.selectedPost.createTime = value.valueOf()
}, },
onPostDateOk(value) { onPostDateOk(value) {
@ -603,7 +530,7 @@ export default {
handleInsertPostMeta() { handleInsertPostMeta() {
this.selectedMetas.push({ this.selectedMetas.push({
value: '', value: '',
key: '', key: ''
}) })
}, },
handleSetPinyinSlug() { handleSetPinyinSlug() {
@ -612,7 +539,7 @@ export default {
let result = '' let result = ''
const tokens = pinyin.parse(this.selectedPost.title) const tokens = pinyin.parse(this.selectedPost.title)
let lastToken let lastToken
tokens.forEach((token, i) => { tokens.forEach(token => {
if (token.type === 2) { if (token.type === 2) {
const target = token.target ? token.target.toLowerCase() : '' const target = token.target ? token.target.toLowerCase() : ''
result += result && !/\n|\s/.test(lastToken.target) ? '-' + target : target result += result && !/\n|\s/.test(lastToken.target) ? '-' + target : target
@ -624,7 +551,7 @@ export default {
this.$set(this.selectedPost, 'slug', result) this.$set(this.selectedPost, 'slug', result)
} }
} }
}, }
}, }
} }
</script> </script>

View File

@ -8,11 +8,7 @@
placeholder="选择或输入标签" placeholder="选择或输入标签"
@change="handleChange" @change="handleChange"
> >
<a-select-option <a-select-option v-for="tag in tags" :key="tag.id" :value="tag.name">{{ tag.name }}</a-select-option>
v-for="tag in tags"
:key="tag.id"
:value="tag.name"
>{{ tag.name }}</a-select-option>
</a-select> </a-select>
</div> </div>
</template> </template>
@ -44,8 +40,7 @@ export default {
this.handleListTags() this.handleListTags()
}, },
watch: { watch: {
tags(newValue, oldValue) { tags(newValue) {
// tags使
if (newValue) { if (newValue) {
this.selectedTagNames = this.tagIds.map(tagId => this.tagIdMap[tagId].name) this.selectedTagNames = this.tagIds.map(tagId => this.tagIdMap[tagId].name)
} }

View File

@ -1,8 +1,5 @@
<template> <template>
<page-view <page-view affix :title="sheetToStage.title ? sheetToStage.title : '新页面'">
affix
:title="sheetToStage.title?sheetToStage.title:'新页面'"
>
<template slot="extra"> <template slot="extra">
<a-space> <a-space>
<ReactiveButton <ReactiveButton
@ -15,28 +12,15 @@
loadedText="保存成功" loadedText="保存成功"
erroredText="保存失败" erroredText="保存失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button @click="handlePreview" :loading="previewSaving">预览</a-button>
@click="handlePreview" <a-button type="primary" @click="sheetSettingVisible = true">发布</a-button>
:loading="previewSaving" <a-button type="dashed" @click="attachmentDrawerVisible = true">附件库</a-button>
>预览</a-button>
<a-button
type="primary"
@click="sheetSettingVisible = true"
>发布</a-button>
<a-button
type="dashed"
@click="attachmentDrawerVisible = true"
>附件库</a-button>
</a-space> </a-space>
</template> </template>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col :span="24"> <a-col :span="24">
<div class="mb-4"> <div class="mb-4">
<a-input <a-input v-model="sheetToStage.title" size="large" placeholder="请输入页面标题" />
v-model="sheetToStage.title"
size="large"
placeholder="请输入页面标题"
/>
</div> </div>
<div id="editor"> <div id="editor">
@ -76,7 +60,6 @@ import { datetimeFormat } from '@/utils/datetime'
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
import SheetSettingDrawer from './components/SheetSettingDrawer' import SheetSettingDrawer from './components/SheetSettingDrawer'
import AttachmentDrawer from '../attachment/components/AttachmentDrawer' import AttachmentDrawer from '../attachment/components/AttachmentDrawer'
import FooterToolBar from '@/components/FooterToolbar'
import MarkdownEditor from '@/components/Editor/MarkdownEditor' import MarkdownEditor from '@/components/Editor/MarkdownEditor'
// import RichTextEditor from '@/components/editor/RichTextEditor' // import RichTextEditor from '@/components/editor/RichTextEditor'
@ -84,10 +67,9 @@ import sheetApi from '@/api/sheet'
export default { export default {
components: { components: {
PageView, PageView,
FooterToolBar,
AttachmentDrawer, AttachmentDrawer,
SheetSettingDrawer, SheetSettingDrawer,
MarkdownEditor, MarkdownEditor
// RichTextEditor // RichTextEditor
}, },
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
@ -100,16 +82,16 @@ export default {
contentChanges: 0, contentChanges: 0,
draftSaving: false, draftSaving: false,
draftSavederrored: false, draftSavederrored: false,
previewSaving: false, previewSaving: false
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
// Get sheetId id from query // Get sheetId id from query
const sheetId = to.query.sheetId const sheetId = to.query.sheetId
next((vm) => { next(vm => {
if (sheetId) { if (sheetId) {
sheetApi.get(sheetId).then((response) => { sheetApi.get(sheetId).then(response => {
const sheet = response.data.data const sheet = response.data.data
vm.sheetToStage = sheet vm.sheetToStage = sheet
vm.selectedMetas = sheet.metas vm.selectedMetas = sheet.metas
@ -140,13 +122,13 @@ export default {
} else { } else {
this.$confirm({ this.$confirm({
title: '当前页面数据未保存,确定要离开吗?', title: '当前页面数据未保存,确定要离开吗?',
content: (h) => <div style="color:red;">如果离开当面页面你的数据很可能会丢失</div>, content: () => <div style="color:red;">如果离开当面页面你的数据很可能会丢失</div>,
onOk() { onOk() {
next() next()
}, },
onCancel() { onCancel() {
next(false) next(false)
}, }
}) })
} }
}, },
@ -163,16 +145,16 @@ export default {
// } // }
}, },
watch: { watch: {
temporaryContent: function(newValue, oldValue) { temporaryContent(newValue) {
if (newValue) { if (newValue) {
this.contentChanges++ this.contentChanges++
} }
}, }
}, },
computed: { computed: {
temporaryContent() { temporaryContent() {
return this.sheetToStage.originalContent return this.sheetToStage.originalContent
}, }
// ...mapGetters(['options']) // ...mapGetters(['options'])
}, },
methods: { methods: {
@ -187,7 +169,7 @@ export default {
if (draftOnly) { if (draftOnly) {
sheetApi sheetApi
.updateDraft(this.sheetToStage.id, this.sheetToStage.originalContent) .updateDraft(this.sheetToStage.id, this.sheetToStage.originalContent)
.then((response) => { .then(() => {
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
.catch(() => { .catch(() => {
@ -201,7 +183,7 @@ export default {
} else { } else {
sheetApi sheetApi
.update(this.sheetToStage.id, this.sheetToStage, false) .update(this.sheetToStage.id, this.sheetToStage, false)
.then((response) => { .then(response => {
this.sheetToStage = response.data.data this.sheetToStage = response.data.data
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -217,7 +199,7 @@ export default {
} else { } else {
sheetApi sheetApi
.create(this.sheetToStage, false) .create(this.sheetToStage, false)
.then((response) => { .then(response => {
this.sheetToStage = response.data.data this.sheetToStage = response.data.data
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -238,11 +220,11 @@ export default {
} }
this.previewSaving = true this.previewSaving = true
if (this.sheetToStage.id) { if (this.sheetToStage.id) {
sheetApi.update(this.sheetToStage.id, this.sheetToStage, false).then((response) => { sheetApi.update(this.sheetToStage.id, this.sheetToStage, false).then(response => {
this.$log.debug('Updated sheet', response.data.data) this.$log.debug('Updated sheet', response.data.data)
sheetApi sheetApi
.preview(this.sheetToStage.id) .preview(this.sheetToStage.id)
.then((response) => { .then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -253,12 +235,12 @@ export default {
}) })
}) })
} else { } else {
sheetApi.create(this.sheetToStage, false).then((response) => { sheetApi.create(this.sheetToStage, false).then(response => {
this.$log.debug('Created sheet', response.data.data) this.$log.debug('Created sheet', response.data.data)
this.sheetToStage = response.data.data this.sheetToStage = response.data.data
sheetApi sheetApi
.preview(this.sheetToStage.id) .preview(this.sheetToStage.id)
.then((response) => { .then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
this.handleRestoreSavedStatus() this.handleRestoreSavedStatus()
}) })
@ -281,7 +263,7 @@ export default {
}, },
onRefreshSheetMetasFromSetting(metas) { onRefreshSheetMetasFromSetting(metas) {
this.selectedMetas = metas this.selectedMetas = metas
}, }
}, }
} }
</script> </script>

View File

@ -3,17 +3,9 @@
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<div class="card-container"> <div class="card-container">
<a-tabs <a-tabs v-model="activeKey" type="card">
v-model="activeKey" <a-tab-pane v-for="pane in panes" :key="pane.key">
type="card" <span slot="tab"> <a-icon :type="pane.icon" />{{ pane.title }} </span>
>
<a-tab-pane
v-for="pane in panes"
:key="pane.key"
>
<span slot="tab">
<a-icon :type="pane.icon" />{{ pane.title }}
</span>
<component :is="pane.component"></component> <component :is="pane.component"></component>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@ -32,36 +24,34 @@ export default {
components: { components: {
PageView, PageView,
IndependentSheetList, IndependentSheetList,
CustomSheetList, CustomSheetList
}, },
data() { data() {
const panes = [ const panes = [
{ title: '独立页面', icon: 'paper-clip', component: 'IndependentSheetList', key: 'independent' }, { title: '独立页面', icon: 'paper-clip', component: 'IndependentSheetList', key: 'independent' },
{ title: '自定义页面', icon: 'fork', component: 'CustomSheetList', key: 'custom' }, { title: '自定义页面', icon: 'fork', component: 'CustomSheetList', key: 'custom' }
] ]
return { return {
activeKey: panes[0].key, activeKey: panes[0].key,
panes, panes
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
// Get post id from query // Get post id from query
const activeKey = to.query.activeKey const activeKey = to.query.activeKey
next((vm) => { next(vm => {
if (activeKey) { if (activeKey) {
vm.activeKey = activeKey vm.activeKey = activeKey
} }
}) })
}, },
watch: { watch: {
activeKey: { activeKey(newVal) {
handler: function(newVal, oldVal) { if (newVal) {
if (newVal) { const path = this.$router.history.current.path
const path = this.$router.history.current.path this.$router.push({ path, query: { activeKey: newVal } }).catch(err => err)
this.$router.push({ path, query: { activeKey: newVal } }).catch((err) => err) }
} }
}, }
},
},
} }
</script> </script>

View File

@ -9,11 +9,7 @@
:dataSource="formattedSheets" :dataSource="formattedSheets"
:loading="loading" :loading="loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<span> <span>
<a-icon type="eye" /> <a-icon type="eye" />
@ -23,24 +19,18 @@
<a-icon type="message" /> <a-icon type="message" />
{{ item.commentCount }} {{ item.commentCount }}
</span> </span>
<a-dropdown <a-dropdown placement="topLeft" :trigger="['click']">
placement="topLeft"
:trigger="['click']"
>
<span> <span>
<a-icon type="bars" /> <a-icon type="bars" />
</span> </span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT'"> <a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT'">
<a <a href="javascript:;" @click="handleEditClick(item)"></a>
href="javascript:;"
@click="handleEditClick(item)"
>编辑</a>
</a-menu-item> </a-menu-item>
<a-menu-item v-else-if="item.status === 'RECYCLE'"> <a-menu-item v-else-if="item.status === 'RECYCLE'">
<a-popconfirm <a-popconfirm
:title="'你确定要发布【' + item.title + '】页面?'" :title="'你确定要发布【' + item.title + '】页面?'"
@confirm="handleEditStatusClick(item.id,'PUBLISHED')" @confirm="handleEditStatusClick(item.id, 'PUBLISHED')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
@ -50,7 +40,7 @@
<a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT'"> <a-menu-item v-if="item.status === 'PUBLISHED' || item.status === 'DRAFT'">
<a-popconfirm <a-popconfirm
:title="'你确定要将【' + item.title + '】页面移到回收站?'" :title="'你确定要将【' + item.title + '】页面移到回收站?'"
@confirm="handleEditStatusClick(item.id,'RECYCLE')" @confirm="handleEditStatusClick(item.id, 'RECYCLE')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
@ -68,21 +58,14 @@
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a <a rel="noopener noreferrer" href="javascript:void(0);" @click="handleShowSheetSettings(item)"></a>
rel="noopener noreferrer"
href="javascript:void(0);"
@click="handleShowSheetSettings(item)"
>设置</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
</template> </template>
<template slot="extra"> <template slot="extra">
<span> <span>
<a-badge <a-badge :status="item.statusProperty.status" :text="item.statusProperty.text" />
:status="item.statusProperty.status"
:text="item.statusProperty.text"
/>
</span> </span>
</template> </template>
<a-list-item-meta> <a-list-item-meta>
@ -93,42 +76,23 @@
slot="title" slot="title"
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;" style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
> >
<a <a v-if="item.status == 'PUBLISHED'" :href="item.fullPath" target="_blank" class="no-underline">
v-if="item.status=='PUBLISHED'" <a-tooltip placement="top" :title="'点击访问【' + item.title + '】'">{{ item.title }}</a-tooltip>
:href="item.fullPath"
target="_blank"
class="no-underline"
>
<a-tooltip
placement="top"
:title="'点击访问【'+item.title+'】'"
>{{ item.title }}</a-tooltip>
</a> </a>
<a <a
v-else-if="item.status=='DRAFT'" v-else-if="item.status == 'DRAFT'"
href="javascript:void(0)" href="javascript:void(0)"
class="no-underline" class="no-underline"
@click="handlePreview(item.id)" @click="handlePreview(item.id)"
> >
<a-tooltip <a-tooltip placement="topLeft" :title="'点击预览【' + item.title + '】'">{{ item.title }}</a-tooltip>
placement="topLeft"
:title="'点击预览【'+item.title+'】'"
>{{ item.title }}</a-tooltip>
</a> </a>
<a <a v-else href="javascript:void(0);" class="no-underline" disabled>
v-else
href="javascript:void(0);"
class="no-underline"
disabled
>
{{ item.title }} {{ item.title }}
</a> </a>
</span> </span>
</a-list-item-meta> </a-list-item-meta>
<span> <span> {{ item.summary }}... </span>
{{ item.summary }}...
</span>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -142,82 +106,46 @@
:loading="loading" :loading="loading"
:scrollToFirstRowOnChange="true" :scrollToFirstRowOnChange="true"
> >
<span <span slot="sheetTitle" slot-scope="text, record">
slot="sheetTitle" <a v-if="record.status == 'PUBLISHED'" :href="record.fullPath" target="_blank" class="no-underline">
slot-scope="text,record" <a-tooltip placement="top" :title="'点击访问【' + text + '】'">{{ text }}</a-tooltip>
>
<a
v-if="record.status=='PUBLISHED'"
:href="record.fullPath"
target="_blank"
class="no-underline"
>
<a-tooltip
placement="top"
:title="'点击访问【'+text+'】'"
>{{ text }}</a-tooltip>
</a> </a>
<a <a
v-else-if="record.status=='DRAFT'" v-else-if="record.status == 'DRAFT'"
href="javascript:void(0)" href="javascript:void(0)"
class="no-underline" class="no-underline"
@click="handlePreview(record.id)" @click="handlePreview(record.id)"
> >
<a-tooltip <a-tooltip placement="topLeft" :title="'点击预览【' + text + '】'">{{ text }}</a-tooltip>
placement="topLeft"
:title="'点击预览【'+text+'】'"
>{{ text }}</a-tooltip>
</a> </a>
<a <a v-else href="javascript:void(0);" class="no-underline" disabled>
v-else
href="javascript:void(0);"
class="no-underline"
disabled
>
{{ text }} {{ text }}
</a> </a>
</span> </span>
<span <span slot="status" slot-scope="statusProperty">
slot="status" <a-badge :status="statusProperty.status" :text="statusProperty.text" />
slot-scope="statusProperty"
>
<a-badge
:status="statusProperty.status"
:text="statusProperty.text"
/>
</span> </span>
<span <span
slot="commentCount" slot="commentCount"
slot-scope="text,record" slot-scope="text, record"
@click="handleShowSheetComments(record)" @click="handleShowSheetComments(record)"
class="cursor-pointer" class="cursor-pointer"
> >
<a-badge <a-badge
:count="record.commentCount" :count="record.commentCount"
:numberStyle="{backgroundColor: '#f38181'} " :numberStyle="{ backgroundColor: '#f38181' }"
:showZero="true" :showZero="true"
:overflowCount="999" :overflowCount="999"
/> />
</span> </span>
<span <span slot="visits" slot-scope="visits">
slot="visits" <a-badge :count="visits" :numberStyle="{ backgroundColor: '#00e0ff' }" :showZero="true" :overflowCount="9999" />
slot-scope="visits"
>
<a-badge
:count="visits"
:numberStyle="{backgroundColor: '#00e0ff'} "
:showZero="true"
:overflowCount="9999"
/>
</span> </span>
<span <span slot="createTime" slot-scope="createTime">
slot="createTime"
slot-scope="createTime"
>
<a-tooltip placement="top"> <a-tooltip placement="top">
<template slot="title"> <template slot="title">
{{ createTime | moment }} {{ createTime | moment }}
@ -226,19 +154,17 @@
</a-tooltip> </a-tooltip>
</span> </span>
<span <span slot="action" slot-scope="text, sheet">
slot="action"
slot-scope="text, sheet"
>
<a <a
href="javascript:;" href="javascript:;"
@click="handleEditClick(sheet)" @click="handleEditClick(sheet)"
v-if="sheet.status === 'PUBLISHED' || sheet.status === 'DRAFT'" v-if="sheet.status === 'PUBLISHED' || sheet.status === 'DRAFT'"
>编辑</a> >编辑</a
>
<a-popconfirm <a-popconfirm
:title="'你确定要发布【' + sheet.title + '】?'" :title="'你确定要发布【' + sheet.title + '】?'"
@confirm="handleEditStatusClick(sheet.id,'PUBLISHED')" @confirm="handleEditStatusClick(sheet.id, 'PUBLISHED')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
v-else-if="sheet.status === 'RECYCLE'" v-else-if="sheet.status === 'RECYCLE'"
@ -250,7 +176,7 @@
<a-popconfirm <a-popconfirm
:title="'你确定要将【' + sheet.title + '】页面移到回收站?'" :title="'你确定要将【' + sheet.title + '】页面移到回收站?'"
@confirm="handleEditStatusClick(sheet.id,'RECYCLE')" @confirm="handleEditStatusClick(sheet.id, 'RECYCLE')"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
v-if="sheet.status === 'PUBLISHED' || sheet.status === 'DRAFT'" v-if="sheet.status === 'PUBLISHED' || sheet.status === 'DRAFT'"
@ -268,10 +194,7 @@
<a href="javascript:;">删除</a> <a href="javascript:;">删除</a>
</a-popconfirm> </a-popconfirm>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a <a href="javascript:void(0);" @click="handleShowSheetSettings(sheet)"></a>
href="javascript:void(0);"
@click="handleShowSheetSettings(sheet)"
>设置</a>
</span> </span>
</a-table> </a-table>
<div class="page-wrapper"> <div class="page-wrapper">
@ -319,41 +242,41 @@ const customColumns = [
title: '标题', title: '标题',
dataIndex: 'title', dataIndex: 'title',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'sheetTitle' }, scopedSlots: { customRender: 'sheetTitle' }
}, },
{ {
title: '状态', title: '状态',
className: 'status', className: 'status',
dataIndex: 'statusProperty', dataIndex: 'statusProperty',
scopedSlots: { customRender: 'status' }, scopedSlots: { customRender: 'status' }
}, },
{ {
title: '评论量', title: '评论量',
dataIndex: 'commentCount', dataIndex: 'commentCount',
scopedSlots: { customRender: 'commentCount' }, scopedSlots: { customRender: 'commentCount' }
}, },
{ {
title: '访问量', title: '访问量',
dataIndex: 'visits', dataIndex: 'visits',
scopedSlots: { customRender: 'visits' }, scopedSlots: { customRender: 'visits' }
}, },
{ {
title: '发布时间', title: '发布时间',
dataIndex: 'createTime', dataIndex: 'createTime',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '操作', title: '操作',
width: '180px', width: '180px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
name: 'CustomSheetList', name: 'CustomSheetList',
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
SheetSettingDrawer, SheetSettingDrawer,
TargetCommentDrawer, TargetCommentDrawer
}, },
data() { data() {
return { return {
@ -361,7 +284,7 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
@ -369,7 +292,7 @@ export default {
sort: null, sort: null,
keyword: null, keyword: null,
categoryId: null, categoryId: null,
status: null, status: null
}, },
loading: false, loading: false,
sheetStatus: sheetApi.sheetStatus, sheetStatus: sheetApi.sheetStatus,
@ -379,16 +302,16 @@ export default {
sheetSettingVisible: false, sheetSettingVisible: false,
sheetCommentVisible: false, sheetCommentVisible: false,
sheets: [], sheets: [],
menu: {}, menu: {}
} }
}, },
computed: { computed: {
formattedSheets() { formattedSheets() {
return this.sheets.map((sheet) => { return this.sheets.map(sheet => {
sheet.statusProperty = this.sheetStatus[sheet.status] sheet.statusProperty = this.sheetStatus[sheet.status]
return sheet return sheet
}) })
}, }
}, },
created() { created() {
this.handleListSheets() this.handleListSheets()
@ -414,7 +337,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
sheetApi sheetApi
.list(this.queryParam) .list(this.queryParam)
.then((response) => { .then(response => {
this.sheets = response.data.data.content this.sheets = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -430,7 +353,7 @@ export default {
handleEditStatusClick(sheetId, status) { handleEditStatusClick(sheetId, status) {
sheetApi sheetApi
.updateStatus(sheetId, status) .updateStatus(sheetId, status)
.then((response) => { .then(() => {
this.$message.success('操作成功!') this.$message.success('操作成功!')
}) })
.finally(() => { .finally(() => {
@ -440,7 +363,7 @@ export default {
handleDeleteClick(sheetId) { handleDeleteClick(sheetId) {
sheetApi sheetApi
.delete(sheetId) .delete(sheetId)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -448,20 +371,20 @@ export default {
}) })
}, },
handleShowSheetSettings(sheet) { handleShowSheetSettings(sheet) {
sheetApi.get(sheet.id).then((response) => { sheetApi.get(sheet.id).then(response => {
this.selectedSheet = response.data.data this.selectedSheet = response.data.data
this.selectedMetas = this.selectedSheet.metas this.selectedMetas = this.selectedSheet.metas
this.sheetSettingVisible = true this.sheetSettingVisible = true
}) })
}, },
handleShowSheetComments(sheet) { handleShowSheetComments(sheet) {
sheetApi.get(sheet.id).then((response) => { sheetApi.get(sheet.id).then(response => {
this.selectedSheet = response.data.data this.selectedSheet = response.data.data
this.sheetCommentVisible = true this.sheetCommentVisible = true
}) })
}, },
handlePreview(sheetId) { handlePreview(sheetId) {
sheetApi.preview(sheetId).then((response) => { sheetApi.preview(sheetId).then(response => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
}) })
}, },
@ -490,7 +413,7 @@ export default {
}, },
onRefreshSheetMetasFromSetting(metas) { onRefreshSheetMetasFromSetting(metas) {
this.selectedMetas = metas this.selectedMetas = metas
}, }
}, }
} }
</script> </script>

View File

@ -9,25 +9,19 @@
:dataSource="independentSheets" :dataSource="independentSheets"
:loading="loading" :loading="loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<span> <span>
<router-link :to="{name:item.routeName}"> <router-link :to="{ name: item.routeName }">
<a-icon type="edit" /> <a-icon type="edit" />
</router-link> </router-link>
</span> </span>
</template> </template>
<template slot="extra"> <template slot="extra">
<span v-if="item.available"></span> <span v-if="item.available"></span>
<span v-else> <span v-else
<a-tooltip >不可用
slot="action" <a-tooltip slot="action" title="当前主题没有对应模板">
title="当前主题没有对应模板"
>
<a-icon type="info-circle-o" /> <a-icon type="info-circle-o" />
</a-tooltip> </a-tooltip>
</span> </span>
@ -37,19 +31,9 @@
slot="title" slot="title"
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;" style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
> >
<a <a :href="item.fullPath" target="_blank" v-if="item.available">{{ item.title }}</a>
:href="item.fullPath" <a :href="item.fullPath" target="_blank" disabled v-else>{{ item.title }}</a>
target="_blank"
v-if="item.available"
>{{ item.title }}</a>
<a
:href="item.fullPath"
target="_blank"
disabled
v-else
>{{ item.title }}</a>
</span> </span>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -63,39 +47,22 @@
:rowKey="sheet => sheet.id" :rowKey="sheet => sheet.id"
:loading="loading" :loading="loading"
> >
<template <template slot="available" slot-scope="available">
slot="available"
slot-scope="available"
>
<span v-if="available"></span> <span v-if="available"></span>
<span v-else> <span v-else
<a-tooltip >不可用
slot="action" <a-tooltip slot="action" title="当前主题没有对应模板">
title="当前主题没有对应模板"
>
<a-icon type="info-circle-o" /> <a-icon type="info-circle-o" />
</a-tooltip> </a-tooltip>
</span> </span>
</template> </template>
<span <span slot="action" slot-scope="text, record">
slot="action" <router-link :to="{ name: record.routeName }">
slot-scope="text, record"
>
<router-link :to="{name:record.routeName}">
<a href="javascript:void(0);">管理</a> <a href="javascript:void(0);">管理</a>
</router-link> </router-link>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a <a :href="record.fullPath" target="_blank" v-if="record.available">访</a>
:href="record.fullPath" <a :href="record.fullPath" target="_blank" disabled v-else>访</a>
target="_blank"
v-if="record.available"
>访问</a>
<a
:href="record.fullPath"
target="_blank"
disabled
v-else
>访问</a>
</span> </span>
</a-table> </a-table>
</div> </div>

View File

@ -1,7 +1,7 @@
<template> <template>
<a-drawer <a-drawer
title="页面设置" title="页面设置"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
placement="right" placement="right"
closable closable
destroyOnClose destroyOnClose
@ -14,16 +14,10 @@
<h3 class="post-setting-drawer-title">基本设置</h3> <h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item"> <div class="post-setting-drawer-item">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item label="页面标题:" v-if="needTitle">
label="页面标题:"
v-if="needTitle"
>
<a-input v-model="selectedSheet.title" /> <a-input v-model="selectedSheet.title" />
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="页面别名:" :help="fullPath">
label="页面别名:"
:help="fullPath"
>
<a-input v-model="selectedSheet.slug" /> <a-input v-model="selectedSheet.slug" />
</a-form-item> </a-form-item>
<a-form-item label="发表时间:"> <a-form-item label="发表时间:">
@ -37,31 +31,15 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="开启评论:"> <a-form-item label="开启评论:">
<a-radio-group <a-radio-group v-model="selectedSheet.disallowComment" :defaultValue="false">
v-model="selectedSheet.disallowComment"
:defaultValue="false"
>
<a-radio :value="false">开启</a-radio> <a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio> <a-radio :value="true">关闭</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="自定义模板:" v-if="customTpls.length > 0">
label="自定义模板:" <a-select v-model="selectedSheet.template" :loading="customTplsLoading">
v-if="customTpls.length>0" <a-select-option key="" value=""></a-select-option>
> <a-select-option v-for="tpl in customTpls" :key="tpl" :value="tpl">{{ tpl }}</a-select-option>
<a-select
v-model="selectedSheet.template"
:loading="customTplsLoading"
>
<a-select-option
key=""
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -94,36 +72,25 @@
class="img" class="img"
:src="selectedSheet.thumbnail || '/images/placeholder.jpg'" :src="selectedSheet.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible = true" @click="thumbDrawerVisible = true"
> />
<a-form layout="vertial"> <a-form layout="vertial">
<a-form-item> <a-form-item>
<a-input <a-input v-model="selectedSheet.thumbnail" placeholder="点击封面图选择图片,或者输入外部链接"></a-input>
v-model="selectedSheet.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-button <a-button class="sheet-thumb-remove" type="dashed" @click="selectedSheet.thumbnail = null">移除</a-button>
class="sheet-thumb-remove"
type="dashed"
@click="selectedSheet.thumbnail = null"
>移除</a-button>
</div> </div>
</div> </div>
</div> </div>
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
</div> </div>
<AttachmentSelectDrawer <AttachmentSelectDrawer v-model="thumbDrawerVisible" @listenToSelect="handleSelectSheetThumb" :drawerWidth="480" />
v-model="thumbDrawerVisible"
@listenToSelect="handleSelectSheetThumb"
:drawerWidth="480"
/>
<a-drawer <a-drawer
title="高级设置" title="高级设置"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
placement="right" placement="right"
closable closable
destroyOnClose destroyOnClose
@ -136,10 +103,7 @@
<div class="post-setting-drawer-item"> <div class="post-setting-drawer-item">
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item label="自定义关键词:"> <a-form-item label="自定义关键词:">
<a-input <a-input v-model="selectedSheet.metaKeywords" placeholder="多个关键词以英文逗号隔开" />
v-model="selectedSheet.metaKeywords"
placeholder="多个关键词以英文逗号隔开"
/>
</a-form-item> </a-form-item>
<a-form-item label="自定义描述:"> <a-form-item label="自定义描述:">
<a-input <a-input
@ -156,11 +120,7 @@
<div class="mb-4"> <div class="mb-4">
<h3 class="post-setting-drawer-title">元数据</h3> <h3 class="post-setting-drawer-title">元数据</h3>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item v-for="(meta, index) in selectedMetas" :key="index" :prop="'meta.' + index + '.value'">
v-for="(meta, index) in selectedMetas"
:key="index"
:prop="'meta.' + index + '.value'"
>
<a-row :gutter="5"> <a-row :gutter="5">
<a-col :span="12"> <a-col :span="12">
<a-input v-model="meta.key"><i slot="addonBefore">K</i></a-input> <a-input v-model="meta.key"><i slot="addonBefore">K</i></a-input>
@ -168,11 +128,7 @@
<a-col :span="12"> <a-col :span="12">
<a-input v-model="meta.value"> <a-input v-model="meta.value">
<i slot="addonBefore">V</i> <i slot="addonBefore">V</i>
<a <a href="javascript:void(0);" slot="addonAfter" @click.prevent="handleRemoveSheetMeta(meta)">
href="javascript:void(0);"
slot="addonAfter"
@click.prevent="handleRemoveSheetMeta(meta)"
>
<a-icon type="close" /> <a-icon type="close" />
</a> </a>
</a-input> </a-input>
@ -180,10 +136,7 @@
</a-row> </a-row>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button <a-button type="dashed" @click="handleInsertSheetMeta()"></a-button>
type="dashed"
@click="handleInsertSheetMeta()"
>新增</a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
@ -191,20 +144,14 @@
</div> </div>
<div class="bottom-control"> <div class="bottom-control">
<a-space> <a-space>
<a-button <a-button type="primary" @click="advancedVisible = false">返回</a-button>
type="primary"
@click="advancedVisible = false"
>返回</a-button>
</a-space> </a-space>
</div> </div>
</a-drawer> </a-drawer>
<div class="bottom-control"> <div class="bottom-control">
<a-space> <a-space>
<a-button <a-button type="dashed" @click="advancedVisible = true">高级</a-button>
type="dashed"
@click="advancedVisible = true"
>高级</a-button>
<ReactiveButton <ReactiveButton
type="danger" type="danger"
v-if="saveDraftButton" v-if="saveDraftButton"
@ -221,9 +168,9 @@
@callback="handleSavedCallback" @callback="handleSavedCallback"
:loading="saving" :loading="saving"
:errored="savedErrored" :errored="savedErrored"
:text="`${selectedSheet.id?'保存':'发布'}`" :text="`${selectedSheet.id ? '保存' : '发布'}`"
:loadedText="`${selectedSheet.id?'保存':'发布'}成功`" :loadedText="`${selectedSheet.id ? '保存' : '发布'}成功`"
:erroredText="`${selectedSheet.id?'保存':'发布'}失败`" :erroredText="`${selectedSheet.id ? '保存' : '发布'}失败`"
></ReactiveButton> ></ReactiveButton>
</a-space> </a-space>
</div> </div>
@ -252,33 +199,33 @@ export default {
saving: false, saving: false,
savedErrored: false, savedErrored: false,
draftSaving: false, draftSaving: false,
draftSavedErrored: false, draftSavedErrored: false
} }
}, },
props: { props: {
sheet: { sheet: {
type: Object, type: Object,
required: true, required: true
}, },
metas: { metas: {
type: Array, type: Array,
required: true, required: true
}, },
needTitle: { needTitle: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false
}, },
saveDraftButton: { saveDraftButton: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, },
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
watch: { watch: {
sheet(val) { sheet(val) {
@ -289,7 +236,7 @@ export default {
}, },
selectedMetas(val) { selectedMetas(val) {
this.$emit('onRefreshSheetMetas', val) this.$emit('onRefreshSheetMetas', val)
}, }
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options']),
@ -318,7 +265,7 @@ export default {
default: default:
return '' return ''
} }
}, }
}, },
methods: { methods: {
handleAfterVisibleChanged(visible) { handleAfterVisibleChanged(visible) {
@ -330,13 +277,13 @@ export default {
}, },
handleListPresetMetasField() { handleListPresetMetasField() {
if (this.metas.length <= 0) { if (this.metas.length <= 0) {
themeApi.getActivatedTheme().then((response) => { themeApi.getActivatedTheme().then(response => {
const fields = response.data.data.sheetMetaField const fields = response.data.data.sheetMetaField
if (fields && fields.length > 0) { if (fields && fields.length > 0) {
for (let i = 0, len = fields.length; i < len; i++) { for (let i = 0, len = fields.length; i < len; i++) {
this.selectedMetas.push({ this.selectedMetas.push({
value: '', value: '',
key: fields[i], key: fields[i]
}) })
} }
} }
@ -347,7 +294,7 @@ export default {
this.customTplsLoading = true this.customTplsLoading = true
themeApi themeApi
.customSheetTpls() .customSheetTpls()
.then((response) => { .then(response => {
this.customTpls = response.data.data this.customTpls = response.data.data
}) })
.finally(() => { .finally(() => {
@ -372,7 +319,7 @@ export default {
if (!this.selectedSheet.title) { if (!this.selectedSheet.title) {
this.$notification['error']({ this.$notification['error']({
message: '提示', message: '提示',
description: '页面标题不能为空!', description: '页面标题不能为空!'
}) })
return return
} }
@ -408,7 +355,7 @@ export default {
this.savedErrored = true this.savedErrored = true
} }
}) })
.then((response) => { .then(response => {
this.selectedSheet = response.data.data this.selectedSheet = response.data.data
}) })
.finally(() => { .finally(() => {
@ -431,7 +378,7 @@ export default {
onClose() { onClose() {
this.$emit('close', false) this.$emit('close', false)
}, },
onSheetDateChange(value, dateString) { onSheetDateChange(value) {
this.selectedSheet.createTime = value.valueOf() this.selectedSheet.createTime = value.valueOf()
}, },
onSheetDateOk(value) { onSheetDateOk(value) {
@ -446,7 +393,7 @@ export default {
handleInsertSheetMeta() { handleInsertSheetMeta() {
this.selectedMetas.push({ this.selectedMetas.push({
value: '', value: '',
key: '', key: ''
}) })
}, },
handleSetPinyinSlug() { handleSetPinyinSlug() {
@ -455,7 +402,7 @@ export default {
let result = '' let result = ''
const tokens = pinyin.parse(this.selectedSheet.title) const tokens = pinyin.parse(this.selectedSheet.title)
let lastToken let lastToken
tokens.forEach((token, i) => { tokens.forEach(token => {
if (token.type === 2) { if (token.type === 2) {
const target = token.target ? token.target.toLowerCase() : '' const target = token.target ? token.target.toLowerCase() : ''
result += result && !/\n|\s/.test(lastToken.target) ? '-' + target : target result += result && !/\n|\s/.test(lastToken.target) ? '-' + target : target
@ -467,7 +414,7 @@ export default {
this.$set(this.selectedSheet, 'slug', result) this.$set(this.selectedSheet, 'slug', result)
} }
} }
}, }
}, }
} }
</script> </script>

View File

@ -2,52 +2,28 @@
<page-view> <page-view>
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<a-card <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input v-model="list.queryParam.keyword" @keyup.enter="handleQuery()" />
v-model="list.queryParam.keyword"
@keyup.enter="handleQuery()"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="状态:"> <a-form-item label="状态:">
<a-select <a-select placeholder="请选择状态" v-model="list.queryParam.type" @change="handleQuery()">
placeholder="请选择状态" <a-select-option v-for="type in Object.keys(list.journalType)" :key="type" :value="type">{{
v-model="list.queryParam.type" list.journalType[type].text
@change="handleQuery()" }}</a-select-option>
>
<a-select-option
v-for="type in Object.keys(list.journalType)"
:key="type"
:value="type"
>{{ list.journalType[type].text }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -56,27 +32,13 @@
</a-form> </a-form>
</div> </div>
<div class="table-operator"> <div class="table-operator">
<a-button <a-button type="primary" icon="plus" @click="handleOpenPublishModal"></a-button>
type="primary"
icon="plus"
@click="handleOpenPublishModal"
>写日志</a-button>
</div> </div>
<a-divider /> <a-divider />
<div class="mt-4"> <div class="mt-4">
<a-empty v-if="!list.loading && list.data.length==0" /> <a-empty v-if="!list.loading && list.data.length == 0" />
<a-list <a-list v-else itemLayout="vertical" :pagination="false" :dataSource="list.data" :loading="list.loading">
v-else <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
itemLayout="vertical"
:pagination="false"
:dataSource="list.data"
:loading="list.loading"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<span> <span>
<a href="javascript:void(0);"> <a href="javascript:void(0);">
@ -85,19 +47,13 @@
</a> </a>
</span> </span>
<span> <span>
<a <a href="javascript:void(0);" @click="handleOpenJournalCommentsDrawer(item)">
href="javascript:void(0);"
@click="handleOpenJournalCommentsDrawer(item)"
>
<a-icon type="message" /> <a-icon type="message" />
{{ item.commentCount }} {{ item.commentCount }}
</a> </a>
</span> </span>
<span v-if="item.type=='INTIMATE'"> <span v-if="item.type == 'INTIMATE'">
<a <a href="javascript:void(0);" disabled>
href="javascript:void(0);"
disabled
>
<a-icon type="lock" /> <a-icon type="lock" />
</a> </a>
</span> </span>
@ -108,10 +64,7 @@
</span> </span>
</template> </template>
<template slot="extra"> <template slot="extra">
<a <a href="javascript:void(0);" @click="handleOpenEditModal(item)"></a>
href="javascript:void(0);"
@click="handleOpenEditModal(item)"
>编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
title="你确定要删除这条日志?" title="你确定要删除这条日志?"
@ -125,17 +78,10 @@
<a-list-item-meta> <a-list-item-meta>
<template slot="description"> <template slot="description">
<p <p v-html="item.content" class="journal-list-content"></p>
v-html="item.content"
class="journal-list-content"
></p>
</template> </template>
<span slot="title">{{ item.createTime | moment }}</span> <span slot="title">{{ item.createTime | moment }}</span>
<a-avatar <a-avatar slot="avatar" size="large" :src="user.avatar" />
slot="avatar"
size="large"
:src="user.avatar"
/>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
<div class="page-wrapper"> <div class="page-wrapper">
@ -163,33 +109,19 @@
shape="circle" shape="circle"
icon="setting" icon="setting"
size="large" size="large"
@click="optionModal.visible=true" @click="optionModal.visible = true"
></a-button> ></a-button>
</div> </div>
<a-modal <a-modal v-model="optionModal.visible" title="页面设置" :afterClose="() => (optionModal.visible = false)">
v-model="optionModal.visible"
title="页面设置"
:afterClose="() => optionModal.visible = false"
>
<template slot="footer"> <template slot="footer">
<a-button <a-button key="submit" type="primary" @click="handleSaveOptions()"></a-button>
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template> </template>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item label="页面标题:" help="* 需要主题进行适配">
label="页面标题:"
help="* 需要主题进行适配"
>
<a-input v-model="optionModal.options.journals_title" /> <a-input v-model="optionModal.options.journals_title" />
</a-form-item> </a-form-item>
<a-form-item label="每页显示条数:"> <a-form-item label="每页显示条数:">
<a-input-number <a-input-number v-model="optionModal.options.journals_page_size" style="width:100%" />
v-model="optionModal.options.journals_page_size"
style="width:100%"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
@ -198,18 +130,12 @@
<a-modal v-model="form.visible"> <a-modal v-model="form.visible">
<template slot="title"> <template slot="title">
{{ formTitle }} {{ formTitle }}
<a-tooltip <a-tooltip slot="action" title="只能输入250字">
slot="action"
title="只能输入250字"
>
<a-icon type="info-circle-o" /> <a-icon type="info-circle-o" />
</a-tooltip> </a-tooltip>
</template> </template>
<template slot="footer"> <template slot="footer">
<a-button <a-button type="dashed" @click="attachmentDrawer.visible = true">附件库</a-button>
type="dashed"
@click="attachmentDrawer.visible = true"
>附件库</a-button>
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@click="handleSaveOrUpdate" @click="handleSaveOrUpdate"
@ -221,12 +147,7 @@
erroredText="发布失败" erroredText="发布失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form-model <a-form-model ref="journalForm" :model="form.model" :rules="form.rules" layout="vertical">
ref="journalForm"
:model="form.model"
:rules="form.rules"
layout="vertical"
>
<a-form-model-item prop="sourceContent"> <a-form-model-item prop="sourceContent">
<a-input <a-input
ref="sourceContentInput" ref="sourceContentInput"
@ -236,12 +157,7 @@
/> />
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<a-switch <a-switch checkedChildren="公开" unCheckedChildren="私密" v-model="form.isPublic" defaultChecked />
checkedChildren="公开"
unCheckedChildren="私密"
v-model="form.isPublic"
defaultChecked
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
@ -278,40 +194,40 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null,
type: null, type: null
}, },
selected: {}, selected: {},
journalType: journalApi.journalType, journalType: journalApi.journalType
}, },
form: { form: {
model: {}, model: {},
rules: { rules: {
sourceContent: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }], sourceContent: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }]
}, },
visible: false, visible: false,
saving: false, saving: false,
saveErrored: false, saveErrored: false,
isPublic: true, isPublic: true
}, },
journalCommentDrawer: { journalCommentDrawer: {
visible: false, visible: false
}, },
attachmentDrawer: { attachmentDrawer: {
visible: false, visible: false
}, },
optionModal: { optionModal: {
visible: false, visible: false,
options: [], options: []
}, }
} }
}, },
beforeMount() { beforeMount() {
@ -322,7 +238,7 @@ export default {
...mapGetters(['user']), ...mapGetters(['user']),
formTitle() { formTitle() {
return this.form.model.id ? '编辑' : '发表' return this.form.model.id ? '编辑' : '发表'
}, }
}, },
methods: { methods: {
...mapActions(['refreshOptionsCache']), ...mapActions(['refreshOptionsCache']),
@ -333,7 +249,7 @@ export default {
this.list.queryParam.sort = this.list.pagination.sort this.list.queryParam.sort = this.list.pagination.sort
journalApi journalApi
.query(this.list.queryParam) .query(this.list.queryParam)
.then((response) => { .then(response => {
this.list.data = response.data.data.content this.list.data = response.data.data.content
this.list.pagination.total = response.data.data.total this.list.pagination.total = response.data.data.total
}) })
@ -344,7 +260,7 @@ export default {
}) })
}, },
hanldeListOptions() { hanldeListOptions() {
optionApi.listAll().then((response) => { optionApi.listAll().then(response => {
this.optionModal.options = response.data.data this.optionModal.options = response.data.data
}) })
}, },
@ -382,7 +298,7 @@ export default {
}, },
handleSaveOrUpdate() { handleSaveOrUpdate() {
const _this = this const _this = this
_this.$refs.journalForm.validate((valid) => { _this.$refs.journalForm.validate(valid => {
if (valid) { if (valid) {
_this.form.model.type = _this.form.isPublic ? 'PUBLIC' : 'INTIMATE' _this.form.model.type = _this.form.isPublic ? 'PUBLIC' : 'INTIMATE'
_this.form.saving = true _this.form.saving = true
@ -434,7 +350,7 @@ export default {
handleSaveOptions() { handleSaveOptions() {
optionApi optionApi
.save(this.optionModal.options) .save(this.optionModal.options)
.then((response) => { .then(() => {
this.$message.success('保存成功!') this.$message.success('保存成功!')
this.optionModal.visible = false this.optionModal.visible = false
}) })
@ -442,7 +358,7 @@ export default {
this.hanldeListOptions() this.hanldeListOptions()
this.refreshOptionsCache() this.refreshOptionsCache()
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,35 +1,13 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24" class="pb-3">
:xl="10" <a-card :title="title" :bodyStyle="{ padding: '16px' }">
:lg="10" <a-form-model ref="linkForm" :model="form.model" :rules="form.rules" layout="horizontal">
:md="10" <a-form-model-item label="网站名称:" prop="name">
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
:title="title"
:bodyStyle="{ padding: '16px' }"
>
<a-form-model
ref="linkForm"
:model="form.model"
:rules="form.rules"
layout="horizontal"
>
<a-form-model-item
label="网站名称:"
prop="name"
>
<a-input v-model="form.model.name" /> <a-input v-model="form.model.name" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="网站地址:" help="* 需要加上 http://" prop="url">
label="网站地址:"
help="* 需要加上 http://"
prop="url"
>
<a-input v-model="form.model.url"> <a-input v-model="form.model.url">
<!-- <a <!-- <a
href="javascript:void(0);" href="javascript:void(0);"
@ -40,41 +18,17 @@
</a> --> </a> -->
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="Logo" prop="logo">
label="Logo"
prop="logo"
>
<a-input v-model="form.model.logo" /> <a-input v-model="form.model.logo" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="分组:" prop="team">
label="分组:" <a-auto-complete :dataSource="computedTeams" v-model="form.model.team" allowClear />
prop="team"
>
<a-auto-complete
:dataSource="computedTeams"
v-model="form.model.team"
allowClear
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="排序编号:" prop="priority">
label="排序编号:" <a-input-number :min="0" v-model="form.model.priority" style="width:100%" />
prop="priority"
>
<a-input-number
:min="0"
v-model="form.model.priority"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="描述:" prop="description">
label="描述:" <a-input type="textarea" :autoSize="{ minRows: 5 }" v-model="form.model.description" />
prop="description"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="form.model.description"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton
@ -99,28 +53,14 @@
loadedText="更新成功" loadedText="更新成功"
erroredText="更新失败" erroredText="更新失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button type="dashed" @click="form.model = {}" v-if="isUpdateMode"></a-button>
type="dashed"
@click="form.model = {}"
v-if="isUpdateMode"
>返回添加</a-button>
</a-button-group> </a-button-group>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24" class="pb-3">
:xl="14" <a-card title="所有友情链接" :bodyStyle="{ padding: '16px' }">
:lg="14"
:md="14"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
title="所有友情链接"
:bodyStyle="{ padding: '16px' }"
>
<!-- Mobile --> <!-- Mobile -->
<a-list <a-list
v-if="isMobile()" v-if="isMobile()"
@ -129,25 +69,15 @@
:dataSource="table.data" :dataSource="table.data"
:loading="table.loading" :loading="table.loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions"> <template slot="actions">
<a-dropdown <a-dropdown placement="topLeft" :trigger="['click']">
placement="topLeft"
:trigger="['click']"
>
<span> <span>
<a-icon type="bars" /> <a-icon type="bars" />
</span> </span>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item> <a-menu-item>
<a <a href="javascript:void(0);" @click="form.model = item">编辑</a>
href="javascript:void(0);"
@click="form.model = item"
>编辑</a>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a-popconfirm <a-popconfirm
@ -178,10 +108,7 @@
{{ item.name }} {{ item.name }}
</span> </span>
</a-list-item-meta> </a-list-item-meta>
<a <a :href="item.url" target="_blank">{{ item.url }}</a>
:href="item.url"
target="_blank"
>{{ item.url }}</a>
</a-list-item> </a-list-item>
</a-list> </a-list>
<!-- Desktop --> <!-- Desktop -->
@ -193,29 +120,12 @@
:rowKey="link => link.id" :rowKey="link => link.id"
:scrollToFirstRowOnChange="true" :scrollToFirstRowOnChange="true"
> >
<template <template slot="url" slot-scope="text">
slot="url" <a target="_blank" :href="text">{{ text }}</a>
slot-scope="text"
>
<a
target="_blank"
:href="text"
>{{ text }}</a>
</template> </template>
<ellipsis <ellipsis :length="15" tooltip slot="name" slot-scope="text">{{ text }}</ellipsis>
:length="15" <span slot="action" slot-scope="text, record">
tooltip <a href="javascript:void(0);" @click="form.model = record">编辑</a>
slot="name"
slot-scope="text"
>{{ text }}</ellipsis>
<span
slot="action"
slot-scope="text, record"
>
<a
href="javascript:void(0);"
@click="form.model = record"
>编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
:title="'你确定要删除【' + record.name + '】链接?'" :title="'你确定要删除【' + record.name + '】链接?'"
@ -236,26 +146,15 @@
shape="circle" shape="circle"
icon="setting" icon="setting"
size="large" size="large"
@click="optionsModal.visible=true" @click="optionsModal.visible = true"
></a-button> ></a-button>
</div> </div>
<a-modal <a-modal v-model="optionsModal.visible" title="页面设置" :afterClose="() => (optionsModal.visible = false)">
v-model="optionsModal.visible"
title="页面设置"
:afterClose="() => optionsModal.visible = false"
>
<template slot="footer"> <template slot="footer">
<a-button <a-button key="submit" type="primary" @click="handleSaveOptions()"></a-button>
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template> </template>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item label="页面标题:" help="* 需要主题进行适配">
label="页面标题:"
help="* 需要主题进行适配"
>
<a-input v-model="optionsModal.data.links_title" /> <a-input v-model="optionsModal.data.links_title" />
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -274,40 +173,40 @@ const columns = [
title: '名称', title: '名称',
dataIndex: 'name', dataIndex: 'name',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'name' }, scopedSlots: { customRender: 'name' }
}, },
{ {
title: '网址', title: '网址',
dataIndex: 'url', dataIndex: 'url',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'url' }, scopedSlots: { customRender: 'url' }
}, },
{ {
title: '分组', title: '分组',
ellipsis: true, ellipsis: true,
dataIndex: 'team', dataIndex: 'team'
}, },
{ {
title: '排序', title: '排序',
dataIndex: 'priority', dataIndex: 'priority'
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
PageView, PageView
}, },
data() { data() {
return { return {
table: { table: {
columns, columns,
data: [], data: [],
loading: false, loading: false
}, },
form: { form: {
model: {}, model: {},
@ -316,23 +215,23 @@ export default {
rules: { rules: {
name: [ name: [
{ required: true, message: '* 友情链接名称不能为空', trigger: ['change'] }, { required: true, message: '* 友情链接名称不能为空', trigger: ['change'] },
{ max: 255, message: '* 友情链接名称的字符长度不能超过 255', trigger: ['change'] }, { max: 255, message: '* 友情链接名称的字符长度不能超过 255', trigger: ['change'] }
], ],
url: [ url: [
{ required: true, message: '* 友情链接地址不能为空', trigger: ['change'] }, { required: true, message: '* 友情链接地址不能为空', trigger: ['change'] },
{ max: 1023, message: '* 友情链接地址的字符长度不能超过 1023', trigger: ['change'] }, { max: 1023, message: '* 友情链接地址的字符长度不能超过 1023', trigger: ['change'] },
{ type: 'url', message: '* 友情链接地址格式有误', trigger: ['change'] }, { type: 'url', message: '* 友情链接地址格式有误', trigger: ['change'] }
], ],
logo: [{ max: 1023, message: '* 友情链接 Logo 的字符长度不能超过 1023', trigger: ['change'] }], logo: [{ max: 1023, message: '* 友情链接 Logo 的字符长度不能超过 1023', trigger: ['change'] }],
description: [{ max: 255, message: '* 友情链接描述的字符长度不能超过 255', trigger: ['change'] }], description: [{ max: 255, message: '* 友情链接描述的字符长度不能超过 255', trigger: ['change'] }],
team: [{ max: 255, message: '* 友情链接分组的字符长度 255', trigger: ['change'] }], team: [{ max: 255, message: '* 友情链接分组的字符长度 255', trigger: ['change'] }]
}, }
}, },
optionsModal: { optionsModal: {
visible: false, visible: false,
data: [], data: []
}, },
teams: [], teams: []
} }
}, },
computed: { computed: {
@ -346,10 +245,10 @@ export default {
return !!this.form.model.id return !!this.form.model.id
}, },
computedTeams() { computedTeams() {
return this.teams.filter((item) => { return this.teams.filter(item => {
return item !== '' return item !== ''
}) })
}, }
}, },
created() { created() {
this.handleListLinks() this.handleListLinks()
@ -362,7 +261,7 @@ export default {
this.table.loading = true this.table.loading = true
linkApi linkApi
.listAll() .listAll()
.then((response) => { .then(response => {
this.table.data = response.data.data this.table.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -372,19 +271,19 @@ export default {
}) })
}, },
handleListLinkTeams() { handleListLinkTeams() {
linkApi.listTeams().then((response) => { linkApi.listTeams().then(response => {
this.teams = response.data.data this.teams = response.data.data
}) })
}, },
handleListOptions() { handleListOptions() {
optionApi.listAll().then((response) => { optionApi.listAll().then(response => {
this.optionsModal.data = response.data.data this.optionsModal.data = response.data.data
}) })
}, },
handleDeleteLink(id) { handleDeleteLink(id) {
linkApi linkApi
.delete(id) .delete(id)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -393,13 +292,13 @@ export default {
}) })
}, },
handleParseUrl() { handleParseUrl() {
linkApi.getByParse(this.form.model.url).then((response) => { linkApi.getByParse(this.form.model.url).then(response => {
this.form.model = response.data.data this.form.model = response.data.data
}) })
}, },
handleCreateOrUpdateLink() { handleCreateOrUpdateLink() {
const _this = this const _this = this
_this.$refs.linkForm.validate((valid) => { _this.$refs.linkForm.validate(valid => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.isUpdateMode) { if (_this.isUpdateMode) {
@ -429,19 +328,18 @@ export default {
}) })
}, },
handleSavedCallback() { handleSavedCallback() {
const _this = this if (this.form.errored) {
if (_this.form.errored) { this.form.errored = false
_this.form.errored = false
} else { } else {
_this.form.model = {} this.form.model = {}
_this.handleListLinks() this.handleListLinks()
_this.handleListLinkTeams() this.handleListLinkTeams()
} }
}, },
handleSaveOptions() { handleSaveOptions() {
optionApi optionApi
.save(this.optionsModal.data) .save(this.optionsModal.data)
.then((response) => { .then(() => {
this.$message.success('保存成功!') this.$message.success('保存成功!')
this.optionsModal.visible = false this.optionsModal.visible = false
}) })
@ -449,7 +347,7 @@ export default {
this.handleListOptions() this.handleListOptions()
this.refreshOptionsCache() this.refreshOptionsCache()
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,56 +1,29 @@
<template> <template>
<page-view> <page-view>
<a-row <a-row :gutter="12" type="flex" align="middle">
:gutter="12" <a-col :span="24" class="pb-3">
type="flex" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
align="middle"
>
<a-col
:span="24"
class="pb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input v-model="list.queryParam.keyword" /> <a-input v-model="list.queryParam.keyword" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="分组:"> <a-form-item label="分组:">
<a-select <a-select v-model="list.queryParam.team" @change="handleQuery()">
v-model="list.queryParam.team" <a-select-option v-for="(item, index) in computedTeams" :key="index" :value="item">{{
@change="handleQuery()" item
> }}</a-select-option>
<a-select-option
v-for="(item,index) in computedTeams"
:key="index"
:value="item"
>{{ item }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -58,12 +31,8 @@
</a-row> </a-row>
</a-form> </a-form>
</div> </div>
<div class="table-operator mb-0"> <div class="mb-0 table-operator">
<a-button <a-button type="primary" icon="plus" @click="form.visible = true">添加</a-button>
type="primary"
icon="plus"
@click="form.visible = true"
>添加</a-button>
</div> </div>
</a-card> </a-card>
</a-col> </a-col>
@ -73,28 +42,13 @@
:dataSource="list.data" :dataSource="list.data"
:loading="list.loading" :loading="list.loading"
> >
<a-list-item <a-list-item slot="renderItem" slot-scope="item, index" :key="index">
slot="renderItem" <a-card :bodyStyle="{ padding: 0 }" hoverable @click="handleOpenEditForm(item)">
slot-scope="item, index"
:key="index"
>
<a-card
:bodyStyle="{ padding: 0 }"
hoverable
@click="handleOpenEditForm(item)"
>
<div class="photo-thumb"> <div class="photo-thumb">
<img <img :src="item.thumbnail" loading="lazy" />
:src="item.thumbnail"
loading="lazy"
>
</div> </div>
<a-card-meta class="p-3"> <a-card-meta class="p-3">
<ellipsis <ellipsis :length="isMobile() ? 12 : 16" tooltip slot="description">{{ item.name }}</ellipsis>
:length="isMobile()?12:16"
tooltip
slot="description"
>{{ item.name }}</ellipsis>
</a-card-meta> </a-card-meta>
</a-card> </a-card>
</a-list-item> </a-list-item>
@ -106,7 +60,7 @@
:current="list.pagination.page" :current="list.pagination.page"
:total="list.pagination.total" :total="list.pagination.total"
:defaultPageSize="list.pagination.size" :defaultPageSize="list.pagination.size"
:pageSizeOptions="['18', '36', '54','72','90','108']" :pageSizeOptions="['18', '36', '54', '72', '90', '108']"
showSizeChanger showSizeChanger
@change="handlePaginationChange" @change="handlePaginationChange"
@showSizeChange="handlePaginationChange" @showSizeChange="handlePaginationChange"
@ -114,88 +68,48 @@
/> />
</div> </div>
<div style="position: fixed;bottom: 30px;right: 30px;"> <div style="position: fixed;bottom: 30px;right: 30px;">
<a-button <a-button type="primary" shape="circle" icon="setting" size="large" @click="optionFormVisible = true"></a-button>
type="primary"
shape="circle"
icon="setting"
size="large"
@click="optionFormVisible=true"
></a-button>
</div> </div>
<a-modal <a-modal v-model="optionFormVisible" title="页面设置" :afterClose="() => (optionFormVisible = false)">
v-model="optionFormVisible"
title="页面设置"
:afterClose="() => optionFormVisible = false"
>
<template slot="footer"> <template slot="footer">
<a-button <a-button key="submit" type="primary" @click="handleSaveOptions()"></a-button>
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template> </template>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item <a-form-item label="页面标题:" help="* 需要主题进行适配">
label="页面标题:"
help="* 需要主题进行适配"
>
<a-input v-model="options.photos_title" /> <a-input v-model="options.photos_title" />
</a-form-item> </a-form-item>
<a-form-item label="每页显示条数:"> <a-form-item label="每页显示条数:">
<a-input-number <a-input-number v-model="options.photos_page_size" style="width:100%" />
v-model="options.photos_page_size"
style="width:100%"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
<a-drawer <a-drawer
:title="`图片${form.model.id?'修改':'添加'}`" :title="`图片${form.model.id ? '修改' : '添加'}`"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="form.visible" :visible="form.visible"
destroyOnClose destroyOnClose
@close="onDrawerClose" @close="onDrawerClose"
> >
<a-form-model <a-form-model ref="photoForm" :model="form.model" :rules="form.rules" layout="vertical">
ref="photoForm" <a-form-model-item prop="url" label="图片地址:">
:model="form.model"
:rules="form.rules"
layout="vertical"
>
<a-form-model-item
prop="url"
label="图片地址:"
>
<div class="pb-2"> <div class="pb-2">
<img <img
:src="form.model.url || '/images/placeholder.jpg'" :src="form.model.url || '/images/placeholder.jpg'"
@click="attachmentSelectDrawer.visible = true" @click="attachmentSelectDrawer.visible = true"
class="w-full cursor-pointer" class="w-full cursor-pointer"
style="border-radius:4px" style="border-radius:4px"
> />
</div> </div>
<a-input <a-input v-model="form.model.url" placeholder="点击封面图选择图片,或者输入外部链接" />
v-model="form.model.url"
placeholder="点击封面图选择图片,或者输入外部链接"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="thumbnail" label="缩略图地址:">
prop="thumbnail"
label="缩略图地址:"
>
<a-input v-model="form.model.thumbnail" /> <a-input v-model="form.model.thumbnail" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="name" label="图片名称:">
prop="name"
label="图片名称:"
>
<a-input v-model="form.model.name" /> <a-input v-model="form.model.name" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="takeTime" label="拍摄日期:">
prop="takeTime"
label="拍摄日期:"
>
<a-date-picker <a-date-picker
showTime showTime
:defaultValue="takeTimeDefaultValue" :defaultValue="takeTimeDefaultValue"
@ -205,32 +119,14 @@
@ok="onTakeTimeSelect" @ok="onTakeTimeSelect"
/> />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="location" label="拍摄地点:">
prop="location"
label="拍摄地点:"
>
<a-input v-model="form.model.location" /> <a-input v-model="form.model.location" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="team" label="分组:">
prop="team" <a-auto-complete :dataSource="computedTeams" v-model="form.model.team" allowClear style="width:100%" />
label="分组:"
>
<a-auto-complete
:dataSource="computedTeams"
v-model="form.model.team"
allowClear
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="description" label="描述:">
prop="description" <a-input v-model="form.model.description" type="textarea" :autoSize="{ minRows: 5 }" />
label="描述:"
>
<a-input
v-model="form.model.description"
type="textarea"
:autoSize="{ minRows: 5 }"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
@ -241,9 +137,9 @@
@callback="handleCreateOrUpdateCallback" @callback="handleCreateOrUpdateCallback"
:loading="form.saving" :loading="form.saving"
:errored="form.saveErrored" :errored="form.saveErrored"
:text="`${form.model.id?'修改':'添加'}`" :text="`${form.model.id ? '修改' : '添加'}`"
:loadedText="`${form.model.id?'修改':'添加'}成功`" :loadedText="`${form.model.id ? '修改' : '添加'}成功`"
:erroredText="`${form.model.id?'修改':'添加'}失败`" :erroredText="`${form.model.id ? '修改' : '添加'}失败`"
></ReactiveButton> ></ReactiveButton>
<a-popconfirm <a-popconfirm
title="你确定要删除该图片?" title="你确定要删除该图片?"
@ -295,15 +191,15 @@ export default {
page: 1, page: 1,
size: 18, size: 18,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 18, size: 18,
sort: null, sort: null,
keyword: null, keyword: null,
team: null, team: null
}, }
}, },
form: { form: {
@ -312,21 +208,21 @@ export default {
rules: { rules: {
url: [{ required: true, message: '* 图片地址不能为空', trigger: ['change'] }], url: [{ required: true, message: '* 图片地址不能为空', trigger: ['change'] }],
thumbnail: [{ required: true, message: '* 缩略图地址不能为空', trigger: ['change'] }], thumbnail: [{ required: true, message: '* 缩略图地址不能为空', trigger: ['change'] }],
name: [{ required: true, message: '* 图片名称不能为空', trigger: ['change'] }], name: [{ required: true, message: '* 图片名称不能为空', trigger: ['change'] }]
}, },
saving: false, saving: false,
saveErrored: false, saveErrored: false,
deleting: false, deleting: false,
deleteErrored: false, deleteErrored: false
}, },
attachmentSelectDrawer: { attachmentSelectDrawer: {
visible: false, visible: false
}, },
optionFormVisible: false, optionFormVisible: false,
teams: [], teams: [],
options: [], options: []
} }
}, },
created() { created() {
@ -343,10 +239,10 @@ export default {
return datetimeFormat(new Date(), 'YYYY-MM-DD HH:mm:ss') return datetimeFormat(new Date(), 'YYYY-MM-DD HH:mm:ss')
}, },
computedTeams() { computedTeams() {
return this.teams.filter((item) => { return this.teams.filter(item => {
return item !== '' return item !== ''
}) })
}, }
}, },
methods: { methods: {
...mapActions(['refreshOptionsCache']), ...mapActions(['refreshOptionsCache']),
@ -357,7 +253,7 @@ export default {
this.list.queryParam.sort = this.list.pagination.sort this.list.queryParam.sort = this.list.pagination.sort
photoApi photoApi
.query(this.list.queryParam) .query(this.list.queryParam)
.then((response) => { .then(response => {
this.list.data = response.data.data.content this.list.data = response.data.data.content
this.list.pagination.total = response.data.data.total this.list.pagination.total = response.data.data.total
}) })
@ -371,18 +267,18 @@ export default {
this.handlePaginationChange(1, this.list.pagination.size) this.handlePaginationChange(1, this.list.pagination.size)
}, },
hanldeListOptions() { hanldeListOptions() {
optionApi.listAll().then((response) => { optionApi.listAll().then(response => {
this.options = response.data.data this.options = response.data.data
}) })
}, },
hanldeListPhotoTeams() { hanldeListPhotoTeams() {
photoApi.listTeams().then((response) => { photoApi.listTeams().then(response => {
this.teams = response.data.data this.teams = response.data.data
}) })
}, },
handleCreateOrUpdate() { handleCreateOrUpdate() {
const _this = this const _this = this
_this.$refs.photoForm.validate((valid) => { _this.$refs.photoForm.validate(valid => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.form.model.id) { if (_this.form.model.id) {
@ -472,7 +368,7 @@ export default {
handleSaveOptions() { handleSaveOptions() {
optionApi optionApi
.save(this.options) .save(this.options)
.then((response) => { .then(() => {
this.$message.success('保存成功!') this.$message.success('保存成功!')
this.optionFormVisible = false this.optionFormVisible = false
}) })
@ -481,12 +377,12 @@ export default {
this.refreshOptionsCache() this.refreshOptionsCache()
}) })
}, },
onTakeTimeChange(value, dateString) { onTakeTimeChange(value) {
this.form.model.takeTime = value.valueOf() this.form.model.takeTime = value.valueOf()
}, },
onTakeTimeSelect(value) { onTakeTimeSelect(value) {
this.form.model.takeTime = value.valueOf() this.form.model.takeTime = value.valueOf()
}, }
}, }
} }
</script> </script>

View File

@ -2,116 +2,69 @@
<page-view> <page-view>
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<a-card <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:bordered="false" <a-card :bordered="false" class="environment-info" :bodyStyle="{ padding: '16px' }">
:bodyStyle="{ padding: '16px' }"
>
<a-card
:bordered="false"
class="environment-info"
:bodyStyle="{ padding: '16px' }"
>
<template slot="title"> <template slot="title">
环境信息 环境信息
<a <a href="javascript:void(0);" @click="handleCopyEnvironments">
href="javascript:void(0);"
@click="handleCopyEnvironments"
>
<a-icon type="copy" /> <a-icon type="copy" />
</a> </a>
</template> </template>
<a-popover <a-popover slot="extra" placement="left" :title="isLatest ? '当前为最新版本' : '有新版本'">
slot="extra"
placement="left"
:title="isLatest?'当前为最新版本':'有新版本'"
>
<template slot="content"> <template slot="content">
<p>{{ versionMessage }}</p> <p>{{ versionMessage }}</p>
<a-button <a-button type="dashed" @click="handleShowVersionContent"></a-button>
type="dashed"
@click="handleShowVersionContent"
>查看详情</a-button>
</template> </template>
<a-button <a-button
:loading="checking" :loading="checking"
type="dashed" type="dashed"
shape="circle" shape="circle"
:icon="isLatest?'check-circle':'exclamation-circle'" :icon="isLatest ? 'check-circle' : 'exclamation-circle'"
></a-button> ></a-button>
</a-popover> </a-popover>
<ul class="m-0 p-0 list-none"> <ul class="p-0 m-0 list-none">
<li>版本{{ environments.version }}</li> <li>版本{{ environments.version }}</li>
<li>数据库{{ environments.database }}</li> <li>数据库{{ environments.database }}</li>
<li>运行模式{{ environments.mode }}</li> <li>运行模式{{ environments.mode }}</li>
<li>启动时间{{ environments.startTime | moment }}</li> <li>启动时间{{ environments.startTime | moment }}</li>
</ul> </ul>
<a <a href="https://halo.run" target="_blank" class="mr-3"
href="https://halo.run" >官网
target="_blank"
class="mr-3"
>官网
<a-icon type="link" /> <a-icon type="link" />
</a> </a>
<a <a href="https://docs.halo.run" target="_blank" class="mr-3"
href="https://docs.halo.run" >文档
target="_blank"
class="mr-3"
>文档
<a-icon type="link" /> <a-icon type="link" />
</a> </a>
<a <a href="https://github.com/halo-dev" target="_blank" class="mr-3"
href="https://github.com/halo-dev" >开源组织
target="_blank"
class="mr-3"
>开源组织
<a-icon type="link" /> <a-icon type="link" />
</a> </a>
<a <a href="https://bbs.halo.run" target="_blank" class="mr-3"
href="https://bbs.halo.run" >在线社区
target="_blank"
class="mr-3"
>在线社区
<a-icon type="link" /> <a-icon type="link" />
</a> </a>
</a-card> </a-card>
<a-card <a-card title="开发者" :bordered="false" :bodyStyle="{ padding: '16px' }" :loading="contributorsLoading">
title="开发者" <a :href="item.html_url" v-for="(item, index) in contributors" :key="index" target="_blank">
:bordered="false" <a-tooltip placement="top" :title="item.login">
:bodyStyle="{ padding: '16px' }" <a-avatar size="large" :src="item.avatar_url" :style="{ marginRight: '10px', marginBottom: '10px' }" />
:loading="contributorsLoading"
>
<a
:href="item.html_url"
v-for="(item,index) in contributors"
:key="index"
target="_blank"
>
<a-tooltip
placement="top"
:title="item.login"
>
<a-avatar
size="large"
:src="item.avatar_url"
:style="{ marginRight: '10px',marginBottom: '10px'}"
/>
</a-tooltip> </a-tooltip>
</a> </a>
</a-card> </a-card>
</a-card> </a-card>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24"> </a-col>
</a-col>
</a-row> </a-row>
<a-modal <a-modal
:title="versionContentModalTitle" :title="versionContentModalTitle"
:visible="versionContentVisible" :visible="versionContentVisible"
ok-text="查看更多" ok-text="查看更多"
@cancel="versionContentVisible=false" @cancel="versionContentVisible = false"
@ok="handleOpenVersionUrl" @ok="handleOpenVersionUrl"
:width="620" :width="620"
> >
@ -127,7 +80,7 @@ import marked from 'marked'
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
export default { export default {
components: { components: {
PageView, PageView
}, },
data() { data() {
return { return {
@ -152,14 +105,14 @@ export default {
received_events_url: '', received_events_url: '',
type: '', type: '',
site_admin: false, site_admin: false,
contributions: 0, contributions: 0
}, }
], ],
contributorsLoading: true, contributorsLoading: true,
checking: false, checking: false,
isLatest: false, isLatest: false,
latestData: {}, latestData: {},
versionContentVisible: false, versionContentVisible: false
} }
}, },
computed: { computed: {
@ -177,7 +130,7 @@ export default {
}, },
versionContentModalTitle() { versionContentModalTitle() {
return `${this.latestData.name} 更新内容` return `${this.latestData.name} 更新内容`
}, }
}, },
created() { created() {
this.getEnvironments() this.getEnvironments()
@ -185,7 +138,7 @@ export default {
}, },
methods: { methods: {
async getEnvironments() { async getEnvironments() {
await adminApi.environments().then((response) => { await adminApi.environments().then(response => {
this.environments = response.data.data this.environments = response.data.data
}) })
this.checkServerUpdate() this.checkServerUpdate()
@ -196,11 +149,11 @@ export default {
运行模式${this.environments.mode} 运行模式${this.environments.mode}
User Agent${navigator.userAgent}` User Agent${navigator.userAgent}`
this.$copyText(text) this.$copyText(text)
.then((message) => { .then(message => {
this.$log.debug('copy', message) this.$log.debug('copy', message)
this.$message.success('复制成功!') this.$message.success('复制成功!')
}) })
.catch((err) => { .catch(err => {
this.$log.debug('copy.err', err) this.$log.debug('copy.err', err)
this.$message.error('复制失败!') this.$message.error('复制失败!')
}) })
@ -210,11 +163,11 @@ User Agent${navigator.userAgent}`
_this.contributorsLoading = true _this.contributorsLoading = true
axios axios
.get('https://api.github.com/repos/halo-dev/halo/contributors') .get('https://api.github.com/repos/halo-dev/halo/contributors')
.then((response) => { .then(response => {
_this.contributors = response.data _this.contributors = response.data
}) })
.catch(function(error) { .catch(function(error) {
console.error('Fetch contributors error', error) _this.$log.error('Fetch contributors error', error)
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
@ -227,7 +180,7 @@ User Agent${navigator.userAgent}`
_this.checking = true _this.checking = true
axios axios
.get('https://api.github.com/repos/halo-dev/halo/releases/latest') .get('https://api.github.com/repos/halo-dev/halo/releases/latest')
.then((response) => { .then(response => {
const data = response.data const data = response.data
_this.latestData = data _this.latestData = data
if (data.draft || data.prerelease) { if (data.draft || data.prerelease) {
@ -245,25 +198,25 @@ User Agent${navigator.userAgent}`
message: title, message: title,
description: content, description: content,
icon: <a-icon type="smile" style="color: #108ee9" />, icon: <a-icon type="smile" style="color: #108ee9" />,
btn: (h) => { btn: h => {
return h( return h(
'a-button', 'a-button',
{ {
props: { props: {
type: 'primary', type: 'primary',
size: 'small', size: 'small'
}, },
on: { on: {
click: () => this.handleShowVersionContent(), click: () => this.handleShowVersionContent()
}, }
}, },
'去看看' '去看看'
) )
}, }
}) })
}) })
.catch(function(error) { .catch(function(error) {
console.error('Check update fail', error) this.$log.error('Check update fail', error)
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
@ -290,7 +243,7 @@ User Agent${navigator.userAgent}`
return -1 return -1
} }
return major * 1000000 + minor * 1000 + micro return major * 1000000 + minor * 1000 + micro
}, }
}, }
} }
</script> </script>

View File

@ -1,34 +1,16 @@
<template> <template>
<div> <div>
<a-row <a-row type="flex" justify="center" align="middle" class="h-screen">
type="flex" <a-col :xxl="8" :xl="12" :lg="16" :md="20" :sm="20" :xs="23">
justify="center"
align="middle"
class="h-screen"
>
<a-col
:xxl="8"
:xl="12"
:lg="16"
:md="20"
:sm="20"
:xs="23"
>
<div class="card-container animated fadeIn"> <div class="card-container animated fadeIn">
<a-card <a-card :bordered="false" style="box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;">
:bordered="false"
style="box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;"
>
<div class="halo-logo"> <div class="halo-logo">
<span>Halo <span
>Halo
<small>安装向导</small> <small>安装向导</small>
</span> </span>
</div> </div>
<a-alert <a-alert :message="`欢迎使用 Halo您正在安装的是 Halo ${VERSION}。`" type="success" show-icon />
:message="`欢迎使用 Halo您正在安装的是 Halo ${VERSION}。`"
type="success"
show-icon
/>
<!-- Blogger info --> <!-- Blogger info -->
<div class="mt-5 mb-5"> <div class="mt-5 mb-5">
<a-radio-group v-model="installationMode"> <a-radio-group v-model="installationMode">
@ -48,111 +30,51 @@
layout="horizontal" layout="horizontal"
v-show="isInstallMode" v-show="isInstallMode"
> >
<a-divider <a-divider orientation="left" dashed>
orientation="left"
dashed
>
管理员信息 管理员信息
</a-divider> </a-divider>
<a-form-model-item prop="username"> <a-form-model-item prop="username">
<a-input <a-input v-model="form.model.username" placeholder="用户名">
v-model="form.model.username" <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
placeholder="用户名"
>
<a-icon
slot="prefix"
type="user"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="username"> <a-form-model-item prop="username">
<a-input <a-input v-model="form.model.nickname" placeholder="用户昵称">
v-model="form.model.nickname" <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
placeholder="用户昵称"
>
<a-icon
slot="prefix"
type="user"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="email"> <a-form-model-item prop="email">
<a-input <a-input v-model="form.model.email" placeholder="用户邮箱">
v-model="form.model.email" <a-icon slot="prefix" type="mail" style="color: rgba(0,0,0,.25)" />
placeholder="用户邮箱"
>
<a-icon
slot="prefix"
type="mail"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="password"> <a-form-model-item prop="password">
<a-input <a-input v-model="form.model.password" type="password" placeholder="登录密码8-100位">
v-model="form.model.password" <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
type="password"
placeholder="登录密码8-100位"
>
<a-icon
slot="prefix"
type="lock"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="confirmPassword"> <a-form-model-item prop="confirmPassword">
<a-input <a-input v-model="form.model.confirmPassword" type="password" placeholder="确认登录密码">
v-model="form.model.confirmPassword" <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
type="password"
placeholder="确认登录密码"
>
<a-icon
slot="prefix"
type="lock"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-divider <a-divider orientation="left" dashed>
orientation="left"
dashed
>
站点信息 站点信息
</a-divider> </a-divider>
<a-form-model-item prop="url"> <a-form-model-item prop="url">
<a-input <a-input v-model="form.model.url" placeholder="博客地址">
v-model="form.model.url" <a-icon slot="prefix" type="link" style="color: rgba(0,0,0,.25)" />
placeholder="博客地址"
>
<a-icon
slot="prefix"
type="link"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="title"> <a-form-model-item prop="title">
<a-input <a-input v-model="form.model.title" placeholder="博客标题">
v-model="form.model.title" <a-icon slot="prefix" type="book" style="color: rgba(0,0,0,.25)" />
placeholder="博客标题"
>
<a-icon
slot="prefix"
type="book"
style="color: rgba(0,0,0,.25)"
/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
<!-- Data migration --> <!-- Data migration -->
<div <div class="animated fadeIn" v-show="isImportMode">
class="animated fadeIn"
v-show="isImportMode"
>
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
@ -222,11 +144,11 @@ export default {
rules: { rules: {
username: [ username: [
{ required: true, message: '* 用户名不能为空', trigger: ['change'] }, { required: true, message: '* 用户名不能为空', trigger: ['change'] },
{ max: 50, message: '* 用户名的字符长度不能超过 50', trigger: ['change'] }, { max: 50, message: '* 用户名的字符长度不能超过 50', trigger: ['change'] }
], ],
nickname: [ nickname: [
{ required: true, message: '* 用户昵称不能为空', trigger: ['change'] }, { required: true, message: '* 用户昵称不能为空', trigger: ['change'] },
{ max: 255, message: '* 用户昵称的字符长度不能超过 255', trigger: ['change'] }, { max: 255, message: '* 用户昵称的字符长度不能超过 255', trigger: ['change'] }
], ],
email: [ email: [
{ required: true, message: '* 电子邮件地址不能为空', trigger: ['change'] }, { required: true, message: '* 电子邮件地址不能为空', trigger: ['change'] },
@ -234,27 +156,27 @@ export default {
{ {
pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g, pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g,
message: '* 电子邮件地址的格式不正确', message: '* 电子邮件地址的格式不正确',
trigger: ['change'], trigger: ['change']
}, }
], ],
password: [ password: [
{ required: true, message: '* 密码不能为空', trigger: ['change'] }, { required: true, message: '* 密码不能为空', trigger: ['change'] },
{ min: 8, max: 100, message: '* 密码的字符长度必须在 8 - 100 之间', trigger: ['change'] }, { min: 8, max: 100, message: '* 密码的字符长度必须在 8 - 100 之间', trigger: ['change'] }
], ],
confirmPassword: [ confirmPassword: [
{ required: true, message: '* 确认密码不能为空', trigger: ['change'] }, { required: true, message: '* 确认密码不能为空', trigger: ['change'] },
{ validator: confirmPasswordValidate, trigger: ['change'] }, { validator: confirmPasswordValidate, trigger: ['change'] }
], ],
url: [{ required: true, message: '* 博客地址不能为空', trigger: ['change'] }], url: [{ required: true, message: '* 博客地址不能为空', trigger: ['change'] }],
title: [{ required: true, message: '* 博客标题不能为空', trigger: ['change'] }], title: [{ required: true, message: '* 博客标题不能为空', trigger: ['change'] }]
}, },
installing: false, installing: false,
installErrored: false, installErrored: false,
importing: false, importing: false,
importErrored: false, importErrored: false,
importData: null, importData: null
}, }
} }
}, },
beforeMount() { beforeMount() {
@ -267,7 +189,7 @@ export default {
}, },
isImportMode() { isImportMode() {
return this.installationMode === 'import' return this.installationMode === 'import'
}, }
}, },
methods: { methods: {
...mapActions(['installCleanToken']), ...mapActions(['installCleanToken']),
@ -278,11 +200,11 @@ export default {
} }
}, },
handleInstall() { handleInstall() {
this.$refs.installationForm.validate((valid) => { this.$refs.installationForm.validate(valid => {
if (valid) { if (valid) {
this.form.installing = true this.form.installing = true
this.installCleanToken(this.form.model) this.installCleanToken(this.form.model)
.then((response) => { .then(response => {
this.$log.debug('Installation response', response) this.$log.debug('Installation response', response)
}) })
.catch(() => { .catch(() => {
@ -307,7 +229,7 @@ export default {
onImportUpload(data) { onImportUpload(data) {
this.$log.debug('Selected data', data) this.$log.debug('Selected data', data)
this.form.importData = data this.form.importData = data
return new Promise((resolve, reject) => { return new Promise(resolve => {
this.$log.debug('Handle uploading') this.$log.debug('Handle uploading')
resolve() resolve()
}) })
@ -320,7 +242,7 @@ export default {
this.form.importing = true this.form.importing = true
migrateApi migrateApi
.migrate(this.form.importData) .migrate(this.form.importData)
.then((response) => { .then(() => {
this.$log.debug('Migrated successfullly') this.$log.debug('Migrated successfullly')
}) })
.catch(() => { .catch(() => {
@ -339,8 +261,8 @@ export default {
this.$message.success('导入成功!') this.$message.success('导入成功!')
this.$router.push({ name: 'Login' }) this.$router.push({ name: 'Login' })
} }
}, }
}, }
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -1,157 +1,125 @@
<template> <template>
<page-view :title="advancedOptions?'高级选项':'基础选项'"> <page-view :title="advancedOptions ? '高级选项' : '基础选项'">
<template slot="extra"> <template slot="extra">
<a-button <a-button type="link" @click="advancedOptions = !advancedOptions" style="padding:0">
type="link" 切换到{{ advancedOptions ? '基础选项' : '高级选项' }}
@click="advancedOptions = !advancedOptions"
style="padding:0"
>
切换到{{ advancedOptions?'基础选项':'高级选项' }}
</a-button> </a-button>
</template> </template>
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<div class="card-container"> <div class="card-container">
<a-tabs <a-tabs type="card" class="general" v-if="!advancedOptions">
type="card"
class="general"
v-if="!advancedOptions"
>
<a-tab-pane key="general"> <a-tab-pane key="general">
<span slot="tab"> <span slot="tab"> <a-icon type="tool" />常规设置 </span>
<a-icon type="tool" />常规设置
</span>
<GeneralTab <GeneralTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="seo"> <a-tab-pane key="seo">
<span slot="tab"> <span slot="tab"> <a-icon type="global" />SEO 设置 </span>
<a-icon type="global" />SEO 设置
</span>
<SeoTab <SeoTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="post"> <a-tab-pane key="post">
<span slot="tab"> <span slot="tab"> <a-icon type="form" />文章设置 </span>
<a-icon type="form" />文章设置
</span>
<PostTab <PostTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="comment"> <a-tab-pane key="comment">
<span slot="tab"> <span slot="tab"> <a-icon type="message" />评论设置 </span>
<a-icon type="message" />评论设置
</span>
<CommentTab <CommentTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="attachment"> <a-tab-pane key="attachment">
<span slot="tab"> <span slot="tab"> <a-icon type="picture" />附件设置 </span>
<a-icon type="picture" />附件设置
</span>
<AttachmentTab <AttachmentTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="smtp"> <a-tab-pane key="smtp">
<span slot="tab"> <span slot="tab"> <a-icon type="mail" />SMTP 服务 </span>
<a-icon type="mail" />SMTP 服务
</span>
<SmtpTab <SmtpTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="other"> <a-tab-pane key="other">
<span slot="tab"> <span slot="tab"> <a-icon type="align-left" />其他设置 </span>
<a-icon type="align-left" />其他设置
</span>
<OtherTab <OtherTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
<a-tabs <a-tabs type="card" class="advanced" v-else>
type="card"
class="advanced"
v-else
>
<a-tab-pane key="permalink"> <a-tab-pane key="permalink">
<span slot="tab"> <span slot="tab"> <a-icon type="link" />固定链接 </span>
<a-icon type="link" />固定链接
</span>
<PermalinkTab <PermalinkTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="api"> <a-tab-pane key="api">
<span slot="tab"> <span slot="tab"> <a-icon type="api" />API 设置 </span>
<a-icon type="api" />API 设置
</span>
<ApiTab <ApiTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="advanced-other"> <a-tab-pane key="advanced-other">
<span slot="tab"> <span slot="tab"> <a-icon type="align-left" />其他设置 </span>
<a-icon type="align-left" />其他设置
</span>
<AdvancedOtherTab <AdvancedOtherTab
:options="options" :options="options"
@onChange="onOptionsChange" @onChange="onOptionsChange"
@onSave="onSaveOptions" @onSave="onSaveOptions"
:saving="saving" :saving="saving"
:errored="errored" :errored="errored"
@callback="errored=false" @callback="errored = false"
/> />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@ -187,14 +155,14 @@ export default {
OtherTab, OtherTab,
PermalinkTab, PermalinkTab,
ApiTab, ApiTab,
AdvancedOtherTab, AdvancedOtherTab
}, },
data() { data() {
return { return {
options: {}, options: {},
advancedOptions: false, advancedOptions: false,
saving: false, saving: false,
errored: false, errored: false
} }
}, },
created() { created() {
@ -203,7 +171,7 @@ export default {
methods: { methods: {
...mapActions(['refreshUserCache', 'refreshOptionsCache']), ...mapActions(['refreshUserCache', 'refreshOptionsCache']),
hanldeListOptions() { hanldeListOptions() {
optionApi.listAll().then((response) => { optionApi.listAll().then(response => {
this.options = response.data.data this.options = response.data.data
}) })
}, },
@ -225,7 +193,7 @@ export default {
this.refreshOptionsCache() this.refreshOptionsCache()
this.refreshUserCache() this.refreshUserCache()
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,95 +1,42 @@
<template> <template>
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col v-if="options.developer_mode" :xl="6" :lg="6" :md="12" :sm="24" :xs="24" class="pb-3">
v-if="options.developer_mode" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:xl="6" <div slot="title"><a-icon type="experiment" /> 开发者选项</div>
:lg="6"
:md="12"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div slot="title">
<a-icon type="experiment" /> 开发者选项
</div>
<p style="min-height: 50px;">点击进入开发者选项页面</p> <p style="min-height: 50px;">点击进入开发者选项页面</p>
<a-button <a-button type="primary" class="float-right" @click="handleToDeveloperOptions()"></a-button>
type="primary"
class="float-right"
@click="handleToDeveloperOptions()"
>进入</a-button>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24" class="mb-3">
:xl="6" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:lg="6" <div slot="title"><a-icon type="hdd" /> 博客备份</div>
:md="12"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div slot="title">
<a-icon type="hdd" /> 博客备份
</div>
<p style="min-height: 50px;">支持备份全站数据和数据导出支持下载到本地</p> <p style="min-height: 50px;">支持备份全站数据和数据导出支持下载到本地</p>
<a-dropdown class="float-right"> <a-dropdown class="float-right">
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item key="1" @click="backupWorkDirDrawerVisible = true">
key="1"
@click="backupWorkDirDrawerVisible = true"
>
整站备份 整站备份
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="2" @click="exportDataDrawerVisible = true">
key="2"
@click="exportDataDrawerVisible = true"
>
数据导出 数据导出
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="3" @click="exportMarkdownDrawerVisible = true">
key="3"
@click="exportMarkdownDrawerVisible = true"
>
导出文章为 Markdown 文档 导出文章为 Markdown 文档
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<a-button class="ml-2"> 备份 <a-button class="ml-2">
备份
<a-icon type="down" /> <a-icon type="down" />
</a-button> </a-button>
</a-dropdown> </a-dropdown>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24" class="pb-3">
:xl="6" <a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
:lg="6" <div slot="title"><a-icon type="file-markdown" /> Markdown 文章导入</div>
:md="12"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div slot="title">
<a-icon type="file-markdown" /> Markdown 文章导入
</div>
<p style="min-height: 50px;">支持 Hexo/Jekyll 文章导入并解析元数据</p> <p style="min-height: 50px;">支持 Hexo/Jekyll 文章导入并解析元数据</p>
<a-button <a-button type="primary" class="float-right" @click="markdownUpload = true">导入</a-button>
type="primary"
class="float-right"
@click="markdownUpload = true"
>导入</a-button>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
@ -131,11 +78,11 @@ export default {
exportDataDrawerVisible: false, exportDataDrawerVisible: false,
exportMarkdownDrawerVisible: false, exportMarkdownDrawerVisible: false,
markdownUpload: false, markdownUpload: false,
uploadHandler: backupApi.importMarkdown, uploadHandler: backupApi.importMarkdown
} }
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options'])
}, },
methods: { methods: {
handleChange(info) { handleChange(info) {
@ -154,7 +101,7 @@ export default {
}, },
onUploadClose() { onUploadClose() {
this.$refs.upload.handleClearFileList() this.$refs.upload.handleClearFileList()
}, }
}, }
} }
</script> </script>

View File

@ -1,17 +1,14 @@
<template> <template>
<a-drawer <a-drawer
title="整站备份" title="整站备份"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-alert <a-alert
message="注意:备份后生成的压缩文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。" message="注意:备份后生成的压缩文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。"
@ -19,16 +16,8 @@
closable closable
/> />
<a-divider>历史备份</a-divider> <a-divider>历史备份</a-divider>
<a-list <a-list itemLayout="vertical" size="small" :dataSource="backups" :loading="loading">
itemLayout="vertical" <a-list-item slot="renderItem" slot-scope="backup">
size="small"
:dataSource="backups"
:loading="loading"
>
<a-list-item
slot="renderItem"
slot-scope="backup"
>
<a-button <a-button
slot="extra" slot="extra"
type="link" type="link"
@ -36,17 +25,11 @@
icon="delete" icon="delete"
:loading="backup.deleting" :loading="backup.deleting"
@click="handleBackupDeleteClick(backup)" @click="handleBackupDeleteClick(backup)"
>删除</a-button> >删除</a-button
>
<a-list-item-meta> <a-list-item-meta>
<a <a slot="title" href="javascript:void(0)" @click="handleDownloadBackupPackage(backup)">
slot="title" <a-icon type="schedule" style="color: #52c41a" />
href="javascript:void(0)"
@click="handleDownloadBackupPackage(backup)"
>
<a-icon
type="schedule"
style="color: #52c41a"
/>
{{ backup.filename }} {{ backup.filename }}
</a> </a>
<p slot="description">{{ backup.updateTime | timeAgo }}/{{ backup.fileSize | fileSizeFormat }}</p> <p slot="description">{{ backup.updateTime | timeAgo }}/{{ backup.fileSize | fileSizeFormat }}</p>
@ -69,12 +52,7 @@
loadedText="备份成功" loadedText="备份成功"
erroredText="备份失败" erroredText="备份失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button type="dashed" icon="reload" :loading="loading" @click="handleListBackups"></a-button>
type="dashed"
icon="reload"
:loading="loading"
@click="handleListBackups"
>刷新</a-button>
</a-space> </a-space>
</div> </div>
</a-drawer> </a-drawer>
@ -90,19 +68,19 @@ export default {
backuping: false, backuping: false,
loading: false, loading: false,
backupErrored: false, backupErrored: false,
backups: [], backups: []
} }
}, },
model: { model: {
prop: 'visible', prop: 'visible',
event: 'close', event: 'close'
}, },
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
methods: { methods: {
handleAfterVisibleChanged(visible) { handleAfterVisibleChanged(visible) {
@ -114,7 +92,7 @@ export default {
this.loading = true this.loading = true
backupApi backupApi
.listWorkDirBackups() .listWorkDirBackups()
.then((response) => { .then(response => {
this.backups = response.data.data this.backups = response.data.data
}) })
.finally(() => { .finally(() => {
@ -155,7 +133,7 @@ export default {
handleDownloadBackupPackage(item) { handleDownloadBackupPackage(item) {
backupApi backupApi
.fetchWorkDir(item.filename) .fetchWorkDir(item.filename)
.then((response) => { .then(response => {
var downloadElement = document.createElement('a') var downloadElement = document.createElement('a')
var href = new window.URL(response.data.data.downloadLink) var href = new window.URL(response.data.data.downloadLink)
downloadElement.href = href downloadElement.href = href
@ -165,13 +143,13 @@ export default {
document.body.removeChild(downloadElement) document.body.removeChild(downloadElement)
window.URL.revokeObjectURL(href) window.URL.revokeObjectURL(href)
}) })
.catch((error) => { .catch(error => {
this.$message.error(error.data.message) this.$message.error(error.data.message)
}) })
}, },
onClose() { onClose() {
this.$emit('close', false) this.$emit('close', false)
}, }
}, }
} }
</script> </script>

View File

@ -1,17 +1,14 @@
<template> <template>
<a-drawer <a-drawer
title="数据导出" title="数据导出"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-alert <a-alert
message="注意:导出后的数据文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。" message="注意:导出后的数据文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。"
@ -19,16 +16,8 @@
closable closable
/> />
<a-divider>历史文件</a-divider> <a-divider>历史文件</a-divider>
<a-list <a-list itemLayout="vertical" size="small" :dataSource="files" :loading="loading">
itemLayout="vertical" <a-list-item slot="renderItem" slot-scope="file">
size="small"
:dataSource="files"
:loading="loading"
>
<a-list-item
slot="renderItem"
slot-scope="file"
>
<a-button <a-button
slot="extra" slot="extra"
type="link" type="link"
@ -36,17 +25,11 @@
icon="delete" icon="delete"
:loading="file.deleting" :loading="file.deleting"
@click="handleFileDeleteClick(file)" @click="handleFileDeleteClick(file)"
>删除</a-button> >删除</a-button
>
<a-list-item-meta> <a-list-item-meta>
<a <a slot="title" href="javascript:void(0)" @click="handleDownloadBackupFile(file)">
slot="title" <a-icon type="schedule" style="color: #52c41a" />
href="javascript:void(0)"
@click="handleDownloadBackupFile(file)"
>
<a-icon
type="schedule"
style="color: #52c41a"
/>
{{ file.filename }} {{ file.filename }}
</a> </a>
<p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p> <p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p>
@ -69,12 +52,7 @@
loadedText="备份成功" loadedText="备份成功"
erroredText="备份失败" erroredText="备份失败"
></ReactiveButton> ></ReactiveButton>
<a-button <a-button type="dashed" icon="reload" :loading="loading" @click="handleListBackups"></a-button>
type="dashed"
icon="reload"
:loading="loading"
@click="handleListBackups"
>刷新</a-button>
</a-space> </a-space>
</div> </div>
</a-drawer> </a-drawer>
@ -90,19 +68,19 @@ export default {
backuping: false, backuping: false,
loading: false, loading: false,
backupErrored: false, backupErrored: false,
files: [], files: []
} }
}, },
model: { model: {
prop: 'visible', prop: 'visible',
event: 'close', event: 'close'
}, },
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
methods: { methods: {
handleAfterVisibleChanged(visible) { handleAfterVisibleChanged(visible) {
@ -114,7 +92,7 @@ export default {
this.loading = true this.loading = true
backupApi backupApi
.listExportedData() .listExportedData()
.then((response) => { .then(response => {
this.files = response.data.data this.files = response.data.data
}) })
.finally(() => { .finally(() => {
@ -155,7 +133,7 @@ export default {
handleDownloadBackupFile(item) { handleDownloadBackupFile(item) {
backupApi backupApi
.fetchData(item.filename) .fetchData(item.filename)
.then((response) => { .then(response => {
var downloadElement = document.createElement('a') var downloadElement = document.createElement('a')
var href = new window.URL(response.data.data.downloadLink) var href = new window.URL(response.data.data.downloadLink)
downloadElement.href = href downloadElement.href = href
@ -171,7 +149,7 @@ export default {
}, },
onClose() { onClose() {
this.$emit('close', false) this.$emit('close', false)
}, }
}, }
} }
</script> </script>

View File

@ -1,17 +1,14 @@
<template> <template>
<a-drawer <a-drawer
title="导出文章为 Markdown 文档" title="导出文章为 Markdown 文档"
:width="isMobile()?'100%':'480'" :width="isMobile() ? '100%' : '480'"
closable closable
:visible="visible" :visible="visible"
destroyOnClose destroyOnClose
@close="onClose" @close="onClose"
:afterVisibleChange="handleAfterVisibleChanged" :afterVisibleChange="handleAfterVisibleChanged"
> >
<a-row <a-row type="flex" align="middle">
type="flex"
align="middle"
>
<a-col :span="24"> <a-col :span="24">
<a-alert <a-alert
message="注意:导出后的数据文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。" message="注意:导出后的数据文件存储在临时文件中,重启服务器会造成备份文件的丢失,所以请尽快下载。"
@ -19,16 +16,8 @@
closable closable
/> />
<a-divider>历史文件</a-divider> <a-divider>历史文件</a-divider>
<a-list <a-list itemLayout="vertical" size="small" :dataSource="files" :loading="loading">
itemLayout="vertical" <a-list-item slot="renderItem" slot-scope="file">
size="small"
:dataSource="files"
:loading="loading"
>
<a-list-item
slot="renderItem"
slot-scope="file"
>
<a-button <a-button
slot="extra" slot="extra"
type="link" type="link"
@ -36,17 +25,11 @@
icon="delete" icon="delete"
:loading="file.deleting" :loading="file.deleting"
@click="handleFileDeleteClick(file)" @click="handleFileDeleteClick(file)"
>删除</a-button> >删除</a-button
>
<a-list-item-meta> <a-list-item-meta>
<a <a slot="title" href="javascript:void(0)" @click="handleDownloadMarkdownPackage(file)">
slot="title" <a-icon type="schedule" style="color: #52c41a" />
href="javascript:void(0)"
@click="handleDownloadMarkdownPackage(file)"
>
<a-icon
type="schedule"
style="color: #52c41a"
/>
{{ file.filename }} {{ file.filename }}
</a> </a>
<p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p> <p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p>
@ -76,12 +59,7 @@
erroredText="备份失败" erroredText="备份失败"
></ReactiveButton> ></ReactiveButton>
</a-popconfirm> </a-popconfirm>
<a-button <a-button type="dashed" icon="reload" :loading="loading" @click="handleListBackups"></a-button>
type="dashed"
icon="reload"
:loading="loading"
@click="handleListBackups"
>刷新</a-button>
</a-space> </a-space>
</div> </div>
</a-drawer> </a-drawer>
@ -97,19 +75,19 @@ export default {
backuping: false, backuping: false,
loading: false, loading: false,
backupErrored: false, backupErrored: false,
files: [], files: []
} }
}, },
model: { model: {
prop: 'visible', prop: 'visible',
event: 'close', event: 'close'
}, },
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true, default: true
}, }
}, },
methods: { methods: {
handleAfterVisibleChanged(visible) { handleAfterVisibleChanged(visible) {
@ -121,7 +99,7 @@ export default {
this.loading = true this.loading = true
backupApi backupApi
.listExportedMarkdowns() .listExportedMarkdowns()
.then((response) => { .then(response => {
this.files = response.data.data this.files = response.data.data
}) })
.finally(() => { .finally(() => {
@ -162,7 +140,7 @@ export default {
handleDownloadMarkdownPackage(item) { handleDownloadMarkdownPackage(item) {
backupApi backupApi
.fetchMarkdown(item.filename) .fetchMarkdown(item.filename)
.then((response) => { .then(response => {
var downloadElement = document.createElement('a') var downloadElement = document.createElement('a')
var href = new window.URL(response.data.data.downloadLink) var href = new window.URL(response.data.data.downloadLink)
downloadElement.href = href downloadElement.href = href
@ -178,7 +156,7 @@ export default {
}, },
onClose() { onClose() {
this.$emit('close', false) this.$emit('close', false)
}, }
}, }
} }
</script> </script>

View File

@ -2,39 +2,26 @@
<page-view> <page-view>
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<div <div class="card-container" v-if="options.developer_mode">
class="card-container"
v-if="options.developer_mode"
>
<a-tabs type="card"> <a-tabs type="card">
<a-tab-pane key="environment"> <a-tab-pane key="environment">
<span slot="tab"> <span slot="tab"> <a-icon type="safety" />运行环境 </span>
<a-icon type="safety" />运行环境
</span>
<Environment /> <Environment />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="runtimeLogs"> <a-tab-pane key="runtimeLogs">
<span slot="tab"> <span slot="tab"> <a-icon type="code" />实时日志 </span>
<a-icon type="code" />实时日志
</span>
<RuntimeLogs /> <RuntimeLogs />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="optionsList"> <a-tab-pane key="optionsList">
<span slot="tab"> <span slot="tab"> <a-icon type="table" />系统变量 </span>
<a-icon type="table" />系统变量
</span>
<OptionsList /> <OptionsList />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="staticStorage"> <a-tab-pane key="staticStorage">
<span slot="tab"> <span slot="tab"> <a-icon type="cloud" />静态存储 </span>
<a-icon type="cloud" />静态存储
</span>
<StaticStorage /> <StaticStorage />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="settings"> <a-tab-pane key="settings">
<span slot="tab"> <span slot="tab"> <a-icon type="setting" />设置 </span>
<a-icon type="setting" />设置
</span>
<SettingsForm /> <SettingsForm />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@ -65,10 +52,10 @@ export default {
RuntimeLogs, RuntimeLogs,
SettingsForm, SettingsForm,
OptionsList, OptionsList,
StaticStorage, StaticStorage
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options'])
}, }
} }
</script> </script>

View File

@ -1,20 +1,8 @@
<template> <template>
<div> <div>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24" class="mb-3">
:xl="12" <a-card title="服务器" :bordered="false" hoverable :bodyStyle="{ padding: 0 }">
:lg="12"
:md="24"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
title="服务器"
:bordered="false"
hoverable
:bodyStyle="{ padding: 0 }"
>
<table class="w-full"> <table class="w-full">
<tbody class="ant-table-tbody"> <tbody class="ant-table-tbody">
<tr> <tr>
@ -46,20 +34,8 @@
</a-card> </a-card>
<a-divider dashed /> <a-divider dashed />
</a-col> </a-col>
<a-col <a-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24" class="mb-3">
:xl="12" <a-card title="使用情况" :bordered="false" hoverable :bodyStyle="{ padding: 0 }">
:lg="12"
:md="24"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
title="使用情况"
:bordered="false"
hoverable
:bodyStyle="{ padding: 0 }"
>
<table class="w-full"> <table class="w-full">
<tbody class="ant-table-tbody"> <tbody class="ant-table-tbody">
<tr> <tr>
@ -91,20 +67,8 @@
</a-card> </a-card>
<a-divider dashed /> <a-divider dashed />
</a-col> </a-col>
<a-col <a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24" class="mb-3">
:xl="24" <a-card title="环境" :bordered="false" hoverable :bodyStyle="{ padding: 0 }">
:lg="24"
:md="24"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
title="环境"
:bordered="false"
hoverable
:bodyStyle="{ padding: 0 }"
>
<table class="w-full"> <table class="w-full">
<tbody class="ant-table-tbody"> <tbody class="ant-table-tbody">
<tr> <tr>
@ -118,10 +82,7 @@
<tr> <tr>
<td>Java Home</td> <td>Java Home</td>
<td> <td>
<ellipsis <ellipsis :length="isMobile() ? 50 : 256" tooltip>
:length="isMobile() ? 50 : 256"
tooltip
>
{{ systemProperties['java.home'].value }} {{ systemProperties['java.home'].value }}
</ellipsis> </ellipsis>
</td> </td>
@ -131,20 +92,8 @@
</a-card> </a-card>
<a-divider dashed /> <a-divider dashed />
</a-col> </a-col>
<a-col <a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24" class="mb-3">
:xl="24" <a-card title="应用" :bordered="false" hoverable :bodyStyle="{ padding: 0 }">
:lg="24"
:md="24"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
title="应用"
:bordered="false"
hoverable
:bodyStyle="{ padding: 0 }"
>
<table class="w-full"> <table class="w-full">
<tbody class="ant-table-tbody"> <tbody class="ant-table-tbody">
<tr> <tr>
@ -161,15 +110,12 @@
</tr> </tr>
<tr> <tr>
<td>已启动时间</td> <td>已启动时间</td>
<td>{{ system.process.uptime | dayTime }} </td> <td>{{ system.process.uptime | dayTime }}</td>
</tr> </tr>
<tr> <tr>
<td>启动目录</td> <td>启动目录</td>
<td> <td>
<ellipsis <ellipsis :length="isMobile() ? 50 : 256" tooltip>
:length="isMobile() ? 50 : 256"
tooltip
>
{{ systemProperties['user.dir'].value }} {{ systemProperties['user.dir'].value }}
</ellipsis> </ellipsis>
</td> </td>
@ -177,10 +123,7 @@
<tr> <tr>
<td>日志目录</td> <td>日志目录</td>
<td> <td>
<ellipsis <ellipsis :length="isMobile() ? 50 : 256" tooltip>
:length="isMobile() ? 50 : 256"
tooltip
>
{{ systemProperties['LOG_FILE'].value }} {{ systemProperties['LOG_FILE'].value }}
</ellipsis> </ellipsis>
</td> </td>
@ -191,13 +134,7 @@
</a-col> </a-col>
</a-row> </a-row>
<div style="position: fixed;bottom: 30px;right: 30px;"> <div style="position: fixed;bottom: 30px;right: 30px;">
<a-button <a-button type="primary" shape="circle" icon="sync" size="large" @click="handleRefresh"></a-button>
type="primary"
shape="circle"
icon="sync"
size="large"
@click="handleRefresh"
></a-button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,53 +1,28 @@
<template> <template>
<div class="option-tab-wrapper"> <div class="option-tab-wrapper">
<a-card <a-card :bordered="false" :bodyStyle="{ padding: 0 }">
:bordered="false"
:bodyStyle="{ padding: 0 }"
>
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input v-model="queryParam.keyword" @keyup.enter="handleQuery()" />
v-model="queryParam.keyword"
@keyup.enter="handleQuery()"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="6" :sm="24">
:md="6"
:sm="24"
>
<a-form-item label="类型:"> <a-form-item label="类型:">
<a-select <a-select v-model="queryParam.type" placeholder="请选择类型" @change="handleQuery()" allowClear>
v-model="queryParam.type" <a-select-option v-for="item in Object.keys(optionType)" :key="item" :value="item">{{
placeholder="请选择类型" optionType[item].text
@change="handleQuery()" }}</a-select-option>
allowClear
>
<a-select-option
v-for="item in Object.keys(optionType)"
:key="item"
:value="item"
>{{ optionType[item].text }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :md="12" :sm="24">
:md="12"
:sm="24"
>
<span class="table-page-search-submitButtons"> <span class="table-page-search-submitButtons">
<a-space> <a-space>
<a-button <a-button type="primary" @click="handleQuery()"></a-button>
type="primary"
@click="handleQuery()"
>查询</a-button>
<a-button @click="handleResetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
@ -56,11 +31,7 @@
</a-form> </a-form>
</div> </div>
<div class="table-operator"> <div class="table-operator">
<a-button <a-button type="primary" icon="plus" @click="handleOpenFormModal"></a-button>
type="primary"
icon="plus"
@click="handleOpenFormModal"
>新增</a-button>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<a-table <a-table
@ -71,16 +42,10 @@
:pagination="false" :pagination="false"
:scrollToFirstRowOnChange="true" :scrollToFirstRowOnChange="true"
> >
<span <span slot="type" slot-scope="typeProperty">
slot="type"
slot-scope="typeProperty"
>
{{ typeProperty.text }} {{ typeProperty.text }}
</span> </span>
<span <span slot="createTime" slot-scope="createTime">
slot="createTime"
slot-scope="createTime"
>
<a-tooltip placement="top"> <a-tooltip placement="top">
<template slot="title"> <template slot="title">
{{ createTime | moment }} {{ createTime | moment }}
@ -88,10 +53,7 @@
{{ createTime | timeAgo }} {{ createTime | timeAgo }}
</a-tooltip> </a-tooltip>
</span> </span>
<span <span slot="updateTime" slot-scope="updateTime">
slot="updateTime"
slot-scope="updateTime"
>
<a-tooltip placement="top"> <a-tooltip placement="top">
<template slot="title"> <template slot="title">
{{ updateTime | moment }} {{ updateTime | moment }}
@ -99,14 +61,8 @@
{{ updateTime | timeAgo }} {{ updateTime | timeAgo }}
</a-tooltip> </a-tooltip>
</span> </span>
<span <span slot="action" slot-scope="text, record">
slot="action" <a href="javascript:void(0);" @click="handleOpenEditFormModal(record)"></a>
slot-scope="text, record"
>
<a
href="javascript:void(0);"
@click="handleOpenEditFormModal(record)"
>编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
:title="'你确定要永久删除该变量?'" :title="'你确定要永久删除该变量?'"
@ -133,11 +89,7 @@
</div> </div>
</div> </div>
</a-card> </a-card>
<a-modal <a-modal v-model="form.visible" :title="formTitle" :afterClose="onFormClose">
v-model="form.visible"
:title="formTitle"
:afterClose="onFormClose"
>
<template slot="footer"> <template slot="footer">
<ReactiveButton <ReactiveButton
@click="handleSaveOrUpdate" @click="handleSaveOrUpdate"
@ -155,30 +107,12 @@
banner banner
closable closable
/> />
<a-form-model <a-form-model ref="optionForm" :model="form.model" :rules="form.rules" layout="vertical">
ref="optionForm" <a-form-model-item prop="key" label="Key">
:model="form.model" <a-input ref="keyInput" v-model="form.model.key" />
:rules="form.rules"
layout="vertical"
>
<a-form-model-item
prop="key"
label="Key"
>
<a-input
ref="keyInput"
v-model="form.model.key"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="value" label="Value">
prop="value" <a-input type="textarea" :autoSize="{ minRows: 5 }" v-model="form.model.value" />
label="Value"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="form.model.value"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
@ -192,38 +126,38 @@ const columns = [
title: 'Key', title: 'Key',
dataIndex: 'key', dataIndex: 'key',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'key' }, scopedSlots: { customRender: 'key' }
}, },
{ {
title: 'Value', title: 'Value',
dataIndex: 'value', dataIndex: 'value',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'value' }, scopedSlots: { customRender: 'value' }
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'typeProperty', dataIndex: 'typeProperty',
width: '100px', width: '100px',
scopedSlots: { customRender: 'type' }, scopedSlots: { customRender: 'type' }
}, },
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '更新时间', title: '更新时间',
dataIndex: 'updateTime', dataIndex: 'updateTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'updateTime' }, scopedSlots: { customRender: 'updateTime' }
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '120px', width: '120px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
name: 'OptionsList', name: 'OptionsList',
@ -235,14 +169,14 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1, total: 1
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null,
type: null, type: null
}, },
loading: false, loading: false,
options: [], options: [],
@ -252,23 +186,23 @@ export default {
model: {}, model: {},
rules: { rules: {
key: [{ required: true, message: '* Key 不能为空', trigger: ['change'] }], key: [{ required: true, message: '* Key 不能为空', trigger: ['change'] }],
value: [{ required: true, message: '* Value 不能为空', trigger: ['change'] }], value: [{ required: true, message: '* Value 不能为空', trigger: ['change'] }]
}, },
saving: false, saving: false,
saveErrored: false, saveErrored: false
}, }
} }
}, },
computed: { computed: {
formattedDatas() { formattedDatas() {
return this.options.map((option) => { return this.options.map(option => {
option.typeProperty = this.optionType[option.type] option.typeProperty = this.optionType[option.type]
return option return option
}) })
}, },
formTitle() { formTitle() {
return this.form.model.id ? '编辑' : '新增' return this.form.model.id ? '编辑' : '新增'
}, }
}, },
beforeMount() { beforeMount() {
this.hanldeListOptions() this.hanldeListOptions()
@ -282,7 +216,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
optionApi optionApi
.query(this.queryParam) .query(this.queryParam)
.then((response) => { .then(response => {
this.options = response.data.data.content this.options = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -298,7 +232,7 @@ export default {
handleDeleteOption(id) { handleDeleteOption(id) {
optionApi optionApi
.delete(id) .delete(id)
.then((response) => { .then(() => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -336,7 +270,7 @@ export default {
}, },
handleSaveOrUpdate() { handleSaveOrUpdate() {
const _this = this const _this = this
_this.$refs.optionForm.validate((valid) => { _this.$refs.optionForm.validate(valid => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.form.model.id) { if (_this.form.model.id) {
@ -375,7 +309,7 @@ export default {
this.hanldeListOptions() this.hanldeListOptions()
this.refreshOptionsCache() this.refreshOptionsCache()
} }
}, }
}, }
} }
</script> </script>

View File

@ -2,35 +2,19 @@
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item> <a-form-item>
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<codemirror <codemirror v-model="logContent" :options="codemirrorOptions"></codemirror>
v-model="logContent"
:options="codemirrorOptions"
></codemirror>
</a-spin> </a-spin>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-space> <a-space>
<a-select <a-select defaultValue="200" v-model="logLines" @change="handleLoadLogsLines" style="width: 100px">
defaultValue="200"
v-model="logLines"
@change="handleLoadLogsLines"
style="width: 100px"
>
<a-select-option value="200">200 </a-select-option> <a-select-option value="200">200 </a-select-option>
<a-select-option value="500">500 </a-select-option> <a-select-option value="500">500 </a-select-option>
<a-select-option value="800">800 </a-select-option> <a-select-option value="800">800 </a-select-option>
<a-select-option value="1000">1000 </a-select-option> <a-select-option value="1000">1000 </a-select-option>
</a-select> </a-select>
<a-button <a-button type="primary" @click="handleLoadLogsLines()" :loading="loading">刷新</a-button>
type="primary" <a-button type="dashed" @click="handleDownloadLogFile()" :loading="downloading">下载</a-button>
@click="handleLoadLogsLines()"
:loading="loading"
>刷新</a-button>
<a-button
type="dashed"
@click="handleDownloadLogFile()"
:loading="downloading"
>下载</a-button>
</a-space> </a-space>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -43,7 +27,7 @@ import { datetimeFormat } from '@/utils/datetime'
export default { export default {
name: 'RuntimeLogs', name: 'RuntimeLogs',
components: { components: {
codemirror, codemirror
}, },
data() { data() {
return { return {
@ -51,12 +35,12 @@ export default {
tabSize: 4, tabSize: 4,
mode: 'shell', mode: 'shell',
lineNumbers: true, lineNumbers: true,
line: true, line: true
}, },
logContent: '', logContent: '',
loading: false, loading: false,
logLines: 200, logLines: 200,
downloading: false, downloading: false
} }
}, },
beforeMount() { beforeMount() {
@ -71,7 +55,7 @@ export default {
this.loading = true this.loading = true
adminApi adminApi
.getLogFiles(this.logLines) .getLogFiles(this.logLines)
.then((response) => { .then(response => {
this.logContent = response.data.data this.logContent = response.data.data
}) })
.finally(() => { .finally(() => {
@ -85,7 +69,7 @@ export default {
this.downloading = true this.downloading = true
adminApi adminApi
.getLogFiles(this.logLines) .getLogFiles(this.logLines)
.then((response) => { .then(response => {
var blob = new Blob([response.data.data]) var blob = new Blob([response.data.data])
var downloadElement = document.createElement('a') var downloadElement = document.createElement('a')
var href = window.URL.createObjectURL(blob) var href = window.URL.createObjectURL(blob)
@ -105,7 +89,7 @@ export default {
hide() hide()
}, 400) }, 400)
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,8 +1,5 @@
<template> <template>
<a-form <a-form layout="vertical" :wrapperCol="wrapperCol">
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="开发者选项:"> <a-form-item label="开发者选项:">
<a-switch v-model="options.developer_mode" /> <a-switch v-model="options.developer_mode" />
</a-form-item> </a-form-item>
@ -10,7 +7,7 @@
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@click="handleSaveOptions" @click="handleSaveOptions"
@callback="errored=false" @callback="errored = false"
:loading="saving" :loading="saving"
:errored="errored" :errored="errored"
text="保存" text="保存"

View File

@ -1,26 +1,12 @@
<template> <template>
<div class="option-tab-wrapper"> <div class="option-tab-wrapper">
<a-card <a-card :bordered="false" :bodyStyle="{ padding: 0 }">
:bordered="false"
:bodyStyle="{ padding: 0 }"
>
<div class="table-operator"> <div class="table-operator">
<a-button <a-button type="primary" icon="cloud-upload" @click="uploadModal.visible = true">上传</a-button>
type="primary" <a-button icon="plus" @click="handleOpenCreateDirectoryModal({})">
icon="cloud-upload"
@click="uploadModal.visible = true"
>上传</a-button>
<a-button
icon="plus"
@click="handleOpenCreateDirectoryModal({})"
>
新建文件夹 新建文件夹
</a-button> </a-button>
<a-button <a-button icon="sync" @click="handleListStatics" :loading="list.loading">
icon="sync"
@click="handleListStatics"
:loading="list.loading"
>
刷新 刷新
</a-button> </a-button>
</div> </div>
@ -33,56 +19,27 @@
size="middle" size="middle"
:loading="list.loading" :loading="list.loading"
> >
<span <span slot="name" slot-scope="name">
slot="name" <ellipsis :length="64" tooltip>
slot-scope="name"
>
<ellipsis
:length="64"
tooltip
>
{{ name }} {{ name }}
</ellipsis> </ellipsis>
</span> </span>
<span <span slot="createTime" slot-scope="createTime">
slot="createTime"
slot-scope="createTime"
>
{{ createTime | moment }} {{ createTime | moment }}
</span> </span>
<span <span slot="action" slot-scope="text, record">
slot="action" <a href="javascript:void(0);" v-if="!record.isFile" @click="handleUpload(record)"></a>
slot-scope="text, record" <a :href="options.blog_url + record.relativePath" target="_blank" v-else>访</a>
>
<a
href="javascript:void(0);"
v-if="!record.isFile"
@click="handleUpload(record)"
>上传</a>
<a
:href="options.blog_url+record.relativePath"
target="_blank"
v-else
>访问</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
<a <a href="javascript:void(0);" class="ant-dropdown-link">更多</a>
href="javascript:void(0);"
class="ant-dropdown-link"
>更多</a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item <a-menu-item key="1" v-if="!record.isFile">
key="1" <a href="javascript:void(0);" @click="handleOpenCreateDirectoryModal(record)"></a>
v-if="!record.isFile"
>
<a
href="javascript:void(0);"
@click="handleOpenCreateDirectoryModal(record)"
>创建文件夹</a>
</a-menu-item> </a-menu-item>
<a-menu-item key="2"> <a-menu-item key="2">
<a-popconfirm <a-popconfirm
:title="record.isFile?'你确定要删除该文件?':'你确定要删除该文件夹?'" :title="record.isFile ? '你确定要删除该文件?' : '你确定要删除该文件夹?'"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
@confirm="handleDelete(record.relativePath)" @confirm="handleDelete(record.relativePath)"
@ -91,19 +48,10 @@
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item key="3"> <a-menu-item key="3">
<a <a href="javascript:void(0);" @click="handleOpenRenameModal(record)"></a>
href="javascript:void(0);"
@click="handleOpenRenameModal(record)"
>重命名</a>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item key="4" v-if="record.isFile">
key="4" <a href="javascript:void(0);" @click="handleOpenEditContentModal(record)"></a>
v-if="record.isFile"
>
<a
href="javascript:void(0);"
@click="handleOpenEditContentModal(record)"
>编辑</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-dropdown> </a-dropdown>
@ -125,11 +73,7 @@
:filed="list.selected.relativePath" :filed="list.selected.relativePath"
></FilePondUpload> ></FilePondUpload>
</a-modal> </a-modal>
<a-modal <a-modal v-model="directoryForm.visible" :afterClose="onDirectoryFormModalClose" title="创建文件夹">
v-model="directoryForm.visible"
:afterClose="onDirectoryFormModalClose"
title="创建文件夹"
>
<template slot="footer"> <template slot="footer">
<ReactiveButton <ReactiveButton
@click="handleCreateDirectory" @click="handleCreateDirectory"
@ -141,29 +85,13 @@
erroredText="创建失败" erroredText="创建失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form-model <a-form-model ref="directoryForm" :model="directoryForm.model" :rules="directoryForm.rules" layout="vertical">
ref="directoryForm" <a-form-model-item prop="name" label="文件夹名:">
:model="directoryForm.model" <a-input ref="createDirectoryInput" v-model="directoryForm.model.name" @keyup.enter="handleCreateDirectory" />
:rules="directoryForm.rules"
layout="vertical"
>
<a-form-model-item
prop="name"
label="文件夹名:"
>
<a-input
ref="createDirectoryInput"
v-model="directoryForm.model.name"
@keyup.enter="handleCreateDirectory"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
<a-modal <a-modal v-model="renameForm.visible" :afterClose="onRenameModalClose" title="重命名">
v-model="renameForm.visible"
:afterClose="onRenameModalClose"
title="重命名"
>
<template slot="footer"> <template slot="footer">
<ReactiveButton <ReactiveButton
@click="handleRenameDirectoryOrFile" @click="handleRenameDirectoryOrFile"
@ -175,21 +103,9 @@
erroredText="重命名失败" erroredText="重命名失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form-model <a-form-model ref="renameForm" :model="renameForm.model" :rules="renameForm.rules" layout="vertical">
ref="renameForm" <a-form-model-item prop="name" :label="list.selected.isFile ? '文件名:' : '文件夹名:'">
:model="renameForm.model" <a-input ref="renameModalInput" v-model="renameForm.model.name" @keyup.enter="handleRenameDirectoryOrFile" />
:rules="renameForm.rules"
layout="vertical"
>
<a-form-model-item
prop="name"
:label="list.selected.isFile?'文件名:':'文件夹名:'"
>
<a-input
ref="renameModalInput"
v-model="renameForm.model.name"
@keyup.enter="handleRenameDirectoryOrFile"
/>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
@ -243,29 +159,29 @@ const columns = [
{ {
title: '文件名', title: '文件名',
dataIndex: 'name', dataIndex: 'name',
scopedSlots: { customRender: 'name' }, scopedSlots: { customRender: 'name' }
}, },
{ {
title: '文件类型', title: '文件类型',
dataIndex: 'mimeType', dataIndex: 'mimeType',
scopedSlots: { customRender: 'mimeType' }, scopedSlots: { customRender: 'mimeType' }
}, },
{ {
title: '上传时间', title: '上传时间',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'createTime' }, scopedSlots: { customRender: 'createTime' }
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '120px', width: '120px',
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' }
}, }
] ]
export default { export default {
components: { components: {
codemirror, codemirror
}, },
name: 'StaticStorage', name: 'StaticStorage',
data() { data() {
@ -274,41 +190,41 @@ export default {
columns: columns, columns: columns,
data: [], data: [],
loading: false, loading: false,
selected: {}, selected: {}
}, },
uploadModal: { uploadModal: {
visible: false, visible: false,
uploadHandler: staticApi.upload, uploadHandler: staticApi.upload
}, },
directoryForm: { directoryForm: {
model: { model: {
name: null, name: null
}, },
visible: false, visible: false,
saving: false, saving: false,
saveErrored: false, saveErrored: false,
rules: { rules: {
name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }], name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }]
}, }
}, },
renameForm: { renameForm: {
model: { model: {
name: null, name: null
}, },
visible: false, visible: false,
saving: false, saving: false,
saveErrored: false, saveErrored: false,
rules: { rules: {
name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }], name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }]
}, }
}, },
editContentForm: { editContentForm: {
model: { model: {
content: null, content: null
}, },
visible: false, visible: false,
saving: false, saving: false,
@ -318,10 +234,10 @@ export default {
options: { options: {
tabSize: 4, tabSize: 4,
lineNumbers: true, lineNumbers: true,
line: true, line: true
}, }
}, }
}, }
} }
}, },
beforeMount() { beforeMount() {
@ -336,14 +252,14 @@ export default {
return data.sort(function(a, b) { return data.sort(function(a, b) {
return a.isFile - b.isFile return a.isFile - b.isFile
}) })
}, }
}, },
methods: { methods: {
handleListStatics() { handleListStatics() {
this.list.loading = true this.list.loading = true
staticApi staticApi
.list() .list()
.then((response) => { .then(response => {
this.list.data = response.data.data this.list.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -355,7 +271,7 @@ export default {
handleDelete(path) { handleDelete(path) {
staticApi staticApi
.delete(path) .delete(path)
.then((response) => { .then(() => {
this.$message.success(`删除成功!`) this.$message.success(`删除成功!`)
}) })
.finally(() => { .finally(() => {
@ -376,7 +292,7 @@ export default {
}, },
handleCreateDirectory() { handleCreateDirectory() {
const _this = this const _this = this
_this.$refs.directoryForm.validate((valid) => { _this.$refs.directoryForm.validate(valid => {
if (valid) { if (valid) {
this.directoryForm.saving = true this.directoryForm.saving = true
staticApi staticApi
@ -419,7 +335,7 @@ export default {
}, },
handleRenameDirectoryOrFile() { handleRenameDirectoryOrFile() {
const _this = this const _this = this
_this.$refs.renameForm.validate((valid) => { _this.$refs.renameForm.validate(valid => {
if (valid) { if (valid) {
this.renameForm.saving = true this.renameForm.saving = true
staticApi staticApi
@ -449,7 +365,7 @@ export default {
_this.list.selected = file _this.list.selected = file
const arr = file.name.split('.') const arr = file.name.split('.')
const postfix = arr[arr.length - 1] const postfix = arr[arr.length - 1]
staticApi.getContent(_this.options.blog_url + file.relativePath).then((response) => { staticApi.getContent(_this.options.blog_url + file.relativePath).then(response => {
_this.editContentForm.model.content = response.data _this.editContentForm.model.content = response.data
const info = _this.editContentForm.codeMirror.instance.findModeByExtension(postfix) const info = _this.editContentForm.codeMirror.instance.findModeByExtension(postfix)
if (info === undefined) { if (info === undefined) {
@ -503,7 +419,7 @@ export default {
this.editContentForm.visible = false this.editContentForm.visible = false
this.list.selected = {} this.list.selected = {}
this.editContentForm.model.content = '' this.editContentForm.model.content = ''
}, }
}, }
} }
</script> </script>

View File

@ -1,12 +1,6 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="advancedOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="advancedOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item <a-form-model-item
label="全局绝对路径:" label="全局绝对路径:"
help="* 对网站上面的所有页面路径、本地附件路径、以及主题中的静态资源路径有效。" help="* 对网站上面的所有页面路径、本地附件路径、以及主题中的静态资源路径有效。"

View File

@ -1,23 +1,11 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="apiOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="apiOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item label="API 服务:"> <a-form-model-item label="API 服务:">
<a-switch v-model="options.api_enabled" /> <a-switch v-model="options.api_enabled" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="Access key" prop="api_access_key">
label="Access key" <a-input-password v-model="options.api_access_key" autocomplete="new-password" />
prop="api_access_key"
>
<a-input-password
v-model="options.api_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton
@ -77,7 +65,7 @@ export default {
methods: { methods: {
handleSaveOptions() { handleSaveOptions() {
const _this = this const _this = this
_this.$refs.apiOptionsForm.validate((valid) => { _this.$refs.apiOptionsForm.validate(valid => {
if (valid) { if (valid) {
_this.$emit('onSave') _this.$emit('onSave')
} }

View File

@ -11,32 +11,19 @@
<a-switch v-model="options.attachment_upload_image_preview_enable" /> <a-switch v-model="options.attachment_upload_image_preview_enable" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="最大上传文件数:"> <a-form-model-item label="最大上传文件数:">
<a-input-number <a-input-number v-model="options.attachment_upload_max_files" :min="1" style="width:100%" />
v-model="options.attachment_upload_max_files"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="同时上传文件数:"> <a-form-model-item label="同时上传文件数:">
<a-input-number <a-input-number v-model="options.attachment_upload_max_parallel_uploads" :min="1" style="width:100%" />
v-model="options.attachment_upload_max_parallel_uploads"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="存储位置:"> <a-form-model-item label="存储位置:">
<a-select v-model="options.attachment_type"> <a-select v-model="options.attachment_type">
<a-select-option <a-select-option v-for="item in Object.keys(attachmentType)" :key="item" :value="item">{{
v-for="item in Object.keys(attachmentType)" attachmentType[item].text
:key="item" }}</a-select-option>
:value="item"
>{{ attachmentType[item].text }}</a-select-option>
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<div <div id="smmsForm" v-show="options.attachment_type === 'SMMS'">
id="smmsForm"
v-show="options.attachment_type === 'SMMS'"
>
<a-form-model-item label="Secret Token"> <a-form-model-item label="Secret Token">
<a-input-password <a-input-password
v-model="options.smms_api_secret_token" v-model="options.smms_api_secret_token"
@ -45,10 +32,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="upOssForm" v-show="options.attachment_type === 'UPOSS'">
id="upOssForm"
v-show="options.attachment_type === 'UPOSS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.oss_upyun_domain_protocol"> <a-select v-model="options.oss_upyun_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -56,10 +40,7 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.oss_upyun_domain" placeholder="无需再加上 http:// 或者 https://" />
v-model="options.oss_upyun_domain"
placeholder="无需再加上 http:// 或者 https://"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="空间名称:"> <a-form-model-item label="空间名称:">
<a-input v-model="options.oss_upyun_bucket" /> <a-input v-model="options.oss_upyun_bucket" />
@ -68,19 +49,13 @@
<a-input v-model="options.oss_upyun_operator" /> <a-input v-model="options.oss_upyun_operator" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="操作员密码:"> <a-form-model-item label="操作员密码:">
<a-input-password <a-input-password v-model="options.oss_upyun_password" autocomplete="new-password" />
v-model="options.oss_upyun_password"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input v-model="options.oss_upyun_source" /> <a-input v-model="options.oss_upyun_source" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.oss_upyun_style_rule" placeholder="间隔标识符+图片处理版本名称" />
v-model="options.oss_upyun_style_rule"
placeholder="间隔标识符+图片处理版本名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -89,10 +64,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="qiniuOssForm" v-show="options.attachment_type === 'QINIUOSS'">
id="qiniuOssForm"
v-show="options.attachment_type === 'QINIUOSS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.oss_qiniu_domain_protocol"> <a-select v-model="options.oss_qiniu_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -100,47 +72,25 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.oss_qiniu_domain" placeholder="无需再加上 http:// 或者 https://" />
v-model="options.oss_qiniu_domain"
placeholder="无需再加上 http:// 或者 https://"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="区域:"> <a-form-model-item label="区域:">
<a-auto-complete <a-auto-complete :dataSource="qiniuOssZones" v-model="options.oss_qiniu_zone" allowClear />
:dataSource="qiniuOssZones"
v-model="options.oss_qiniu_zone"
allowClear
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Key"> <a-form-model-item label="Access Key">
<a-input-password <a-input-password v-model="options.oss_qiniu_access_key" autocomplete="new-password" />
v-model="options.oss_qiniu_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Secret Key"> <a-form-model-item label="Secret Key">
<a-input-password <a-input-password v-model="options.oss_qiniu_secret_key" autocomplete="new-password" />
v-model="options.oss_qiniu_secret_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input <a-input v-model="options.oss_qiniu_source" placeholder="不填写则上传到根目录" />
v-model="options.oss_qiniu_source"
placeholder="不填写则上传到根目录"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket"> <a-form-model-item label="Bucket">
<a-input <a-input v-model="options.oss_qiniu_bucket" placeholder="存储空间名称" />
v-model="options.oss_qiniu_bucket"
placeholder="存储空间名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.oss_qiniu_style_rule" placeholder="样式分隔符+图片处理样式名称" />
v-model="options.oss_qiniu_style_rule"
placeholder="样式分隔符+图片处理样式名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -149,10 +99,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="aliOssForm" v-show="options.attachment_type === 'ALIOSS'">
id="aliOssForm"
v-show="options.attachment_type === 'ALIOSS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.oss_ali_domain_protocol"> <a-select v-model="options.oss_ali_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -160,43 +107,25 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.oss_ali_domain" placeholder="如不填写,路径根域名将为 Bucket + EndPoint" />
v-model="options.oss_ali_domain"
placeholder="如不填写,路径根域名将为 Bucket + EndPoint"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket"> <a-form-model-item label="Bucket">
<a-input <a-input v-model="options.oss_ali_bucket_name" placeholder="存储空间名称" />
v-model="options.oss_ali_bucket_name"
placeholder="存储空间名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="EndPoint地域节点"> <a-form-model-item label="EndPoint地域节点">
<a-input v-model="options.oss_ali_endpoint" /> <a-input v-model="options.oss_ali_endpoint" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Key"> <a-form-model-item label="Access Key">
<a-input-password <a-input-password v-model="options.oss_ali_access_key" autocomplete="new-password" />
v-model="options.oss_ali_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Secret"> <a-form-model-item label="Access Secret">
<a-input-password <a-input-password v-model="options.oss_ali_access_secret" autocomplete="new-password" />
v-model="options.oss_ali_access_secret"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input <a-input v-model="options.oss_ali_source" placeholder="不填写则上传到根目录" />
v-model="options.oss_ali_source"
placeholder="不填写则上传到根目录"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.oss_ali_style_rule" placeholder="请到阿里云控制台的图片处理获取" />
v-model="options.oss_ali_style_rule"
placeholder="请到阿里云控制台的图片处理获取"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -205,10 +134,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="baiduBosForm" v-show="options.attachment_type === 'BAIDUBOS'">
id="baiduBosForm"
v-show="options.attachment_type === 'BAIDUBOS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.bos_baidu_domain_protocol"> <a-select v-model="options.bos_baidu_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -216,37 +142,22 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.bos_baidu_domain" placeholder="如不填写,路径根域名将为 Bucket + EndPoint" />
v-model="options.bos_baidu_domain"
placeholder="如不填写,路径根域名将为 Bucket + EndPoint"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket"> <a-form-model-item label="Bucket">
<a-input <a-input v-model="options.bos_baidu_bucket_name" placeholder="存储空间名称" />
v-model="options.bos_baidu_bucket_name"
placeholder="存储空间名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="EndPoint地域节点"> <a-form-model-item label="EndPoint地域节点">
<a-input v-model="options.bos_baidu_endpoint" /> <a-input v-model="options.bos_baidu_endpoint" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Key"> <a-form-model-item label="Access Key">
<a-input-password <a-input-password v-model="options.bos_baidu_access_key" autocomplete="new-password" />
v-model="options.bos_baidu_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Secret Key"> <a-form-model-item label="Secret Key">
<a-input-password <a-input-password v-model="options.bos_baidu_secret_key" autocomplete="new-password" />
v-model="options.bos_baidu_secret_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.bos_baidu_style_rule" placeholder="请到百度云控制台的图片处理获取" />
v-model="options.bos_baidu_style_rule"
placeholder="请到百度云控制台的图片处理获取"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -255,10 +166,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="tencentCosForm" v-show="options.attachment_type === 'TENCENTCOS'">
id="tencentCosForm"
v-show="options.attachment_type === 'TENCENTCOS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.cos_tencent_domain_protocol"> <a-select v-model="options.cos_tencent_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -266,47 +174,25 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.cos_tencent_domain" placeholder="如不填写,路径根域名将为 Bucket + 区域地址" />
v-model="options.cos_tencent_domain"
placeholder="如不填写,路径根域名将为 Bucket + 区域地址"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket"> <a-form-model-item label="Bucket">
<a-input <a-input v-model="options.cos_tencent_bucket_name" placeholder="存储桶名称" />
v-model="options.cos_tencent_bucket_name"
placeholder="存储桶名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="区域:"> <a-form-model-item label="区域:">
<a-auto-complete <a-auto-complete :dataSource="tencentCosRegions" v-model="options.cos_tencent_region" allowClear />
:dataSource="tencentCosRegions"
v-model="options.cos_tencent_region"
allowClear
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Secret Id"> <a-form-model-item label="Secret Id">
<a-input-password <a-input-password v-model="options.cos_tencent_secret_id" autocomplete="new-password" />
v-model="options.cos_tencent_secret_id"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Secret Key"> <a-form-model-item label="Secret Key">
<a-input-password <a-input-password v-model="options.cos_tencent_secret_key" autocomplete="new-password" />
v-model="options.cos_tencent_secret_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input <a-input v-model="options.cos_tencent_source" placeholder="不填写则上传到根目录" />
v-model="options.cos_tencent_source"
placeholder="不填写则上传到根目录"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.cos_tencent_style_rule" placeholder="请到腾讯云控制台的图片处理获取" />
v-model="options.cos_tencent_style_rule"
placeholder="请到腾讯云控制台的图片处理获取"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -315,10 +201,7 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="huaweiObsForm" v-show="options.attachment_type === 'HUAWEIOBS'">
id="huaweiObsForm"
v-show="options.attachment_type === 'HUAWEIOBS'"
>
<a-form-model-item label="绑定域名协议:"> <a-form-model-item label="绑定域名协议:">
<a-select v-model="options.obs_huawei_domain_protocol"> <a-select v-model="options.obs_huawei_domain_protocol">
<a-select-option value="https://">HTTPS</a-select-option> <a-select-option value="https://">HTTPS</a-select-option>
@ -326,46 +209,25 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="绑定域名:"> <a-form-model-item label="绑定域名:">
<a-input <a-input v-model="options.obs_huawei_domain" placeholder="如不填写,路径根域名将为 Bucket + EndPoint" />
v-model="options.obs_huawei_domain"
placeholder="如不填写,路径根域名将为 Bucket + EndPoint"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket桶名称"> <a-form-model-item label="Bucket桶名称">
<a-input <a-input v-model="options.obs_huawei_bucket_name" placeholder="桶名称" />
v-model="options.obs_huawei_bucket_name"
placeholder="桶名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="EndPoint终端节点"> <a-form-model-item label="EndPoint终端节点">
<a-input <a-input v-model="options.obs_huawei_endpoint" placeholder="Endpoint" />
v-model="options.obs_huawei_endpoint"
placeholder="Endpoint"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Key"> <a-form-model-item label="Access Key">
<a-input-password <a-input-password v-model="options.obs_huawei_access_key" autocomplete="new-password" />
v-model="options.obs_huawei_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Secret"> <a-form-model-item label="Access Secret">
<a-input-password <a-input-password v-model="options.obs_huawei_access_secret" autocomplete="new-password" />
v-model="options.obs_huawei_access_secret"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input <a-input v-model="options.obs_huawei_source" placeholder="不填写则上传到根目录" />
v-model="options.obs_huawei_source"
placeholder="不填写则上传到根目录"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="图片处理策略:"> <a-form-model-item label="图片处理策略:">
<a-input <a-input v-model="options.obs_huawei_style_rule" placeholder="请到华为云控制台的图片处理创建" />
v-model="options.obs_huawei_style_rule"
placeholder="请到华为云控制台的图片处理创建"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="缩略图处理策略:"> <a-form-model-item label="缩略图处理策略:">
<a-input <a-input
@ -374,39 +236,21 @@
/> />
</a-form-model-item> </a-form-model-item>
</div> </div>
<div <div id="minioForm" v-show="options.attachment_type === 'MINIO'">
id="minioForm"
v-show="options.attachment_type === 'MINIO'"
>
<a-form-model-item label="EndPoint终端节点"> <a-form-model-item label="EndPoint终端节点">
<a-input <a-input v-model="options.minio_endpoint" placeholder="Endpoint" />
v-model="options.minio_endpoint"
placeholder="Endpoint"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Bucket桶名称"> <a-form-model-item label="Bucket桶名称">
<a-input <a-input v-model="options.minio_bucket_name" placeholder="桶名称" />
v-model="options.minio_bucket_name"
placeholder="桶名称"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Key"> <a-form-model-item label="Access Key">
<a-input-password <a-input-password v-model="options.minio_access_key" autocomplete="new-password" />
v-model="options.minio_access_key"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="Access Secret"> <a-form-model-item label="Access Secret">
<a-input-password <a-input-password v-model="options.minio_access_secret" autocomplete="new-password" />
v-model="options.minio_access_secret"
autocomplete="new-password"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文件目录:"> <a-form-model-item label="文件目录:">
<a-input <a-input v-model="options.minio_source" placeholder="不填写则上传到根目录" />
v-model="options.minio_source"
placeholder="不填写则上传到根目录"
/>
</a-form-model-item> </a-form-model-item>
</div> </div>
<a-form-model-item> <a-form-model-item>

View File

@ -1,12 +1,6 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="commentOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="commentOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item label="评论者头像:"> <a-form-model-item label="评论者头像:">
<a-select v-model="options.comment_gravatar_default"> <a-select v-model="options.comment_gravatar_default">
<a-select-option value="mm">默认</a-select-option> <a-select-option value="mm">默认</a-select-option>
@ -27,10 +21,7 @@
<a-form-model-item label="评论回复通知对方:"> <a-form-model-item label="评论回复通知对方:">
<a-switch v-model="options.comment_reply_notice" /> <a-switch v-model="options.comment_reply_notice" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="API 评论开关:" help="* 关闭之后将无法进行评论">
label="API 评论开关:"
help="* 关闭之后将无法进行评论"
>
<a-switch v-model="options.comment_api_enabled" /> <a-switch v-model="options.comment_api_enabled" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="评论模块 JS"> <a-form-model-item label="评论模块 JS">
@ -42,11 +33,7 @@
/> />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="每页显示条数: "> <a-form-model-item label="每页显示条数: ">
<a-input-number <a-input-number v-model="options.comment_page_size" :min="1" style="width:100%" />
v-model="options.comment_page_size"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="占位提示:"> <a-form-model-item label="占位提示:">
<a-input v-model="options.comment_content_placeholder" /> <a-input v-model="options.comment_content_placeholder" />

View File

@ -1,59 +1,27 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="generalOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="generalOptionsForm" <a-form-model-item label="博客标题:" prop="blog_title">
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item
label="博客标题:"
prop="blog_title"
>
<a-input v-model="options.blog_title" /> <a-input v-model="options.blog_title" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="博客地址:" prop="blog_url">
label="博客地址:" <a-input v-model="options.blog_url" placeholder="如https://halo.run" />
prop="blog_url"
>
<a-input
v-model="options.blog_url"
placeholder="如https://halo.run"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="Logo" prop="blog_logo">
label="Logo"
prop="blog_logo"
>
<a-input v-model="options.blog_logo"> <a-input v-model="options.blog_logo">
<a <a href="javascript:void(0);" slot="addonAfter" @click="handleShowLogoSelector">
href="javascript:void(0);"
slot="addonAfter"
@click="handleShowLogoSelector"
>
<a-icon type="picture" /> <a-icon type="picture" />
</a> </a>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="Favicon" prop="blog_favicon">
label="Favicon"
prop="blog_favicon"
>
<a-input v-model="options.blog_favicon"> <a-input v-model="options.blog_favicon">
<a <a href="javascript:void(0);" slot="addonAfter" @click="handleShowFaviconSelector">
href="javascript:void(0);"
slot="addonAfter"
@click="handleShowFaviconSelector"
>
<a-icon type="picture" /> <a-icon type="picture" />
</a> </a>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="页脚信息:" prop="blog_footer_info">
label="页脚信息:"
prop="blog_footer_info"
>
<a-input <a-input
type="textarea" type="textarea"
:autoSize="{ minRows: 5 }" :autoSize="{ minRows: 5 }"

View File

@ -1,12 +1,6 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="otherOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="otherOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item label="自定义全局 head"> <a-form-model-item label="自定义全局 head">
<a-input <a-input
type="textarea" type="textarea"

View File

@ -1,27 +1,29 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="permalinkOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="permalinkOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item label="文章固定链接类型:"> <a-form-model-item label="文章固定链接类型:">
<template slot="help"> <template slot="help">
<span v-if="options.post_permalink_type === 'DEFAULT'">{{ options.blog_url }}/{{ options.archives_prefix }}/{slug}{{ options.path_suffix }}</span> <span v-if="options.post_permalink_type === 'DEFAULT'"
<span v-else-if="options.post_permalink_type === 'YEAR'">{{ options.blog_url }}{{ new Date() | moment_post_year }}{slug}{{ options.path_suffix }}</span> >{{ options.blog_url }}/{{ options.archives_prefix }}/{slug}{{ options.path_suffix }}</span
<span v-else-if="options.post_permalink_type === 'DATE'">{{ options.blog_url }}{{ new Date() | moment_post_date }}{slug}{{ options.path_suffix }}</span> >
<span v-else-if="options.post_permalink_type === 'DAY'">{{ options.blog_url }}{{ new Date() | moment_post_day }}{slug}{{ options.path_suffix }}</span> <span v-else-if="options.post_permalink_type === 'YEAR'"
>{{ options.blog_url }}{{ new Date() | moment_post_year }}{slug}{{ options.path_suffix }}</span
>
<span v-else-if="options.post_permalink_type === 'DATE'"
>{{ options.blog_url }}{{ new Date() | moment_post_date }}{slug}{{ options.path_suffix }}</span
>
<span v-else-if="options.post_permalink_type === 'DAY'"
>{{ options.blog_url }}{{ new Date() | moment_post_day }}{slug}{{ options.path_suffix }}</span
>
<span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p={id}</span> <span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p={id}</span>
<span v-else-if="options.post_permalink_type === 'ID_SLUG'">{{ options.blog_url }}/{{ options.archives_prefix }}/{id}{{ options.path_suffix }}</span> <span v-else-if="options.post_permalink_type === 'ID_SLUG'"
>{{ options.blog_url }}/{{ options.archives_prefix }}/{id}{{ options.path_suffix }}</span
>
</template> </template>
<a-select v-model="options.post_permalink_type"> <a-select v-model="options.post_permalink_type">
<a-select-option <a-select-option v-for="item in Object.keys(postPermalinkType)" :key="item" :value="item">{{
v-for="item in Object.keys(postPermalinkType)" postPermalinkType[item].text
:key="item" }}</a-select-option>
:value="item"
>{{ postPermalinkType[item].text }}</a-select-option>
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="归档前缀:"> <a-form-model-item label="归档前缀:">
@ -44,21 +46,20 @@
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="自定义页面固定链接类型:"> <a-form-model-item label="自定义页面固定链接类型:">
<template slot="help"> <template slot="help">
<span v-if="options.sheet_permalink_type === 'SECONDARY'">{{ options.blog_url }}/{{ options.sheet_prefix }}/{slug}{{ options.path_suffix }}</span> <span v-if="options.sheet_permalink_type === 'SECONDARY'"
<span v-else-if="options.sheet_permalink_type === 'ROOT'">{{ options.blog_url }}/{slug}{{ options.path_suffix }}</span> >{{ options.blog_url }}/{{ options.sheet_prefix }}/{slug}{{ options.path_suffix }}</span
>
<span v-else-if="options.sheet_permalink_type === 'ROOT'"
>{{ options.blog_url }}/{slug}{{ options.path_suffix }}</span
>
</template> </template>
<a-select v-model="options.sheet_permalink_type"> <a-select v-model="options.sheet_permalink_type">
<a-select-option <a-select-option v-for="item in Object.keys(sheetPermalinkType)" :key="item" :value="item">{{
v-for="item in Object.keys(sheetPermalinkType)" sheetPermalinkType[item].text
:key="item" }}</a-select-option>
:value="item"
>{{ sheetPermalinkType[item].text }}</a-select-option>
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="自定义页面前缀:" v-show="options.sheet_permalink_type === 'SECONDARY'">
label="自定义页面前缀:"
v-show="options.sheet_permalink_type === 'SECONDARY'"
>
<template slot="help"> <template slot="help">
<span>{{ options.blog_url }}/{{ options.sheet_prefix }}/{slug}{{ options.path_suffix }}</span> <span>{{ options.blog_url }}/{{ options.sheet_prefix }}/{slug}{{ options.path_suffix }}</span>
</template> </template>
@ -111,16 +112,16 @@ export default {
props: { props: {
options: { options: {
type: Object, type: Object,
required: true, required: true
}, },
saving: { saving: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
errored: { errored: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
data() { data() {
return { return {
@ -130,25 +131,25 @@ export default {
xl: { span: 8 }, xl: { span: 8 },
lg: { span: 8 }, lg: { span: 8 },
sm: { span: 12 }, sm: { span: 12 },
xs: { span: 24 }, xs: { span: 24 }
}, },
rules: {}, rules: {}
} }
}, },
watch: { watch: {
options(val) { options(val) {
this.$emit('onChange', val) this.$emit('onChange', val)
}, }
}, },
methods: { methods: {
handleSaveOptions() { handleSaveOptions() {
const _this = this const _this = this
_this.$refs.permalinkOptionsForm.validate((valid) => { _this.$refs.permalinkOptionsForm.validate(valid => {
if (valid) { if (valid) {
this.$emit('onSave') this.$emit('onSave')
} }
}) })
}, }
}, }
} }
</script> </script>

View File

@ -1,12 +1,6 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="postOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="postOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<!-- <a-form-model-item label="默认编辑器:"> <!-- <a-form-model-item label="默认编辑器:">
<a-select v-model="options.default_editor"> <a-select v-model="options.default_editor">
<a-select-option value="MARKDOWN">Markdown 编辑器</a-select-option> <a-select-option value="MARKDOWN">Markdown 编辑器</a-select-option>
@ -21,18 +15,10 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="首页每页文章条数:"> <a-form-model-item label="首页每页文章条数:">
<a-input-number <a-input-number v-model="options.post_index_page_size" :min="1" style="width:100%" />
v-model="options.post_index_page_size"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="归档页面每页文章条数:" help="* 包括标签所属文章页面、分类所属文章页面"> <a-form-model-item label="归档页面每页文章条数:" help="* 包括标签所属文章页面、分类所属文章页面">
<a-input-number <a-input-number v-model="options.post_archives_page_size" :min="1" style="width:100%" />
v-model="options.post_archives_page_size"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="RSS 内容类型:"> <a-form-model-item label="RSS 内容类型:">
<a-select v-model="options.rss_content_type"> <a-select v-model="options.rss_content_type">
@ -41,33 +27,18 @@
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="RSS 内容条数:"> <a-form-model-item label="RSS 内容条数:">
<a-input-number <a-input-number v-model="options.rss_page_size" :min="1" style="width:100%" />
v-model="options.rss_page_size"
:min="1"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="文章摘要字数:"> <a-form-model-item label="文章摘要字数:">
<a-input-number <a-input-number v-model="options.post_summary_length" style="width:100%" />
v-model="options.post_summary_length"
style="width:100%"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="自动清理回收站文章:"> <a-form-model-item label="自动清理回收站文章:">
<a-switch v-model="options.recycled_post_cleaning_enabled" /> <a-switch v-model="options.recycled_post_cleaning_enabled" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item v-show="options.recycled_post_cleaning_enabled" label="回收站文章保留时长:">
v-show="options.recycled_post_cleaning_enabled"
label="回收站文章保留时长:">
<a-input-group compact> <a-input-group compact>
<a-input-number <a-input-number v-model="options.recycled_post_retention_time" :min="1" :precision="0" style="width: 70%" />
v-model="options.recycled_post_retention_time" <a-select v-model="options.recycled_post_retention_timeunit" style="width: 30%">
:min="1"
:precision="0"
style="width: 70%"/>
<a-select
v-model="options.recycled_post_retention_timeunit"
style="width: 30%">
<a-select-option value="DAY"></a-select-option> <a-select-option value="DAY"></a-select-option>
<a-select-option value="HOUR">小时</a-select-option> <a-select-option value="HOUR">小时</a-select-option>
</a-select> </a-select>

View File

@ -1,36 +1,14 @@
<template> <template>
<div> <div>
<a-form-model <a-form-model ref="seoOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
ref="seoOptionsForm" <a-form-model-item label="屏蔽搜索引擎:" prop="seo_spider_disabled">
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item
label="屏蔽搜索引擎:"
prop="seo_spider_disabled"
>
<a-switch v-model="options.seo_spider_disabled" /> <a-switch v-model="options.seo_spider_disabled" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="关键词:" prop="seo_keywords">
label="关键词:" <a-input v-model="options.seo_keywords" placeholder="多个关键词以英文状态下的逗号隔开" />
prop="seo_keywords"
>
<a-input
v-model="options.seo_keywords"
placeholder="多个关键词以英文状态下的逗号隔开"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="博客描述:" prop="seo_description">
label="博客描述:" <a-input type="textarea" :autoSize="{ minRows: 5 }" v-model="options.seo_description" />
prop="seo_description"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="options.seo_description"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton

View File

@ -1,58 +1,31 @@
<template> <template>
<div class="custom-tab-wrapper"> <div class="custom-tab-wrapper">
<a-tabs> <a-tabs>
<a-tab-pane <a-tab-pane tab="发信设置" key="smtpoptions">
tab="发信设置" <a-form-model ref="smtpOptionsForm" :model="options" :rules="rules" layout="vertical" :wrapperCol="wrapperCol">
key="smtpoptions"
>
<a-form-model
ref="smtpOptionsForm"
:model="options"
:rules="rules"
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-model-item label="是否启用:"> <a-form-model-item label="是否启用:">
<a-switch v-model="options.email_enabled" /> <a-switch v-model="options.email_enabled" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="SMTP 地址:" prop="email_host">
label="SMTP 地址:"
prop="email_host"
>
<a-input v-model="options.email_host" /> <a-input v-model="options.email_host" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="发送协议:" prop="email_protocol">
label="发送协议:"
prop="email_protocol"
>
<a-input v-model="options.email_protocol" /> <a-input v-model="options.email_protocol" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="SSL 端口:" prop="email_ssl_port">
label="SSL 端口:"
prop="email_ssl_port"
>
<a-input v-model="options.email_ssl_port" /> <a-input v-model="options.email_ssl_port" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="邮箱账号:" prop="email_username">
label="邮箱账号:"
prop="email_username"
>
<a-input v-model="options.email_username" /> <a-input v-model="options.email_username" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="邮箱密码:" prop="email_password">
label="邮箱密码:"
prop="email_password"
>
<a-input-password <a-input-password
v-model="options.email_password" v-model="options.email_password"
placeholder="部分邮箱可能是授权码" placeholder="部分邮箱可能是授权码"
autocomplete="new-password" autocomplete="new-password"
/> />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="发件人:" prop="email_from_name">
label="发件人:"
prop="email_from_name"
>
<a-input v-model="options.email_from_name" /> <a-input v-model="options.email_from_name" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
@ -69,10 +42,7 @@
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane tab="发送测试" key="smtptest">
tab="发送测试"
key="smtptest"
>
<a-form-model <a-form-model
ref="smtpTestForm" ref="smtpTestForm"
:model="mailParam" :model="mailParam"
@ -80,33 +50,20 @@
layout="vertical" layout="vertical"
:wrapperCol="wrapperCol" :wrapperCol="wrapperCol"
> >
<a-form-model-item <a-form-model-item label="收件人地址:" prop="to">
label="收件人地址:"
prop="to"
>
<a-input v-model="mailParam.to" /> <a-input v-model="mailParam.to" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="主题:" prop="subject">
label="主题:"
prop="subject"
>
<a-input v-model="mailParam.subject" /> <a-input v-model="mailParam.subject" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item label="内容:" prop="content">
label="内容:" <a-input type="textarea" :autoSize="{ minRows: 5 }" v-model="mailParam.content" />
prop="content"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="mailParam.content"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item> <a-form-model-item>
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@click="handleTestMailClick" @click="handleTestMailClick"
@callback="sendErrored=false" @callback="sendErrored = false"
:loading="sending" :loading="sending"
:errored="sendErrored" :errored="sendErrored"
text="发送" text="发送"
@ -179,7 +136,7 @@ export default {
methods: { methods: {
handleSaveOptions() { handleSaveOptions() {
const _this = this const _this = this
_this.$refs.smtpOptionsForm.validate((valid) => { _this.$refs.smtpOptionsForm.validate(valid => {
if (valid) { if (valid) {
_this.$emit('onSave') _this.$emit('onSave')
} }
@ -187,12 +144,12 @@ export default {
}, },
handleTestMailClick() { handleTestMailClick() {
const _this = this const _this = this
_this.$refs.smtpTestForm.validate((valid) => { _this.$refs.smtpTestForm.validate(valid => {
if (valid) { if (valid) {
this.sending = true this.sending = true
mailApi mailApi
.testMail(this.mailParam) .testMail(this.mailParam)
.then((response) => { .then(response => {
this.$message.info(response.data.message) this.$message.info(response.data.message)
}) })
.catch(() => { .catch(() => {

Some files were not shown because too many files have changed in this diff Show More