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/basic-setup": "^0.19.0",
|
||||||
"@codemirror/lang-html": "^0.19.3",
|
"@codemirror/lang-html": "^0.19.3",
|
||||||
"@codemirror/lang-java": "^0.19.1",
|
"@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",
|
"ant-design-vue": "^1.7.8",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
|
|
|
@ -5,7 +5,7 @@ specifiers:
|
||||||
'@codemirror/basic-setup': ^0.19.0
|
'@codemirror/basic-setup': ^0.19.0
|
||||||
'@codemirror/lang-html': ^0.19.3
|
'@codemirror/lang-html': ^0.19.3
|
||||||
'@codemirror/lang-java': ^0.19.1
|
'@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-babel': ^3.12.1
|
||||||
'@vue/cli-plugin-eslint': ^4.5.15
|
'@vue/cli-plugin-eslint': ^4.5.15
|
||||||
'@vue/cli-plugin-unit-jest': ^4.5.15
|
'@vue/cli-plugin-unit-jest': ^4.5.15
|
||||||
|
@ -54,7 +54,7 @@ dependencies:
|
||||||
'@codemirror/basic-setup': 0.19.0
|
'@codemirror/basic-setup': 0.19.0
|
||||||
'@codemirror/lang-html': 0.19.3
|
'@codemirror/lang-html': 0.19.3
|
||||||
'@codemirror/lang-java': 0.19.1
|
'@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
|
ant-design-vue: 1.7.8_9065e7474e033a8e4b95615fc8e6c36c
|
||||||
crypto-js: 4.1.1
|
crypto-js: 4.1.1
|
||||||
dayjs: 1.10.7
|
dayjs: 1.10.7
|
||||||
|
@ -1348,32 +1348,33 @@ packages:
|
||||||
purgecss: 2.3.0
|
purgecss: 2.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@halo-dev/admin-api/1.0.0-alpha.44:
|
/@halo-dev/admin-api/1.0.0-alpha.46:
|
||||||
resolution: {integrity: sha512-nCJsx4gDxCjkoGJKsBpcgLhAUc8RYrqKMGM1VSi9t64xTFdXDjIJgtcIQuBgJ0b4R58JIp6A6ti5S764vz4BDA==}
|
resolution: {integrity: sha512-OlDZm2/L/9IKlhCjWWFO1KkqxfEip/nMZrqavMQXghcHomWBqxvMttB4P1vg+0xmoZh65uYhl3De6VSIbL2FsQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dependencies:
|
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:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@halo-dev/logger/1.0.0-alpha.44:
|
/@halo-dev/logger/1.0.0-alpha.46:
|
||||||
resolution: {integrity: sha512-ORHP6pj8wLb+mwsk+pYqvH9tqNWTr+96AiZgtSMcdwojA1KupFSVzHN0aqpk8HeGfSrBNbMz4VT6ey+OVnWrcQ==}
|
resolution: {integrity: sha512-iBLa55wq6i++9l2L4w1X0vY5mMmqOFBbzjvTNv9TcG2ssUNXL1s0uRgasjvlQAI5EDfvmHhth/RW4ACPM+EUnQ==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@halo-dev/rest-api-client/1.0.0-alpha.44:
|
/@halo-dev/rest-api-client/1.0.0-alpha.46:
|
||||||
resolution: {integrity: sha512-fCzh7ihLpI7hrq0S2M9YjROTvoTT5JeEOqv5O/hon4bTfpBqEb7REjjYMONR8lJNgmEI9wzviEhn7Xxg7nX/vA==}
|
resolution: {integrity: sha512-kv88fszMZe26KAVfZXzjIvqZV6wsMWCxMtoWgk9F8DkYs3kQt9n+98g5azmtFgZh2RYh3O4einjcFOUQ44yRxg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@halo-dev/logger': 1.0.0-alpha.44
|
'@halo-dev/logger': 1.0.0-alpha.46
|
||||||
axios: 0.24.0
|
axios: 0.24.0
|
||||||
form-data: 4.0.0
|
form-data: 4.0.0
|
||||||
js-base64: 3.7.2
|
js-base64: 3.7.2
|
||||||
qs: 6.10.1
|
qs: 6.10.3
|
||||||
store: 2.0.12
|
tslib: 2.3.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -9403,8 +9404,8 @@ packages:
|
||||||
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/qs/6.10.1:
|
/qs/6.10.3:
|
||||||
resolution: {integrity: sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==}
|
resolution: {integrity: sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
dependencies:
|
dependencies:
|
||||||
side-channel: 1.0.4
|
side-channel: 1.0.4
|
||||||
|
@ -10351,10 +10352,6 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/store/2.0.12:
|
|
||||||
resolution: {integrity: sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/stream-browserify/2.0.2:
|
/stream-browserify/2.0.2:
|
||||||
resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
|
resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
|
||||||
dependencies:
|
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 AttachmentUploadModal from './Attachment/AttachmentUploadModal'
|
||||||
import ReactiveButton from './Button/ReactiveButton'
|
import ReactiveButton from './Button/ReactiveButton'
|
||||||
import PostTag from './Post/PostTag'
|
import PostTag from './Post/PostTag'
|
||||||
|
import AttachmentInput from './Input/AttachmentInput'
|
||||||
|
|
||||||
const _components = {
|
const _components = {
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
|
@ -15,7 +16,8 @@ const _components = {
|
||||||
AttachmentSelectDrawer,
|
AttachmentSelectDrawer,
|
||||||
AttachmentUploadModal,
|
AttachmentUploadModal,
|
||||||
ReactiveButton,
|
ReactiveButton,
|
||||||
PostTag
|
PostTag,
|
||||||
|
AttachmentInput
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = {}
|
const components = {}
|
||||||
|
|
|
@ -141,16 +141,22 @@ export const asyncRouterMap = [
|
||||||
meta: { title: '主题', hiddenHeaderContent: false }
|
meta: { title: '主题', hiddenHeaderContent: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/interface/menus',
|
path: '/interface/themes/setting',
|
||||||
name: 'MenuList',
|
name: 'ThemeSetting',
|
||||||
component: () => import('@/views/interface/MenuList'),
|
component: () => import('@/views/interface/ThemeSetting'),
|
||||||
meta: { title: '菜单', hiddenHeaderContent: false }
|
meta: { title: '主题设置', hiddenHeaderContent: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/interface/themes/edit',
|
path: '/interface/themes/edit',
|
||||||
name: 'ThemeEdit',
|
name: 'ThemeEdit',
|
||||||
component: () => import('@/views/interface/ThemeEdit'),
|
component: () => import('@/views/interface/ThemeEdit'),
|
||||||
meta: { title: '主题编辑', hiddenHeaderContent: false }
|
meta: { title: '主题编辑', hiddenHeaderContent: false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/interface/menus',
|
||||||
|
name: 'MenuList',
|
||||||
|
component: () => import('@/views/interface/MenuList'),
|
||||||
|
meta: { title: '菜单设置', hiddenHeaderContent: false }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,7 +52,8 @@ import {
|
||||||
TimePicker,
|
TimePicker,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Tree,
|
Tree,
|
||||||
TreeSelect
|
TreeSelect,
|
||||||
|
Descriptions
|
||||||
} from 'ant-design-vue'
|
} from 'ant-design-vue'
|
||||||
|
|
||||||
Vue.use(Affix)
|
Vue.use(Affix)
|
||||||
|
@ -106,6 +107,7 @@ Vue.use(Steps)
|
||||||
Vue.use(Empty)
|
Vue.use(Empty)
|
||||||
Vue.use(Result)
|
Vue.use(Result)
|
||||||
Vue.use(Space)
|
Vue.use(Space)
|
||||||
|
Vue.use(Descriptions)
|
||||||
|
|
||||||
// message config
|
// message config
|
||||||
message.config({
|
message.config({
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
||||||
<slot slot="extra" name="extra"></slot>
|
<slot slot="extra" name="extra"></slot>
|
||||||
<slot slot="footer" name="footer"></slot>
|
<slot slot="footer" name="footer"></slot>
|
||||||
|
<slot name="content" />
|
||||||
</a-page-header>
|
</a-page-header>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
<a-page-header :breadcrumb="{ props: { routes: breadList } }" :sub-title="subTitle" :title="title">
|
||||||
<slot slot="extra" name="extra"></slot>
|
<slot slot="extra" name="extra"></slot>
|
||||||
<slot slot="footer" name="footer"></slot>
|
<slot slot="footer" name="footer"></slot>
|
||||||
|
<slot name="content" />
|
||||||
</a-page-header>
|
</a-page-header>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<a-icon style="margin-right:3px" type="lock" />
|
<a-icon style="margin-right:3px" type="lock" />
|
||||||
启用
|
启用
|
||||||
</div>
|
</div>
|
||||||
<div @click="handleOpenThemeSettingDrawer(item)">
|
<div @click="handleRouteToThemeSetting(item)">
|
||||||
<a-icon style="margin-right:3px" type="setting" />
|
<a-icon style="margin-right:3px" type="setting" />
|
||||||
设置
|
设置
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item :key="3" @click="handleOpenLocalUpdateModal(item)">
|
<a-menu-item :key="3" @click="handleOpenLocalUpdateModal(item)">
|
||||||
<a-icon style="margin-right:3px" type="file" />
|
<a-icon style="margin-right:3px" type="file" />
|
||||||
从主题包更新
|
本地更新
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
|
@ -125,63 +125,36 @@
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<a-modal
|
|
||||||
v-model="localUpdateModel.visible"
|
<ThemeDeleteConfirmModal
|
||||||
:afterClose="onThemeInstallModalClose"
|
:theme="themeDeleteModal.selected"
|
||||||
:footer="null"
|
:visible.sync="themeDeleteModal.visible"
|
||||||
destroyOnClose
|
@onAfterClose="themeDeleteModal.selected = {}"
|
||||||
title="更新主题"
|
@success="handleListThemes"
|
||||||
>
|
/>
|
||||||
<FilePondUpload
|
|
||||||
ref="updateByFile"
|
<ThemeLocalUpgradeModal
|
||||||
:accepts="['application/x-zip', 'application/x-zip-compressed', 'application/zip']"
|
:theme="localUpgradeModel.selected"
|
||||||
:field="localUpdateModel.selected.id"
|
:visible.sync="localUpgradeModel.visible"
|
||||||
:multiple="false"
|
@onAfterClose="localUpgradeModel.selected = {}"
|
||||||
:uploadHandler="localUpdateModel.uploadHandler"
|
@success="handleListThemes"
|
||||||
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>
|
|
||||||
</page-view>
|
</page-view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ThemeSettingDrawer from './components/ThemeSettingDrawer'
|
import ThemeSettingDrawer from './components/ThemeSettingDrawer'
|
||||||
|
import ThemeDeleteConfirmModal from './components/ThemeDeleteConfirmModal'
|
||||||
|
import ThemeLocalUpgradeModal from './components/ThemeLocalUpgradeModal'
|
||||||
import { PageView } from '@/layouts'
|
import { PageView } from '@/layouts'
|
||||||
import apiClient from '@/utils/api-client'
|
import apiClient from '@/utils/api-client'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PageView,
|
PageView,
|
||||||
ThemeSettingDrawer
|
ThemeSettingDrawer,
|
||||||
|
ThemeDeleteConfirmModal,
|
||||||
|
ThemeLocalUpgradeModal
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -208,18 +181,14 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
localUpdateModel: {
|
localUpgradeModel: {
|
||||||
visible: false,
|
visible: false,
|
||||||
uploadHandler: (file, options, field) => apiClient.theme.updateByUpload(file, options, field),
|
|
||||||
selected: {}
|
selected: {}
|
||||||
},
|
},
|
||||||
|
|
||||||
themeDeleteModal: {
|
themeDeleteModal: {
|
||||||
visible: false,
|
visible: false,
|
||||||
deleteSettings: false,
|
selected: {}
|
||||||
selected: {},
|
|
||||||
deleting: false,
|
|
||||||
deleteErrored: false
|
|
||||||
},
|
},
|
||||||
|
|
||||||
themeSettingDrawer: {
|
themeSettingDrawer: {
|
||||||
|
@ -245,20 +214,6 @@ export default {
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.handleListThemes()
|
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: {
|
methods: {
|
||||||
handleListThemes() {
|
handleListThemes() {
|
||||||
this.list.loading = true
|
this.list.loading = true
|
||||||
|
@ -281,30 +236,8 @@ export default {
|
||||||
this.handleListThemes()
|
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() {
|
handleUploadSucceed() {
|
||||||
this.installModal.visible = false
|
this.installModal.visible = false
|
||||||
this.localUpdateModel.visible = false
|
|
||||||
this.handleListThemes()
|
this.handleListThemes()
|
||||||
},
|
},
|
||||||
handleRemoteFetching() {
|
handleRemoteFetching() {
|
||||||
|
@ -333,12 +266,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleOpenLocalUpdateModal(item) {
|
handleOpenLocalUpdateModal(item) {
|
||||||
this.localUpdateModel.selected = item
|
this.localUpgradeModel.selected = item
|
||||||
this.localUpdateModel.visible = true
|
this.localUpgradeModel.visible = true
|
||||||
},
|
},
|
||||||
handleOpenThemeSettingDrawer(theme) {
|
handleRouteToThemeSetting(theme) {
|
||||||
this.themeSettingDrawer.selected = theme
|
this.$router.push({ name: 'ThemeSetting', query: { themeId: theme.id } })
|
||||||
this.themeSettingDrawer.visible = true
|
|
||||||
},
|
},
|
||||||
handleOpenThemeDeleteModal(item) {
|
handleOpenThemeDeleteModal(item) {
|
||||||
this.themeDeleteModal.visible = true
|
this.themeDeleteModal.visible = true
|
||||||
|
@ -368,20 +300,12 @@ export default {
|
||||||
if (this.$refs.upload) {
|
if (this.$refs.upload) {
|
||||||
this.$refs.upload.handleClearFileList()
|
this.$refs.upload.handleClearFileList()
|
||||||
}
|
}
|
||||||
if (this.$refs.updateByFile) {
|
|
||||||
this.$refs.updateByFile.handleClearFileList()
|
|
||||||
}
|
|
||||||
this.installModal.remote.url = null
|
this.installModal.remote.url = null
|
||||||
this.handleListThemes()
|
this.handleListThemes()
|
||||||
},
|
},
|
||||||
onThemeSettingsDrawerClose() {
|
onThemeSettingsDrawerClose() {
|
||||||
this.themeSettingDrawer.visible = false
|
this.themeSettingDrawer.visible = false
|
||||||
this.themeSettingDrawer.selected = {}
|
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.version }}</li>
|
||||||
<li>数据库:{{ environments.database }}</li>
|
<li>数据库:{{ environments.database }}</li>
|
||||||
<li>运行模式:{{ environments.mode }}</li>
|
<li>运行模式:{{ environments.mode }}</li>
|
||||||
|
<li>启用主题:{{ activatedTheme.name }}</li>
|
||||||
<li>启动时间:{{ environments.startTime | moment }}</li>
|
<li>启动时间:{{ environments.startTime | moment }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="mr-3" href="https://halo.run" target="_blank"
|
<a class="mr-3" href="https://halo.run" target="_blank"
|
||||||
|
@ -123,7 +124,8 @@ export default {
|
||||||
checking: false,
|
checking: false,
|
||||||
isLatest: false,
|
isLatest: false,
|
||||||
latestData: {},
|
latestData: {},
|
||||||
versionContentVisible: false
|
versionContentVisible: false,
|
||||||
|
activatedTheme: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -145,19 +147,24 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getEnvironments()
|
this.getEnvironments()
|
||||||
|
this.handleGetActivatedTheme()
|
||||||
this.fetchContributors()
|
this.fetchContributors()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getEnvironments() {
|
async getEnvironments() {
|
||||||
await apiClient.getEnvironment().then(response => {
|
const { data } = await apiClient.getEnvironment()
|
||||||
this.environments = response.data
|
this.environments = data
|
||||||
})
|
|
||||||
this.checkServerUpdate()
|
this.checkServerUpdate()
|
||||||
},
|
},
|
||||||
|
async handleGetActivatedTheme() {
|
||||||
|
const { data } = await apiClient.theme.getActivatedTheme()
|
||||||
|
this.activatedTheme = data
|
||||||
|
},
|
||||||
handleCopyEnvironments() {
|
handleCopyEnvironments() {
|
||||||
const text = `版本:${this.environments.version}
|
const text = `版本:${this.environments.version}
|
||||||
数据库:${this.environments.database}
|
数据库:${this.environments.database}
|
||||||
运行模式:${this.environments.mode}
|
运行模式:${this.environments.mode}
|
||||||
|
启用主题:${this.activatedTheme.name}
|
||||||
User Agent:${navigator.userAgent}`
|
User Agent:${navigator.userAgent}`
|
||||||
this.$copyText(text)
|
this.$copyText(text)
|
||||||
.then(message => {
|
.then(message => {
|
||||||
|
|
Loading…
Reference in New Issue