mirror of https://github.com/halo-dev/halo
refactor: theme setting page (halo-dev/console#380)
* refactor: theme setting page Signed-off-by: Ryan Wang <i@ryanc.cc> * feat: add theme descriptions Signed-off-by: Ryan Wang <i@ryanc.cc> * refactor: local upgrade component Signed-off-by: Ryan Wang <i@ryanc.cc> * refactor: theme descriptions Signed-off-by: Ryan Wang <i@ryanc.cc> * chore: bump admin-api depspull/3445/head
parent
a57442284e
commit
b1cf9ee421
|
@ -24,7 +24,7 @@
|
|||
"@codemirror/basic-setup": "^0.19.0",
|
||||
"@codemirror/lang-html": "^0.19.3",
|
||||
"@codemirror/lang-java": "^0.19.1",
|
||||
"@halo-dev/admin-api": "^1.0.0-alpha.44",
|
||||
"@halo-dev/admin-api": "^1.0.0-alpha.46",
|
||||
"ant-design-vue": "^1.7.8",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.10.7",
|
||||
|
|
|
@ -5,7 +5,7 @@ specifiers:
|
|||
'@codemirror/basic-setup': ^0.19.0
|
||||
'@codemirror/lang-html': ^0.19.3
|
||||
'@codemirror/lang-java': ^0.19.1
|
||||
'@halo-dev/admin-api': ^1.0.0-alpha.44
|
||||
'@halo-dev/admin-api': ^1.0.0-alpha.46
|
||||
'@vue/cli-plugin-babel': ^3.12.1
|
||||
'@vue/cli-plugin-eslint': ^4.5.15
|
||||
'@vue/cli-plugin-unit-jest': ^4.5.15
|
||||
|
@ -54,7 +54,7 @@ dependencies:
|
|||
'@codemirror/basic-setup': 0.19.0
|
||||
'@codemirror/lang-html': 0.19.3
|
||||
'@codemirror/lang-java': 0.19.1
|
||||
'@halo-dev/admin-api': 1.0.0-alpha.44
|
||||
'@halo-dev/admin-api': 1.0.0-alpha.46
|
||||
ant-design-vue: 1.7.8_9065e7474e033a8e4b95615fc8e6c36c
|
||||
crypto-js: 4.1.1
|
||||
dayjs: 1.10.7
|
||||
|
@ -1348,32 +1348,33 @@ packages:
|
|||
purgecss: 2.3.0
|
||||
dev: true
|
||||
|
||||
/@halo-dev/admin-api/1.0.0-alpha.44:
|
||||
resolution: {integrity: sha512-nCJsx4gDxCjkoGJKsBpcgLhAUc8RYrqKMGM1VSi9t64xTFdXDjIJgtcIQuBgJ0b4R58JIp6A6ti5S764vz4BDA==}
|
||||
/@halo-dev/admin-api/1.0.0-alpha.46:
|
||||
resolution: {integrity: sha512-OlDZm2/L/9IKlhCjWWFO1KkqxfEip/nMZrqavMQXghcHomWBqxvMttB4P1vg+0xmoZh65uYhl3De6VSIbL2FsQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@halo-dev/rest-api-client': 1.0.0-alpha.44
|
||||
'@halo-dev/rest-api-client': 1.0.0-alpha.46
|
||||
tslib: 2.3.1
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/@halo-dev/logger/1.0.0-alpha.44:
|
||||
resolution: {integrity: sha512-ORHP6pj8wLb+mwsk+pYqvH9tqNWTr+96AiZgtSMcdwojA1KupFSVzHN0aqpk8HeGfSrBNbMz4VT6ey+OVnWrcQ==}
|
||||
/@halo-dev/logger/1.0.0-alpha.46:
|
||||
resolution: {integrity: sha512-iBLa55wq6i++9l2L4w1X0vY5mMmqOFBbzjvTNv9TcG2ssUNXL1s0uRgasjvlQAI5EDfvmHhth/RW4ACPM+EUnQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
|
||||
/@halo-dev/rest-api-client/1.0.0-alpha.44:
|
||||
resolution: {integrity: sha512-fCzh7ihLpI7hrq0S2M9YjROTvoTT5JeEOqv5O/hon4bTfpBqEb7REjjYMONR8lJNgmEI9wzviEhn7Xxg7nX/vA==}
|
||||
/@halo-dev/rest-api-client/1.0.0-alpha.46:
|
||||
resolution: {integrity: sha512-kv88fszMZe26KAVfZXzjIvqZV6wsMWCxMtoWgk9F8DkYs3kQt9n+98g5azmtFgZh2RYh3O4einjcFOUQ44yRxg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@halo-dev/logger': 1.0.0-alpha.44
|
||||
'@halo-dev/logger': 1.0.0-alpha.46
|
||||
axios: 0.24.0
|
||||
form-data: 4.0.0
|
||||
js-base64: 3.7.2
|
||||
qs: 6.10.1
|
||||
store: 2.0.12
|
||||
qs: 6.10.3
|
||||
tslib: 2.3.1
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
@ -9403,8 +9404,8 @@ packages:
|
|||
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
||||
dev: true
|
||||
|
||||
/qs/6.10.1:
|
||||
resolution: {integrity: sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==}
|
||||
/qs/6.10.3:
|
||||
resolution: {integrity: sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==}
|
||||
engines: {node: '>=0.6'}
|
||||
dependencies:
|
||||
side-channel: 1.0.4
|
||||
|
@ -10351,10 +10352,6 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/store/2.0.12:
|
||||
resolution: {integrity: sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=}
|
||||
dev: false
|
||||
|
||||
/stream-browserify/2.0.2:
|
||||
resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
|
||||
dependencies:
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-input :defaultValue="defaultValue" :placeholder="placeholder" :value="value" @change="onInputChange">
|
||||
<a slot="addonAfter" href="javascript:void(0);" @click="attachmentDrawerVisible = true">
|
||||
<a-icon type="picture" />
|
||||
</a>
|
||||
</a-input>
|
||||
<AttachmentSelectDrawer
|
||||
v-model="attachmentDrawerVisible"
|
||||
title="选择附件"
|
||||
@listenToSelect="handleSelectAttachment"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AttachmentInput',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
defaultValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
attachmentDrawerVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onInputChange(e) {
|
||||
this.$emit('input', e.target.value)
|
||||
},
|
||||
handleSelectAttachment(data) {
|
||||
this.$emit('input', encodeURI(data.path))
|
||||
this.attachmentDrawerVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -7,6 +7,7 @@ import AttachmentSelectDrawer from './Attachment/AttachmentSelectDrawer'
|
|||
import AttachmentUploadModal from './Attachment/AttachmentUploadModal'
|
||||
import ReactiveButton from './Button/ReactiveButton'
|
||||
import PostTag from './Post/PostTag'
|
||||
import AttachmentInput from './Input/AttachmentInput'
|
||||
|
||||
const _components = {
|
||||
Ellipsis,
|
||||
|
@ -15,7 +16,8 @@ const _components = {
|
|||
AttachmentSelectDrawer,
|
||||
AttachmentUploadModal,
|
||||
ReactiveButton,
|
||||
PostTag
|
||||
PostTag,
|
||||
AttachmentInput
|
||||
}
|
||||
|
||||
const components = {}
|
||||
|
|
|
@ -141,16 +141,22 @@ export const asyncRouterMap = [
|
|||
meta: { title: '主题', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/interface/menus',
|
||||
name: 'MenuList',
|
||||
component: () => import('@/views/interface/MenuList'),
|
||||
meta: { title: '菜单', hiddenHeaderContent: false }
|
||||
path: '/interface/themes/setting',
|
||||
name: 'ThemeSetting',
|
||||
component: () => import('@/views/interface/ThemeSetting'),
|
||||
meta: { title: '主题设置', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/interface/themes/edit',
|
||||
name: 'ThemeEdit',
|
||||
component: () => import('@/views/interface/ThemeEdit'),
|
||||
meta: { title: '主题编辑', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/interface/menus',
|
||||
name: 'MenuList',
|
||||
component: () => import('@/views/interface/MenuList'),
|
||||
meta: { title: '菜单设置', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -52,7 +52,8 @@ import {
|
|||
TimePicker,
|
||||
Tooltip,
|
||||
Tree,
|
||||
TreeSelect
|
||||
TreeSelect,
|
||||
Descriptions
|
||||
} from 'ant-design-vue'
|
||||
|
||||
Vue.use(Affix)
|
||||
|
@ -106,6 +107,7 @@ Vue.use(Steps)
|
|||
Vue.use(Empty)
|
||||
Vue.use(Result)
|
||||
Vue.use(Space)
|
||||
Vue.use(Descriptions)
|
||||
|
||||
// message config
|
||||
message.config({
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
||||
<slot slot="extra" name="extra"></slot>
|
||||
<slot slot="footer" name="footer"></slot>
|
||||
<slot name="content" />
|
||||
</a-page-header>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,6 +16,7 @@
|
|||
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
||||
<slot slot="extra" name="extra"></slot>
|
||||
<slot slot="footer" name="footer"></slot>
|
||||
<slot name="content" />
|
||||
</a-page-header>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<a-icon style="margin-right:3px" type="lock" />
|
||||
启用
|
||||
</div>
|
||||
<div @click="handleOpenThemeSettingDrawer(item)">
|
||||
<div @click="handleRouteToThemeSetting(item)">
|
||||
<a-icon style="margin-right:3px" type="setting" />
|
||||
设置
|
||||
</div>
|
||||
|
@ -49,7 +49,7 @@
|
|||
</a-menu-item>
|
||||
<a-menu-item :key="3" @click="handleOpenLocalUpdateModal(item)">
|
||||
<a-icon style="margin-right:3px" type="file" />
|
||||
从主题包更新
|
||||
本地更新
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
|
@ -125,63 +125,36 @@
|
|||
</a-tabs>
|
||||
</div>
|
||||
</a-modal>
|
||||
<a-modal
|
||||
v-model="localUpdateModel.visible"
|
||||
:afterClose="onThemeInstallModalClose"
|
||||
:footer="null"
|
||||
destroyOnClose
|
||||
title="更新主题"
|
||||
>
|
||||
<FilePondUpload
|
||||
ref="updateByFile"
|
||||
:accepts="['application/x-zip', 'application/x-zip-compressed', 'application/zip']"
|
||||
:field="localUpdateModel.selected.id"
|
||||
:multiple="false"
|
||||
:uploadHandler="localUpdateModel.uploadHandler"
|
||||
label="点击选择主题更新包或将主题更新包拖拽到此处<br>仅支持 ZIP 格式的文件"
|
||||
name="file"
|
||||
@success="handleUploadSucceed"
|
||||
></FilePondUpload>
|
||||
</a-modal>
|
||||
<a-modal
|
||||
v-model="themeDeleteModal.visible"
|
||||
:afterClose="onThemeDeleteModalClose"
|
||||
:closable="false"
|
||||
:width="416"
|
||||
destroyOnClose
|
||||
title="提示"
|
||||
>
|
||||
<template slot="footer">
|
||||
<a-button @click="themeDeleteModal.visible = false">
|
||||
取消
|
||||
</a-button>
|
||||
<ReactiveButton
|
||||
:errored="themeDeleteModal.deleteErrored"
|
||||
:loading="themeDeleteModal.deleting"
|
||||
erroredText="删除失败"
|
||||
loadedText="删除成功"
|
||||
text="确定"
|
||||
@callback="handleDeleteThemeCallback"
|
||||
@click="handleDeleteTheme(themeDeleteModal.selected.id, themeDeleteModal.deleteSettings)"
|
||||
></ReactiveButton>
|
||||
</template>
|
||||
<p>确定删除【{{ themeDeleteModal.selected.name }}】主题?</p>
|
||||
<a-checkbox v-model="themeDeleteModal.deleteSettings">
|
||||
同时删除主题配置
|
||||
</a-checkbox>
|
||||
</a-modal>
|
||||
|
||||
<ThemeDeleteConfirmModal
|
||||
:theme="themeDeleteModal.selected"
|
||||
:visible.sync="themeDeleteModal.visible"
|
||||
@onAfterClose="themeDeleteModal.selected = {}"
|
||||
@success="handleListThemes"
|
||||
/>
|
||||
|
||||
<ThemeLocalUpgradeModal
|
||||
:theme="localUpgradeModel.selected"
|
||||
:visible.sync="localUpgradeModel.visible"
|
||||
@onAfterClose="localUpgradeModel.selected = {}"
|
||||
@success="handleListThemes"
|
||||
/>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThemeSettingDrawer from './components/ThemeSettingDrawer'
|
||||
import ThemeDeleteConfirmModal from './components/ThemeDeleteConfirmModal'
|
||||
import ThemeLocalUpgradeModal from './components/ThemeLocalUpgradeModal'
|
||||
import { PageView } from '@/layouts'
|
||||
import apiClient from '@/utils/api-client'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PageView,
|
||||
ThemeSettingDrawer
|
||||
ThemeSettingDrawer,
|
||||
ThemeDeleteConfirmModal,
|
||||
ThemeLocalUpgradeModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -208,18 +181,14 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
localUpdateModel: {
|
||||
localUpgradeModel: {
|
||||
visible: false,
|
||||
uploadHandler: (file, options, field) => apiClient.theme.updateByUpload(file, options, field),
|
||||
selected: {}
|
||||
},
|
||||
|
||||
themeDeleteModal: {
|
||||
visible: false,
|
||||
deleteSettings: false,
|
||||
selected: {},
|
||||
deleting: false,
|
||||
deleteErrored: false
|
||||
selected: {}
|
||||
},
|
||||
|
||||
themeSettingDrawer: {
|
||||
|
@ -245,20 +214,6 @@ export default {
|
|||
beforeMount() {
|
||||
this.handleListThemes()
|
||||
},
|
||||
destroyed() {
|
||||
this.$log.debug('Theme list destroyed.')
|
||||
this.themeSettingDrawer.visible = false
|
||||
this.installModal.visible = false
|
||||
this.localUpdateModel.visible = false
|
||||
this.themeDeleteModal.visible = false
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
this.themeSettingDrawer.visible = false
|
||||
this.installModal.visible = false
|
||||
this.localUpdateModel.visible = false
|
||||
this.themeDeleteModal.visible = false
|
||||
next()
|
||||
},
|
||||
methods: {
|
||||
handleListThemes() {
|
||||
this.list.loading = true
|
||||
|
@ -281,30 +236,8 @@ export default {
|
|||
this.handleListThemes()
|
||||
})
|
||||
},
|
||||
handleDeleteTheme(themeId, deleteSettings) {
|
||||
this.themeDeleteModal.deleting = true
|
||||
apiClient.theme
|
||||
.delete(themeId, deleteSettings)
|
||||
.catch(() => {
|
||||
this.themeDeleteModal.deleteErrored = false
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.themeDeleteModal.deleting = false
|
||||
}, 400)
|
||||
})
|
||||
},
|
||||
handleDeleteThemeCallback() {
|
||||
if (this.themeDeleteModal.deleteErrored) {
|
||||
this.themeDeleteModal.deleteErrored = false
|
||||
} else {
|
||||
this.themeDeleteModal.visible = false
|
||||
this.handleListThemes()
|
||||
}
|
||||
},
|
||||
handleUploadSucceed() {
|
||||
this.installModal.visible = false
|
||||
this.localUpdateModel.visible = false
|
||||
this.handleListThemes()
|
||||
},
|
||||
handleRemoteFetching() {
|
||||
|
@ -333,12 +266,11 @@ export default {
|
|||
}
|
||||
},
|
||||
handleOpenLocalUpdateModal(item) {
|
||||
this.localUpdateModel.selected = item
|
||||
this.localUpdateModel.visible = true
|
||||
this.localUpgradeModel.selected = item
|
||||
this.localUpgradeModel.visible = true
|
||||
},
|
||||
handleOpenThemeSettingDrawer(theme) {
|
||||
this.themeSettingDrawer.selected = theme
|
||||
this.themeSettingDrawer.visible = true
|
||||
handleRouteToThemeSetting(theme) {
|
||||
this.$router.push({ name: 'ThemeSetting', query: { themeId: theme.id } })
|
||||
},
|
||||
handleOpenThemeDeleteModal(item) {
|
||||
this.themeDeleteModal.visible = true
|
||||
|
@ -368,20 +300,12 @@ export default {
|
|||
if (this.$refs.upload) {
|
||||
this.$refs.upload.handleClearFileList()
|
||||
}
|
||||
if (this.$refs.updateByFile) {
|
||||
this.$refs.updateByFile.handleClearFileList()
|
||||
}
|
||||
this.installModal.remote.url = null
|
||||
this.handleListThemes()
|
||||
},
|
||||
onThemeSettingsDrawerClose() {
|
||||
this.themeSettingDrawer.visible = false
|
||||
this.themeSettingDrawer.selected = {}
|
||||
},
|
||||
onThemeDeleteModalClose() {
|
||||
this.themeDeleteModal.visible = false
|
||||
this.themeDeleteModal.deleteSettings = false
|
||||
this.themeDeleteModal.selected = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
<template>
|
||||
<page-view :sub-title="theme.current.version || '-'" :title="theme.current.name || '-'" affix>
|
||||
<template slot="extra">
|
||||
<a-dropdown>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="1" @click="handleRemoteUpdate">
|
||||
<a-icon type="cloud" />
|
||||
在线更新
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="localUpgradeModel.visible = true">
|
||||
<a-icon type="file" />
|
||||
本地更新
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-button icon="upload">
|
||||
更新
|
||||
<a-icon type="down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<a-button
|
||||
:disabled="theme.current.activated"
|
||||
icon="delete"
|
||||
type="danger"
|
||||
@click="themeDeleteModal.visible = true"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</template>
|
||||
<a-spin :spinning="theme.loading">
|
||||
<div v-if="theme.current.id" class="card-container">
|
||||
<a-tabs defaultActiveKey="0" type="card">
|
||||
<a-tab-pane :key="0" tab="关于">
|
||||
<a-avatar :alt="theme.current.name" :size="72" :src="theme.current.logo" shape="square" />
|
||||
<a-divider />
|
||||
<a-descriptions :column="1" layout="horizontal">
|
||||
<a-descriptions-item label="作者">
|
||||
<a :href="theme.current.author.website || '#'">
|
||||
{{ theme.current.author.name }}
|
||||
</a>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="介绍">
|
||||
{{ theme.current.description || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="官网">
|
||||
<a :href="theme.current.website || '#'">
|
||||
{{ theme.current.website || '-' }}
|
||||
</a>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Git 仓库">
|
||||
<a :href="theme.current.repo || '#'">
|
||||
{{ theme.current.repo || '-' }}
|
||||
</a>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="主题标识">
|
||||
{{ theme.current.id }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="当前版本">
|
||||
{{ theme.current.version }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="存储位置">
|
||||
{{ theme.current.themePath }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane v-for="(group, index) in theme.configurations" :key="index + 1" :tab="group.label">
|
||||
<a-form
|
||||
:wrapperCol="{
|
||||
xl: { span: 8 },
|
||||
lg: { span: 8 },
|
||||
sm: { span: 12 },
|
||||
xs: { span: 24 }
|
||||
}"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item v-for="(item, formItemIndex) in group.items" :key="formItemIndex" :label="item.label + ':'">
|
||||
<p v-if="item.description && item.description !== ''" slot="help" v-html="item.description"></p>
|
||||
<a-input
|
||||
v-if="item.type === 'TEXT'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
:placeholder="item.placeholder"
|
||||
/>
|
||||
<a-input
|
||||
v-else-if="item.type === 'TEXTAREA'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:autoSize="{ minRows: 5 }"
|
||||
:placeholder="item.placeholder"
|
||||
type="textarea"
|
||||
/>
|
||||
<a-radio-group
|
||||
v-else-if="item.type === 'RADIO'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
>
|
||||
<a-radio v-for="(option, radioIndex) in item.options" :key="radioIndex" :value="option.value">
|
||||
{{ option.label }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
<a-select
|
||||
v-else-if="item.type === 'SELECT'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
>
|
||||
<a-select-option v-for="option in item.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<verte
|
||||
v-else-if="item.type === 'COLOR'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
model="hex"
|
||||
picker="square"
|
||||
style="display: inline-block;height: 24px;"
|
||||
></verte>
|
||||
<AttachmentInput
|
||||
v-else-if="item.type === 'ATTACHMENT'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
:placeholder="item.placeholder"
|
||||
/>
|
||||
<a-input-number
|
||||
v-else-if="item.type === 'NUMBER'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
style="width:100%"
|
||||
/>
|
||||
<a-switch
|
||||
v-else-if="item.type === 'SWITCH'"
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultChecked="item.defaultValue"
|
||||
/>
|
||||
<a-input
|
||||
v-else
|
||||
v-model="theme.settings[item.name]"
|
||||
:defaultValue="item.defaultValue"
|
||||
:placeholder="item.placeholder"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<ReactiveButton
|
||||
:errored="theme.saveErrored"
|
||||
:loading="theme.saving"
|
||||
erroredText="保存失败"
|
||||
loadedText="保存成功"
|
||||
text="保存"
|
||||
type="primary"
|
||||
@callback="theme.saveErrored = false"
|
||||
@click="handleSaveSettings"
|
||||
></ReactiveButton>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
<ThemeDeleteConfirmModal
|
||||
:theme="theme.current"
|
||||
:visible.sync="themeDeleteModal.visible"
|
||||
@success="onThemeDeleteSucceed"
|
||||
/>
|
||||
|
||||
<ThemeLocalUpgradeModal
|
||||
:theme="theme.current"
|
||||
:visible.sync="localUpgradeModel.visible"
|
||||
@success="handleGetTheme"
|
||||
/>
|
||||
</page-view>
|
||||
</template>
|
||||
<script>
|
||||
// components
|
||||
import Verte from 'verte'
|
||||
import 'verte/dist/verte.css'
|
||||
import { PageView } from '@/layouts'
|
||||
import ThemeDeleteConfirmModal from './components/ThemeDeleteConfirmModal'
|
||||
import ThemeLocalUpgradeModal from './components/ThemeLocalUpgradeModal'
|
||||
|
||||
// utils
|
||||
import apiClient from '@/utils/api-client'
|
||||
|
||||
export default {
|
||||
name: 'ThemeSetting',
|
||||
components: {
|
||||
PageView,
|
||||
Verte,
|
||||
ThemeDeleteConfirmModal,
|
||||
ThemeLocalUpgradeModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
theme: {
|
||||
current: {},
|
||||
settings: [],
|
||||
configurations: [],
|
||||
loading: false,
|
||||
saving: false,
|
||||
saveErrored: false
|
||||
},
|
||||
themeDeleteModal: {
|
||||
visible: false
|
||||
},
|
||||
localUpgradeModel: {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
// Get post id from query
|
||||
const themeId = to.query.themeId
|
||||
next(async vm => {
|
||||
await vm.handleGetTheme(themeId)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async handleGetTheme(themeId) {
|
||||
try {
|
||||
this.theme.loading = true
|
||||
if (themeId) {
|
||||
const { data } = await apiClient.theme.get(themeId)
|
||||
this.theme.current = data
|
||||
} else {
|
||||
const { data } = await apiClient.theme.getActivatedTheme()
|
||||
this.theme.current = data
|
||||
}
|
||||
await this.handleGetConfigurations()
|
||||
await this.handleGetSettings()
|
||||
} finally {
|
||||
this.theme.loading = false
|
||||
}
|
||||
},
|
||||
async handleGetConfigurations() {
|
||||
try {
|
||||
const { data } = await apiClient.theme.listConfigurations(this.theme.current.id)
|
||||
this.theme.configurations = data
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
}
|
||||
},
|
||||
async handleGetSettings() {
|
||||
try {
|
||||
const { data } = await apiClient.theme.listSettings(this.theme.current.id)
|
||||
this.theme.settings = data
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
}
|
||||
},
|
||||
async handleSaveSettings() {
|
||||
try {
|
||||
this.theme.saving = true
|
||||
await apiClient.theme.saveSettings(this.theme.current.id, this.theme.settings)
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
this.theme.saveErrored = true
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
this.theme.saving = false
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
onThemeDeleteSucceed() {
|
||||
this.$router.replace({ name: 'ThemeList' })
|
||||
},
|
||||
handleRemoteUpdate() {
|
||||
const _this = this
|
||||
_this.$confirm({
|
||||
title: '提示',
|
||||
maskClosable: true,
|
||||
content: '确定更新【' + _this.theme.current.name + '】主题?',
|
||||
async onOk() {
|
||||
const hideLoading = _this.$message.loading('更新中...', 0)
|
||||
try {
|
||||
await apiClient.theme.updateThemeByFetching(_this.theme.current.id)
|
||||
_this.$message.success('更新成功!')
|
||||
} catch (e) {
|
||||
_this.$log.error('Failed to update theme: ', e)
|
||||
} finally {
|
||||
hideLoading()
|
||||
await _this.handleGetTheme(_this.theme.current.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<a-modal v-model="modalVisible" :afterClose="onAfterClose" :closable="false" :width="416" destroyOnClose title="提示">
|
||||
<template slot="footer">
|
||||
<a-button @click="modalVisible = false">
|
||||
取消
|
||||
</a-button>
|
||||
<ReactiveButton
|
||||
:errored="deleteErrored"
|
||||
:loading="deleting"
|
||||
erroredText="删除失败"
|
||||
loadedText="删除成功"
|
||||
text="确定"
|
||||
@callback="handleDeleteCallback"
|
||||
@click="handleDelete()"
|
||||
></ReactiveButton>
|
||||
</template>
|
||||
<p>确定删除【{{ theme.name }}】主题?</p>
|
||||
<a-checkbox v-model="deleteSettings">
|
||||
同时删除主题配置
|
||||
</a-checkbox>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import apiClient from '@/utils/api-client'
|
||||
|
||||
export default {
|
||||
name: 'ThemeDeleteConfirmModal',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
theme: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deleteErrored: false,
|
||||
deleting: false,
|
||||
deleteSettings: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
modalVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:visible', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleDelete() {
|
||||
try {
|
||||
this.deleting = true
|
||||
await apiClient.theme.delete(this.theme.id, this.deleteSettings)
|
||||
} catch (e) {
|
||||
this.deleteErrored = false
|
||||
this.$log.error('Delete theme failed', e)
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
this.deleting = false
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
handleDeleteCallback() {
|
||||
if (this.deleteErrored) {
|
||||
this.deleteErrored = false
|
||||
} else {
|
||||
this.modalVisible = false
|
||||
this.$emit('success')
|
||||
}
|
||||
},
|
||||
onAfterClose() {
|
||||
this.deleteErrored = false
|
||||
this.deleting = false
|
||||
this.deleteSettings = false
|
||||
this.$emit('onAfterClose')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,56 @@
|
|||
<template>
|
||||
<a-modal v-model="modalVisible" :afterClose="onModalClose" :footer="null" destroyOnClose title="更新主题">
|
||||
<FilePondUpload
|
||||
ref="updateByFile"
|
||||
:accepts="['application/x-zip', 'application/x-zip-compressed', 'application/zip']"
|
||||
:field="theme.id"
|
||||
:multiple="false"
|
||||
:uploadHandler="uploadHandler"
|
||||
label="点击选择主题更新包或将主题更新包拖拽到此处<br>仅支持 ZIP 格式的文件"
|
||||
name="file"
|
||||
@success="onThemeUploadSuccess"
|
||||
></FilePondUpload>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import apiClient from '@/utils/api-client'
|
||||
|
||||
export default {
|
||||
name: 'ThemeLocalUpgradeModal',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
theme: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uploadHandler: (file, options, field) => apiClient.theme.updateByUpload(file, options, field)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
modalVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:visible', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onModalClose() {
|
||||
this.$refs.updateByFile.handleClearFileList()
|
||||
this.$emit('onAfterClose')
|
||||
},
|
||||
onThemeUploadSuccess() {
|
||||
this.modalVisible = false
|
||||
this.$emit('success')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -27,6 +27,7 @@
|
|||
<li>版本:{{ environments.version }}</li>
|
||||
<li>数据库:{{ environments.database }}</li>
|
||||
<li>运行模式:{{ environments.mode }}</li>
|
||||
<li>启用主题:{{ activatedTheme.name }}</li>
|
||||
<li>启动时间:{{ environments.startTime | moment }}</li>
|
||||
</ul>
|
||||
<a class="mr-3" href="https://halo.run" target="_blank"
|
||||
|
@ -123,7 +124,8 @@ export default {
|
|||
checking: false,
|
||||
isLatest: false,
|
||||
latestData: {},
|
||||
versionContentVisible: false
|
||||
versionContentVisible: false,
|
||||
activatedTheme: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -145,19 +147,24 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.getEnvironments()
|
||||
this.handleGetActivatedTheme()
|
||||
this.fetchContributors()
|
||||
},
|
||||
methods: {
|
||||
async getEnvironments() {
|
||||
await apiClient.getEnvironment().then(response => {
|
||||
this.environments = response.data
|
||||
})
|
||||
const { data } = await apiClient.getEnvironment()
|
||||
this.environments = data
|
||||
this.checkServerUpdate()
|
||||
},
|
||||
async handleGetActivatedTheme() {
|
||||
const { data } = await apiClient.theme.getActivatedTheme()
|
||||
this.activatedTheme = data
|
||||
},
|
||||
handleCopyEnvironments() {
|
||||
const text = `版本:${this.environments.version}
|
||||
数据库:${this.environments.database}
|
||||
运行模式:${this.environments.mode}
|
||||
启用主题:${this.activatedTheme.name}
|
||||
User Agent:${navigator.userAgent}`
|
||||
this.$copyText(text)
|
||||
.then(message => {
|
||||
|
|
Loading…
Reference in New Issue