mirror of https://github.com/halo-dev/halo-admin
379 lines
12 KiB
Vue
379 lines
12 KiB
Vue
<template>
|
||
<page-view affix :title="activatedTheme ? activatedTheme.name : '无'" subTitle="当前启用">
|
||
<template slot="extra">
|
||
<a-button icon="reload" :loading="list.loading" @click="handleRefreshThemesCache">
|
||
刷新
|
||
</a-button>
|
||
<a-button type="primary" icon="plus" @click="installModal.visible = true">
|
||
安装
|
||
</a-button>
|
||
</template>
|
||
<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 }">
|
||
<div class="theme-screenshot">
|
||
<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>
|
||
<div @click="handleOpenThemeSettingDrawer(item)">
|
||
<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-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>
|
||
<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>
|
||
</a-menu>
|
||
</a-dropdown>
|
||
</template>
|
||
</a-card>
|
||
</a-list-item>
|
||
</a-list>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<ThemeSettingDrawer
|
||
:theme="themeSettingDrawer.selected"
|
||
v-model="themeSettingDrawer.visible"
|
||
@close="onThemeSettingsDrawerClose"
|
||
/>
|
||
|
||
<a-modal
|
||
title="安装主题"
|
||
v-model="installModal.visible"
|
||
destroyOnClose
|
||
:footer="null"
|
||
:bodyStyle="{ padding: '0 24px 24px' }"
|
||
:afterClose="onThemeInstallModalClose"
|
||
>
|
||
<div class="custom-tab-wrapper">
|
||
<a-tabs>
|
||
<a-tab-pane tab="本地上传" key="1">
|
||
<FilePondUpload
|
||
ref="upload"
|
||
name="file"
|
||
:accepts="['application/x-zip', 'application/x-zip-compressed', 'application/zip']"
|
||
label="点击选择主题包或将主题包拖拽到此处<br>仅支持 ZIP 格式的文件"
|
||
:uploadHandler="installModal.local.uploadHandler"
|
||
@success="handleUploadSucceed"
|
||
></FilePondUpload>
|
||
<a-alert type="info" closable>
|
||
<template slot="message">
|
||
更多主题请访问:
|
||
<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-form-model
|
||
ref="remoteInstallForm"
|
||
:model="installModal.remote"
|
||
:rules="installModal.remote.rules"
|
||
layout="vertical"
|
||
>
|
||
<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>
|
||
<ReactiveButton
|
||
type="primary"
|
||
@click="handleRemoteFetching"
|
||
@callback="handleRemoteFetchCallback"
|
||
:loading="installModal.remote.fetching"
|
||
:errored="installModal.remote.fetchErrored"
|
||
text="下载"
|
||
loadedText="下载成功"
|
||
erroredText="下载失败"
|
||
></ReactiveButton>
|
||
</a-form-model-item>
|
||
</a-form-model>
|
||
<a-alert type="info" closable>
|
||
<template slot="message">
|
||
目前仅支持远程 Git 仓库和 ZIP 下载链接。更多主题请访问:
|
||
<a target="_blank" href="https://halo.run/themes.html">https://halo.run/themes</a>
|
||
</template>
|
||
</a-alert>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</div>
|
||
</a-modal>
|
||
<a-modal
|
||
title="更新主题"
|
||
v-model="localUpdateModel.visible"
|
||
:footer="null"
|
||
destroyOnClose
|
||
:afterClose="onThemeInstallModalClose"
|
||
>
|
||
<FilePondUpload
|
||
ref="updateByupload"
|
||
name="file"
|
||
:accepts="['application/x-zip', 'application/x-zip-compressed', 'application/zip']"
|
||
label="点击选择主题更新包或将主题更新包拖拽到此处<br>仅支持 ZIP 格式的文件"
|
||
:uploadHandler="localUpdateModel.uploadHandler"
|
||
:filed="localUpdateModel.selected.id"
|
||
:multiple="false"
|
||
@success="handleUploadSucceed"
|
||
></FilePondUpload>
|
||
</a-modal>
|
||
<a-modal
|
||
title="提示"
|
||
v-model="themeDeleteModal.visible"
|
||
:width="416"
|
||
:closable="false"
|
||
destroyOnClose
|
||
:afterClose="onThemeDeleteModalClose"
|
||
>
|
||
<template slot="footer">
|
||
<a-button @click="themeDeleteModal.visible = false">
|
||
取消
|
||
</a-button>
|
||
<ReactiveButton
|
||
@click="handleDeleteTheme(themeDeleteModal.selected.id, themeDeleteModal.deleteSettings)"
|
||
@callback="handleDeleteThemeCallback"
|
||
:loading="themeDeleteModal.deleting"
|
||
:errored="themeDeleteModal.deleteErrored"
|
||
text="确定"
|
||
loadedText="删除成功"
|
||
erroredText="删除失败"
|
||
></ReactiveButton>
|
||
</template>
|
||
<p>确定删除【{{ themeDeleteModal.selected.name }}】主题?</p>
|
||
<a-checkbox v-model="themeDeleteModal.deleteSettings">
|
||
同时删除主题配置
|
||
</a-checkbox>
|
||
</a-modal>
|
||
</page-view>
|
||
</template>
|
||
|
||
<script>
|
||
import ThemeSettingDrawer from './components/ThemeSettingDrawer'
|
||
import { PageView } from '@/layouts'
|
||
import themeApi from '@/api/theme'
|
||
|
||
export default {
|
||
components: {
|
||
PageView,
|
||
ThemeSettingDrawer
|
||
},
|
||
data() {
|
||
return {
|
||
list: {
|
||
loading: false,
|
||
data: []
|
||
},
|
||
|
||
installModal: {
|
||
visible: false,
|
||
local: {
|
||
uploadHandler: themeApi.upload
|
||
},
|
||
|
||
remote: {
|
||
url: null,
|
||
|
||
fetching: false,
|
||
fetchErrored: false,
|
||
|
||
rules: {
|
||
url: [{ required: true, message: '* 远程地址不能为空', trigger: ['change'] }]
|
||
}
|
||
}
|
||
},
|
||
|
||
localUpdateModel: {
|
||
visible: false,
|
||
uploadHandler: themeApi.updateByUpload,
|
||
selected: {}
|
||
},
|
||
|
||
themeDeleteModal: {
|
||
visible: false,
|
||
deleteSettings: false,
|
||
selected: {},
|
||
deleting: false,
|
||
deleteErrored: false
|
||
},
|
||
|
||
themeSettingDrawer: {
|
||
visible: false,
|
||
selected: {}
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
sortedThemes() {
|
||
const data = this.list.data.slice(0)
|
||
return data.sort((a, b) => {
|
||
return b.activated - a.activated
|
||
})
|
||
},
|
||
activatedTheme() {
|
||
if (this.sortedThemes.length > 0) {
|
||
return this.sortedThemes[0]
|
||
}
|
||
return null
|
||
}
|
||
},
|
||
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
|
||
themeApi
|
||
.list()
|
||
.then(response => {
|
||
this.list.data = response.data.data
|
||
})
|
||
.finally(() => {
|
||
setTimeout(() => {
|
||
this.list.loading = false
|
||
}, 200)
|
||
})
|
||
},
|
||
handleRefreshThemesCache() {
|
||
themeApi.reload().finally(() => {
|
||
this.handleListThemes()
|
||
})
|
||
},
|
||
handleActiveTheme(theme) {
|
||
themeApi.active(theme.id).finally(() => {
|
||
this.handleListThemes()
|
||
})
|
||
},
|
||
handleDeleteTheme(themeId, deleteSettings) {
|
||
this.themeDeleteModal.deleting = true
|
||
themeApi
|
||
.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() {
|
||
this.$refs.remoteInstallForm.validate(valid => {
|
||
if (valid) {
|
||
this.installModal.remote.fetching = true
|
||
themeApi
|
||
.fetching(this.installModal.remote.url)
|
||
.catch(() => {
|
||
this.installModal.remote.fetchErrored = true
|
||
})
|
||
.finally(() => {
|
||
setTimeout(() => {
|
||
this.installModal.remote.fetching = false
|
||
}, 400)
|
||
})
|
||
}
|
||
})
|
||
},
|
||
handleRemoteFetchCallback() {
|
||
if (this.installModal.remote.fetchErrored) {
|
||
this.installModal.remote.fetchErrored = false
|
||
} else {
|
||
this.installModal.visible = false
|
||
this.handleListThemes()
|
||
}
|
||
},
|
||
handleOpenLocalUpdateModal(item) {
|
||
this.localUpdateModel.selected = item
|
||
this.localUpdateModel.visible = true
|
||
},
|
||
handleOpenThemeSettingDrawer(theme) {
|
||
this.themeSettingDrawer.selected = theme
|
||
this.themeSettingDrawer.visible = true
|
||
},
|
||
handleOpenThemeDeleteModal(item) {
|
||
this.themeDeleteModal.visible = true
|
||
this.themeDeleteModal.selected = item
|
||
},
|
||
handleConfirmRemoteUpdate(item) {
|
||
const _this = this
|
||
_this.$confirm({
|
||
title: '提示',
|
||
maskClosable: true,
|
||
content: '确定更新【' + item.name + '】主题?',
|
||
onOk() {
|
||
const hide = _this.$message.loading('更新中...', 0)
|
||
themeApi
|
||
.update(item.id)
|
||
.then(() => {
|
||
_this.$message.success('更新成功!')
|
||
})
|
||
.finally(() => {
|
||
hide()
|
||
_this.handleListThemes()
|
||
})
|
||
},
|
||
onCancel() {}
|
||
})
|
||
},
|
||
onThemeInstallModalClose() {
|
||
if (this.$refs.upload) {
|
||
this.$refs.upload.handleClearFileList()
|
||
}
|
||
if (this.$refs.updateByupload) {
|
||
this.$refs.updateByupload.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 = {}
|
||
}
|
||
}
|
||
}
|
||
</script>
|