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

View File

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

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"
},
"devDependencies": {
"@babel/polyfill": "^7.12.1",
"@vue/cli-plugin-babel": "^3.8.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "^4.5.4",
"@vue/cli-plugin-unit-jest": "^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",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.3",
"babel-plugin-import": "^1.13.3",
"eslint": "^6.8.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-html": "^6.1.1",
"eslint-plugin-vue": "^6.2.2",
"generate-asset-webpack-plugin": "^0.3.0",
"husky": "^4.3.8",
"prettier": "^1.19.1",
"less": "^3.13.1",
"less-loader": "^5.0.0",
"lint-staged": "^10.5.4",
"tailwindcss": "^1.8.8",
"vue-svg-component-runtime": "^1.0.1",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.6.12"
},
"eslintConfig": {
"root": true,
"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"
}
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"*.{js,jsx,vue}": [
"vue-cli-service lint",
"git add"
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ export default {
},
render() {
const { tooltip, length } = this.$props
const str = this.$slots.default.map((vNode) => vNode.text).join('')
const str = this.$slots.default.map(vNode => vNode.text).join('')
const fullLength = getStrFullLength(str)
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
return strDom

View File

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

View File

@ -1,54 +1,30 @@
<template>
<transition name="showHeader">
<div
v-if="visible"
class="header-animat"
>
<div v-if="visible" class="header-animat">
<a-layout-header
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"
>
<div
v-if="mode === 'sidemenu'"
class="header"
>
<div v-if="mode === 'sidemenu'" class="header">
<a-icon
v-if="device==='mobile'"
v-if="device === 'mobile'"
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click="toggle"
/>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="toggle"
/>
<a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle" />
<user-menu></user-menu>
</div>
<div
v-else
:class="['top-nav-header-index', theme]"
>
<div v-else :class="['top-nav-header-index', theme]">
<div class="header-index-wide">
<div class="header-index-left">
<logo
class="top-nav-header"
v-if="device !== 'mobile'"
/>
<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"
/>
<logo class="top-nav-header" v-if="device !== 'mobile'" />
<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>
<user-menu class="header-index-right"></user-menu>
</div>

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<template>
<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"
:collapsible="collapsible"
v-model="collapsed"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,68 +1,40 @@
<template>
<a-list
itemLayout="horizontal"
:dataSource="formmatedCommentData"
:loading="loading"
>
<a-list-item
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'"
<a-list itemLayout="horizontal" :dataSource="formmatedCommentData" :loading="loading">
<a-list-item 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"
target="_blank"
>{{ item.post.title }}</a><a
v-else-if="item.post.status=='DRAFT'"
>{{ item.post.title }}</a
><a
v-else-if="item.post.status == 'DRAFT'"
href="javascript:void(0)"
@click="handlePostPreview(item.post.id)"
>{{ item.post.title }}</a><a
v-else
href="javascript:void(0)"
>{{ item.post.title }}</a>
>{{ item.post.title }}</a
><a v-else href="javascript:void(0)">{{ item.post.title }}</a>
</template>
<template
slot="author"
v-else-if="type==='sheets'"
>
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a> 发表在 <a
v-if="item.sheet.status=='PUBLISHED'"
<template slot="author" v-else-if="type === 'sheets'">
<a :href="item.authorUrl" target="_blank">{{ item.author }}</a> 发表在 <a
v-if="item.sheet.status == 'PUBLISHED'"
:href="item.sheet.fullPath"
target="_blank"
>{{ item.sheet.title }}</a><a
v-else-if="item.sheet.status=='DRAFT'"
>{{ item.sheet.title }}</a
><a
v-else-if="item.sheet.status == 'DRAFT'"
href="javascript:void(0)"
@click="handleSheetPreview(item.sheet.id)"
>{{ item.sheet.title }}</a><a
v-else
href="javascript:void(0)"
>{{ item.sheet.title }}</a>
>{{ item.sheet.title }}</a
><a v-else href="javascript:void(0)">{{ item.sheet.title }}</a
>
</template>
<!-- <template slot="actions">
<span>回复</span>
</template> -->
<p
class="comment-content-wrapper"
slot="content"
v-html="item.content"
></p>
<a-tooltip
slot="datetime"
:title="item.createTime | moment"
>
<p class="comment-content-wrapper" slot="content" v-html="item.content"></p>
<a-tooltip slot="datetime" :title="item.createTime | moment">
<span>{{ item.createTime | timeAgo }}</span>
</a-tooltip>
</a-comment>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,95 +1,42 @@
<template>
<page-view>
<a-row :gutter="12">
<a-col
v-if="options.developer_mode"
:xl="6"
: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>
<a-col v-if="options.developer_mode" :xl="6" :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>
<a-button
type="primary"
class="float-right"
@click="handleToDeveloperOptions()"
>进入</a-button>
<a-button type="primary" class="float-right" @click="handleToDeveloperOptions()"></a-button>
</a-card>
</a-col>
<a-col
:xl="6"
:lg="6"
:md="12"
:sm="24"
:xs="24"
class="mb-3"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div slot="title">
<a-icon type="hdd" /> 博客备份
</div>
<a-col :xl="6" :lg="6" :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>
<a-dropdown class="float-right">
<a-menu slot="overlay">
<a-menu-item
key="1"
@click="backupWorkDirDrawerVisible = true"
>
<a-menu-item key="1" @click="backupWorkDirDrawerVisible = true">
整站备份
</a-menu-item>
<a-menu-item
key="2"
@click="exportDataDrawerVisible = true"
>
<a-menu-item key="2" @click="exportDataDrawerVisible = true">
数据导出
</a-menu-item>
<a-menu-item
key="3"
@click="exportMarkdownDrawerVisible = true"
>
<a-menu-item key="3" @click="exportMarkdownDrawerVisible = true">
导出文章为 Markdown 文档
</a-menu-item>
</a-menu>
<a-button class="ml-2"> 备份
<a-button class="ml-2">
备份
<a-icon type="down" />
</a-button>
</a-dropdown>
</a-card>
</a-col>
<a-col
:xl="6"
:lg="6"
: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>
<a-col :xl="6" :lg="6" :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>
<a-button
type="primary"
class="float-right"
@click="markdownUpload = true"
>导入</a-button>
<a-button type="primary" class="float-right" @click="markdownUpload = true">导入</a-button>
</a-card>
</a-col>
</a-row>
@ -131,11 +78,11 @@ export default {
exportDataDrawerVisible: false,
exportMarkdownDrawerVisible: false,
markdownUpload: false,
uploadHandler: backupApi.importMarkdown,
uploadHandler: backupApi.importMarkdown
}
},
computed: {
...mapGetters(['options']),
...mapGetters(['options'])
},
methods: {
handleChange(info) {
@ -154,7 +101,7 @@ export default {
},
onUploadClose() {
this.$refs.upload.handleClearFileList()
},
},
}
}
}
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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