mirror of https://github.com/halo-dev/halo
366 lines
9.7 KiB
Vue
366 lines
9.7 KiB
Vue
<template>
|
||
<div class="page-header-index-wide">
|
||
<a-row
|
||
:gutter="12"
|
||
type="flex"
|
||
align="middle"
|
||
>
|
||
<a-col
|
||
class="theme-item"
|
||
:xl="6"
|
||
:lg="6"
|
||
:md="12"
|
||
:sm="12"
|
||
:xs="24"
|
||
v-for="(theme, index) in themes"
|
||
:key="index"
|
||
>
|
||
<a-card
|
||
hoverable
|
||
:title="theme.name"
|
||
>
|
||
<img
|
||
:alt="theme.name"
|
||
:src="theme.screenshots"
|
||
slot="cover"
|
||
/>
|
||
<template
|
||
class="ant-card-actions"
|
||
slot="actions"
|
||
>
|
||
<div v-if="theme.activated">
|
||
<a-icon
|
||
type="unlock"
|
||
theme="twoTone"
|
||
/>
|
||
激活中
|
||
</div>
|
||
<div
|
||
v-else
|
||
@click="handleActivateClick(theme)"
|
||
>
|
||
<a-icon type="lock" />
|
||
激活
|
||
</div>
|
||
<div @click="handleEditClick(theme)">
|
||
<a-icon type="setting" />
|
||
设置
|
||
</div>
|
||
<a-dropdown placement="topCenter">
|
||
<a
|
||
class="ant-dropdown-link"
|
||
href="#"
|
||
>
|
||
<a-icon type="ellipsis" /> 更多
|
||
</a>
|
||
<a-menu slot="overlay">
|
||
<a-menu-item
|
||
:key="1"
|
||
:disabled="theme.activated"
|
||
>
|
||
<a-popconfirm
|
||
v-if="!theme.activated"
|
||
:title="'确定删除【' + theme.name + '】主题?'"
|
||
@confirm="deleteTheme(theme.id)"
|
||
okText="确定"
|
||
cancelText="取消"
|
||
>
|
||
<a-icon type="delete" />
|
||
删除
|
||
</a-popconfirm>
|
||
<span v-else>
|
||
<a-icon type="delete" />
|
||
删除
|
||
</span>
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</a-dropdown>
|
||
</template>
|
||
</a-card>
|
||
</a-col>
|
||
|
||
</a-row>
|
||
<a-drawer
|
||
v-if="themeProperty"
|
||
:title="themeProperty.name + ' 主题设置'"
|
||
width="100%"
|
||
closable
|
||
@close="onClose"
|
||
:visible="visible"
|
||
destroyOnClose
|
||
>
|
||
<a-row
|
||
:gutter="12"
|
||
type="flex"
|
||
>
|
||
<a-col
|
||
:xl="12"
|
||
:lg="12"
|
||
:md="12"
|
||
:sm="24"
|
||
:xs="24"
|
||
>
|
||
<a-skeleton
|
||
active
|
||
:loading="optionLoading"
|
||
:paragraph="{rows: 10}"
|
||
>
|
||
<a-card hoverable>
|
||
<img
|
||
:alt="themeProperty.name"
|
||
:src="themeProperty.screenshots"
|
||
slot="cover"
|
||
/>
|
||
<a-card-meta
|
||
:title="themeProperty.name"
|
||
:description="themeProperty.description"
|
||
>
|
||
<a-avatar
|
||
v-if="themeProperty.author.avatar"
|
||
:src="themeProperty.author.avatar"
|
||
size="large"
|
||
slot="avatar"
|
||
/>
|
||
<a-avatar
|
||
v-else
|
||
size="large"
|
||
slot="avatar"
|
||
>{{ themeProperty.author.name }}</a-avatar>
|
||
</a-card-meta>
|
||
</a-card>
|
||
</a-skeleton>
|
||
</a-col>
|
||
<a-col
|
||
:xl="12"
|
||
:lg="12"
|
||
:md="12"
|
||
:sm="24"
|
||
:xs="24"
|
||
>
|
||
<a-skeleton
|
||
active
|
||
:loading="optionLoading"
|
||
:paragraph="{rows: 20}"
|
||
>
|
||
<a-tabs defaultActiveKey="0">
|
||
<a-tab-pane
|
||
v-for="(group, index) in themeConfiguration"
|
||
:key="index.toString()"
|
||
:tab="group.label"
|
||
>
|
||
<a-form layout="vertical">
|
||
<a-form-item
|
||
v-for="(item, index1) in group.items"
|
||
:label="item.label + ':'"
|
||
:key="index1"
|
||
:wrapper-col="wrapperCol"
|
||
>
|
||
<a-input
|
||
v-model="themeSettings[item.name]"
|
||
:defaultValue="item.defaultValue"
|
||
v-if="item.type == 'TEXT'"
|
||
/>
|
||
<a-input
|
||
type="textarea"
|
||
:autosize="{ minRows: 5 }"
|
||
v-model="themeSettings[item.name]"
|
||
v-else-if="item.type == 'TEXTAREA'"
|
||
/>
|
||
<a-radio-group
|
||
v-decorator="['radio-group']"
|
||
:defaultValue="item.defaultValue"
|
||
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-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>
|
||
</a-form-item>
|
||
<a-form-item>
|
||
<a-button
|
||
type="primary"
|
||
@click="saveSettings"
|
||
>保存</a-button>
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</a-skeleton>
|
||
</a-col>
|
||
</a-row>
|
||
</a-drawer>
|
||
<div class="upload-button">
|
||
<a-button
|
||
type="primary"
|
||
shape="circle"
|
||
icon="plus"
|
||
size="large"
|
||
@click="showUploadModal"
|
||
></a-button>
|
||
</div>
|
||
<a-modal
|
||
title="上传主题"
|
||
v-model="uploadVisible"
|
||
:footer="null"
|
||
>
|
||
<a-upload-dragger
|
||
name="file"
|
||
:multiple="true"
|
||
action="http://localhost:8090/admin/api/attachments/uploads"
|
||
@change="handleChange"
|
||
accept=".zip"
|
||
>
|
||
<p class="ant-upload-drag-icon">
|
||
<a-icon type="inbox" />
|
||
</p>
|
||
<p class="ant-upload-text">点击选择主题或将主题拖拽到此处</p>
|
||
<p class="ant-upload-hint">支持单个或批量上传,仅支持 ZIP 格式的文件</p>
|
||
</a-upload-dragger>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import themeApi from '@/api/theme'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
optionLoading: true,
|
||
uploadVisible: false,
|
||
wrapperCol: {
|
||
xl: { span: 12 },
|
||
lg: { span: 12 },
|
||
sm: { span: 24 },
|
||
xs: { span: 24 }
|
||
},
|
||
themes: [],
|
||
visible: false,
|
||
themeConfiguration: null,
|
||
themeSettings: [],
|
||
themeProperty: null
|
||
}
|
||
},
|
||
computed: {
|
||
activatedTheme() {
|
||
return this.themes.find(theme => theme.activated)
|
||
}
|
||
},
|
||
created() {
|
||
this.loadThemes()
|
||
},
|
||
methods: {
|
||
loadThemes() {
|
||
themeApi.listAll().then(response => {
|
||
this.themes = response.data.data
|
||
})
|
||
},
|
||
settingDrawer(theme) {
|
||
this.visible = true
|
||
this.optionLoading = true
|
||
this.themeProperty = theme
|
||
|
||
setTimeout(() => {
|
||
themeApi.fetchConfiguration(theme.id).then(response => {
|
||
this.themeConfiguration = response.data.data
|
||
themeApi.fetchSettings().then(response => {
|
||
this.themeSettings = response.data.data
|
||
this.visible = true
|
||
this.optionLoading = false
|
||
})
|
||
})
|
||
}, 300)
|
||
},
|
||
activeTheme(themeId) {
|
||
themeApi.active(themeId).then(response => {
|
||
this.$message.success('设置成功!')
|
||
this.loadThemes()
|
||
})
|
||
},
|
||
deleteTheme(key) {
|
||
themeApi.delete(key).then(response => {
|
||
this.$message.success('删除成功!')
|
||
this.loadThemes()
|
||
})
|
||
},
|
||
saveSettings() {
|
||
themeApi.saveSettings(this.themeSettings).then(response => {
|
||
this.$message.success('保存成功!')
|
||
})
|
||
},
|
||
getThemeProperty(themeId) {
|
||
themeApi.getProperty(themeId).then(response => {
|
||
this.themeProperty = response.data.data
|
||
})
|
||
},
|
||
onClose() {
|
||
this.visible = false
|
||
this.optionLoading = false
|
||
this.themeConfiguration = null
|
||
this.themeProperty = null
|
||
},
|
||
showUploadModal() {
|
||
this.uploadVisible = true
|
||
},
|
||
handleChange(info) {
|
||
const status = info.file.status
|
||
if (status !== 'uploading') {
|
||
console.log(info.file, info.fileList)
|
||
}
|
||
if (status === 'done') {
|
||
this.$message.success(`${info.file.name} file uploaded successfully.`)
|
||
} else if (status === 'error') {
|
||
this.$message.error(`${info.file.name} file upload failed.`)
|
||
}
|
||
},
|
||
handleEllipsisClick(theme) {
|
||
this.$log.debug('Ellipsis clicked', theme)
|
||
},
|
||
handleEditClick(theme) {
|
||
this.settingDrawer(theme)
|
||
},
|
||
handleActivateClick(theme) {
|
||
this.activeTheme(theme.id)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.ant-divider-horizontal {
|
||
margin: 14px 0;
|
||
}
|
||
|
||
.theme-item {
|
||
padding-bottom: 12px;
|
||
}
|
||
|
||
.theme-item .theme-control .theme-title {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.theme-item .theme-control .theme-button {
|
||
float: right;
|
||
}
|
||
|
||
.upload-button {
|
||
position: fixed;
|
||
bottom: 80px;
|
||
right: 20px;
|
||
}
|
||
</style>
|