Merge pull request #62 from halo-dev/dev

sync
pull/64/head
Ryan Wang 2020-02-16 15:49:21 +08:00 committed by GitHub
commit ea6d157df3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1020 additions and 296 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
NODE_ENV=production
PUBLIC_PATH=https://cdn.jsdelivr.net/npm/halo-admin@1.2.0/dist/

2
.env.development Normal file
View File

@ -0,0 +1,2 @@
NODE_ENV=development
PUBLIC_PATH=/

View File

@ -124,4 +124,23 @@ postApi.postStatus = {
text: '私密'
}
}
postApi.permalinkType = {
DEFAULT: {
type: 'DEFAULT',
text: '默认'
},
DATE: {
type: 'DATE',
text: '年月型'
},
DAY: {
type: 'DAY',
text: '年月日型'
},
ID: {
type: 'ID',
text: 'ID 型'
}
}
export default postApi

39
src/api/staticPage.js Normal file
View File

@ -0,0 +1,39 @@
import service from '@/utils/service'
const baseUrl = '/api/admin/static_page'
const staticPageApi = {}
staticPageApi.list = () => {
return service({
url: baseUrl,
method: 'get'
})
}
staticPageApi.generate = () => {
return service({
url: `${baseUrl}/generate`,
method: 'get'
})
}
staticPageApi.deploy = () => {
return service({
url: `${baseUrl}/deploy`,
method: 'get'
})
}
staticPageApi.deployType = {
GIT: {
type: 'GIT',
text: 'Git'
},
NETLIFY: {
type: 'NETLIFY',
text: 'Netlify'
}
}
export default staticPageApi

View File

@ -79,7 +79,7 @@ export default {
const subItem = [h('span', { slot: 'title' }, [this.renderIcon(h, menu.meta.icon), h('span', [menu.meta.title])])]
const itemArr = []
const pIndex_ = pIndex + '_' + index
console.log('menu', menu)
this.$log.debug('menu', menu)
if (!menu.hideChildrenInMenu) {
menu.children.forEach(function(item, i) {
itemArr.push(this2_.renderItem(h, item, pIndex_, i))

View File

@ -120,7 +120,7 @@ export default {
},
methods: {
handleFilePondInit() {
console.log('FilePond has initialized')
this.$log.debug('FilePond has initialized')
},
handleClearFileList() {
this.$refs.pond.removeFiles()

View File

@ -911,4 +911,8 @@ body {
img {
width: 100%;
}
}
.ant-input-group-addon {
line-height: initial !important;
}

View File

@ -185,6 +185,13 @@ export const asyncRouterMap = [
component: () => import('@/views/system/ToolList'),
meta: { title: '小工具', hiddenHeaderContent: false }
},
{
path: '/system/tools/staticpages',
name: 'StaticPagesManage',
hidden: true,
component: () => import('@/views/system/staticpages/StaticPagesManage'),
meta: { title: '静态部署', hiddenHeaderContent: false }
},
{
path: '/system/about',
name: 'About',

View File

@ -8,7 +8,11 @@ const keys = [
'developer_mode',
'attachment_upload_image_preview_enable',
'attachment_upload_max_parallel_uploads',
'attachment_upload_max_files'
'attachment_upload_max_files',
'sheet_prefix',
'post_permalink_type',
'archives_prefix',
'path_suffix'
]
const option = {
state: {

View File

@ -22,6 +22,14 @@ Vue.filter('moment', function(dataStr, pattern = 'YYYY-MM-DD HH:mm') {
return moment(dataStr).format(pattern)
})
Vue.filter('moment_post_date', function(dataStr, pattern = '/YYYY/M/') {
return moment(dataStr).format(pattern)
})
Vue.filter('moment_post_day', function(dataStr, pattern = '/YYYY/M/D/') {
return moment(dataStr).format(pattern)
})
Vue.filter('timeAgo', timeAgo)
Vue.filter('fileSizeFormat', function(value) {

View File

@ -2,7 +2,7 @@ export function actionToObject(json) {
try {
return JSON.parse(json)
} catch (e) {
console.log('err', e.message)
this.$log.debug('err', e.message)
}
return []
}

View File

@ -269,11 +269,11 @@ export default {
const text = `${encodeURI(this.attachment.path)}`
this.$copyText(text)
.then(message => {
console.log('copy', message)
this.$log.debug('copy', message)
this.$message.success('复制成功!')
})
.catch(err => {
console.log('copy.err', err)
this.$log.debug('copy.err', err)
this.$message.error('复制失败!')
})
},
@ -281,11 +281,11 @@ export default {
const text = `![${this.attachment.name}](${encodeURI(this.attachment.path)})`
this.$copyText(text)
.then(message => {
console.log('copy', message)
this.$log.debug('copy', message)
this.$message.success('复制成功!')
})
.catch(err => {
console.log('copy.err', err)
this.$log.debug('copy.err', err)
this.$message.error('复制失败!')
})
},

View File

@ -56,13 +56,13 @@
<a
slot="description"
target="_blank"
:href="options.blog_url+'/archives/'+comment.post.url"
:href="comment.post.fullPath"
v-if="this.type=='posts'"
>{{ comment.post.title }}</a>
<a
slot="description"
target="_blank"
:href="options.blog_url+'/s/'+comment.sheet.url"
:href="comment.sheet.fullPath"
v-else-if="this.type=='sheets'"
>{{ comment.sheet.title }}</a>
<span
@ -125,7 +125,6 @@
</template>
<script>
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { mapGetters } from 'vuex'
import commentApi from '@/api/comment'
export default {
name: 'CommentDetail',
@ -136,7 +135,6 @@ export default {
detailLoading: true,
editable: false,
commentStatus: commentApi.commentStatus,
options: [],
keys: ['blog_url']
}
},
@ -163,9 +161,6 @@ export default {
}
}
},
computed: {
...mapGetters(['options'])
},
watch: {
visible: function(newValue, oldValue) {
this.$log.debug('old value', oldValue)

View File

@ -187,12 +187,12 @@
发表在
<a
v-if="type==='posts'"
:href="options.blog_url+'/archives/'+item.post.url"
:href="item.post.fullPath"
target="_blank"
>{{ item.post.title }}</a>
<a
v-if="type === 'sheets'"
:href="options.blog_url+'/s/'+item.sheet.url"
:href="item.sheet.fullPath"
target="_blank"
>{{ item.sheet.title }}</a>
</template>
@ -282,14 +282,14 @@
v-if="type==='posts'"
slot="post"
slot-scope="post"
:href="options.blog_url+'/archives/'+post.url"
:href="post.fullPath"
target="_blank"
>{{ post.title }}</a>
<a
v-if="type === 'sheets'"
slot="sheet"
slot-scope="sheet"
:href="options.blog_url+'/s/'+sheet.url"
:href="sheet.fullPath"
target="_blank"
>{{ sheet.title }}</a>
<span
@ -429,7 +429,6 @@
</template>
<script>
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { mapGetters } from 'vuex'
import CommentDetail from './CommentDetail'
import marked from 'marked'
import commentApi from '@/api/comment'
@ -561,8 +560,7 @@ export default {
comment.content = marked(comment.content)
return comment
})
},
...mapGetters(['options'])
}
},
methods: {
loadComments() {

View File

@ -124,7 +124,7 @@
<a
v-if="item.status=='PUBLISHED' || item.status == 'INTIMATE'"
slot="title"
:href="options.blog_url+'/archives/'+item.url"
:href="item.fullPath"
target="_blank"
>{{ item.title }}</a>
<a
@ -322,7 +322,6 @@
<script>
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { mapGetters } from 'vuex'
import { PageView } from '@/layouts'
import AnalysisCard from './components/AnalysisCard'
import RecentCommentTab from './components/RecentCommentTab'
@ -396,8 +395,7 @@ export default {
log.type = this.logType[log.type].text
return log
})
},
...mapGetters(['options'])
}
},
destroyed: function() {
if (this.logDrawerVisible) {

View File

@ -19,7 +19,7 @@
target="_blank"
>{{ item.author }}</a> 发表在 <a
v-if="item.post.status=='PUBLISHED' || item.post.status=='INTIMATE'"
:href="options.blog_url+'/archives/'+item.post.url"
:href="item.post.fullPath"
target="_blank"
>{{ item.post.title }}</a><a
v-else-if="item.post.status=='DRAFT'"
@ -40,7 +40,7 @@
target="_blank"
>{{ item.author }}</a> 发表在 <a
v-if="item.sheet.status=='PUBLISHED'"
:href="options.blog_url+'/s/'+item.sheet.url"
:href="item.sheet.fullPath"
target="_blank"
>{{ item.sheet.title }}</a><a
v-else-if="item.sheet.status=='DRAFT'"
@ -71,7 +71,6 @@
</template>
<script>
import { mapGetters } from 'vuex'
import commentApi from '@/api/comment'
import postApi from '@/api/post'
import sheetApi from '@/api/sheet'
@ -101,8 +100,7 @@ export default {
comment.content = marked(comment.content)
return comment
})
},
...mapGetters(['options'])
}
},
created() {
this.loadComments()

View File

@ -32,6 +32,20 @@
v-model="categoryToCreate.parentId"
/>
</a-form-item>
<a-form-item
label="封面图"
help="* 在分类页面可展示,需要主题支持"
>
<a-input v-model="categoryToCreate.thumbnail">
<a
href="javascript:void(0);"
slot="addonAfter"
@click="()=>this.thumbnailDrawerVisible = true"
>
<a-icon type="picture" />
</a>
</a-input>
</a-form-item>
<a-form-item
label="描述:"
help="* 分类描述,部分主题可显示"
@ -206,12 +220,19 @@
</a-card>
</a-col>
</a-row>
<AttachmentSelectDrawer
v-model="thumbnailDrawerVisible"
@listenToSelect="handleSelectThumbnail"
title="选择封面图"
/>
</div>
</template>
<script>
import { mixin, mixinDevice } from '@/utils/mixin.js'
import CategorySelectTree from './components/CategorySelectTree'
import AttachmentSelectDrawer from '../attachment/components/AttachmentSelectDrawer'
import categoryApi from '@/api/category'
import menuApi from '@/api/menu'
@ -239,13 +260,14 @@ const columns = [
}
]
export default {
components: { CategorySelectTree },
components: { CategorySelectTree, AttachmentSelectDrawer },
mixins: [mixin, mixinDevice],
data() {
return {
formType: 'create',
categories: [],
categoryToCreate: {},
thumbnailDrawerVisible: false,
menu: {},
loading: false,
columns
@ -311,11 +333,15 @@ export default {
},
handleCategoryToMenu(category) {
this.menu['name'] = category.name
this.menu['url'] = `/categories/${category.slugName}`
this.menu['url'] = `${category.fullPath}`
menuApi.create(this.menu).then(response => {
this.$message.success('添加到菜单成功!')
this.menu = {}
})
},
handleSelectThumbnail(data) {
this.$set(this.categoryToCreate, 'thumbnail', encodeURI(data.path))
this.thumbnailDrawerVisible = false
}
}
}

View File

@ -237,7 +237,7 @@
/>
<a
v-if="item.status=='PUBLISHED' || item.status == 'INTIMATE'"
:href="options.blog_url+'/archives/'+item.url"
:href="item.fullPath"
target="_blank"
style="text-decoration: none;"
>
@ -318,7 +318,7 @@
/>
<a
v-if="record.status=='PUBLISHED' || record.status == 'INTIMATE'"
:href="options.blog_url+'/archives/'+record.url"
:href="record.fullPath"
target="_blank"
style="text-decoration: none;"
>
@ -518,7 +518,6 @@ import TargetCommentDrawer from '../comment/components/TargetCommentDrawer'
import AttachmentSelectDrawer from '../attachment/components/AttachmentSelectDrawer'
import TagSelect from './components/TagSelect'
import CategoryTree from './components/CategoryTree'
import { mapGetters } from 'vuex'
import categoryApi from '@/api/category'
import postApi from '@/api/post'
const columns = [
@ -620,8 +619,7 @@ export default {
post.statusProperty = this.postStatus[post.status]
return post
})
},
...mapGetters(['options'])
}
},
created() {
this.loadPosts()

View File

@ -9,7 +9,10 @@
:xs="24"
:style="{ 'padding-bottom': '12px' }"
>
<a-card :title="title" :bodyStyle="{ padding: '16px' }">
<a-card
:title="title"
:bodyStyle="{ padding: '16px' }"
>
<a-form layout="horizontal">
<a-form-item
label="名称:"
@ -23,6 +26,20 @@
>
<a-input v-model="tagToCreate.slugName" />
</a-form-item>
<a-form-item
label="封面图"
help="* 在标签页面可展示,需要主题支持"
>
<a-input v-model="tagToCreate.thumbnail">
<a
href="javascript:void(0);"
slot="addonAfter"
@click="()=>this.thumbnailDrawerVisible = true"
>
<a-icon type="picture" />
</a>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type="primary"
@ -64,8 +81,11 @@
:xs="24"
:style="{ 'padding-bottom': '12px' }"
>
<a-card title="所有标签" :bodyStyle="{ padding: '16px' }">
<a-empty v-if="tags.length==0"/>
<a-card
title="所有标签"
:bodyStyle="{ padding: '16px' }"
>
<a-empty v-if="tags.length==0" />
<a-tooltip
placement="topLeft"
v-for="tag in tags"
@ -84,18 +104,27 @@
</a-card>
</a-col>
</a-row>
<AttachmentSelectDrawer
v-model="thumbnailDrawerVisible"
@listenToSelect="handleSelectThumbnail"
title="选择封面图"
/>
</div>
</template>
<script>
import tagApi from '@/api/tag'
import AttachmentSelectDrawer from '../attachment/components/AttachmentSelectDrawer'
export default {
components: { AttachmentSelectDrawer },
data() {
return {
formType: 'create',
tags: [],
tagToCreate: {}
tagToCreate: {},
thumbnailDrawerVisible: false
}
},
computed: {
@ -155,6 +184,10 @@ export default {
})
}
this.handleAddTag()
},
handleSelectThumbnail(data) {
this.$set(this.tagToCreate, 'thumbnail', encodeURI(data.path))
this.thumbnailDrawerVisible = false
}
}
}

View File

@ -24,10 +24,13 @@
>
<a-input v-model="selectedPost.title" />
</a-form-item>
<a-form-item
label="文章路径:"
:help="options.blog_url+'/archives/' + (selectedPost.url ? selectedPost.url : '{auto_generate}')"
>
<a-form-item label="文章路径:">
<template slot="help">
<span v-if="options.post_permalink_type === 'DEFAULT'">{{ options.blog_url }}/{{ options.archives_prefix }}/{{ selectedPost.url?selectedPost.url:'${url}' }}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'DATE'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_date }}{{ selectedPost.url?selectedPost.url:'${url}' }}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'DAY'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_day }}{{ selectedPost.url?selectedPost.url:'${url}' }}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p={{ selectedPost.id?selectedPost.id:'${id}' }}</span>
</template>
<a-input v-model="selectedPost.url" />
</a-form-item>
<a-form-item label="访问密码:">
@ -164,7 +167,7 @@
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">缩略</h3>
<h3 class="post-setting-drawer-title">封面</h3>
<div class="post-setting-drawer-item">
<div class="post-thumb">
<img
@ -177,7 +180,7 @@
<a-form-item>
<a-input
v-model="selectedPost.thumbnail"
placeholder="点击缩略图选择图片,或者输入外部链接"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>

View File

@ -62,12 +62,12 @@
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<a
:href="options.blog_url+item.url"
:href="item.url"
target="_blank"
v-if="item.status"
>{{ item.title }}</a>
<a
:href="options.blog_url+item.url"
:href="item.url"
target="_blank"
disabled
v-else
@ -124,12 +124,12 @@
</router-link>
<a-divider type="vertical" />
<a
:href="options.blog_url+record.url"
:href="record.url"
target="_blank"
v-if="record.status"
>访问</a>
<a
:href="options.blog_url+record.url"
:href="record.url"
target="_blank"
disabled
v-else
@ -247,7 +247,7 @@
>
<a
v-if="item.status=='PUBLISHED'"
:href="options.blog_url+'/archives/'+item.url"
:href="item.fullPath"
target="_blank"
style="text-decoration: none;"
>
@ -298,7 +298,7 @@
>
<a
v-if="record.status=='PUBLISHED'"
:href="options.blog_url+'/s/'+record.url"
:href="record.fullPath"
target="_blank"
style="text-decoration: none;"
>
@ -497,7 +497,7 @@ const internalColumns = [
dataIndex: 'title'
},
{
title: '访问路径',
title: '访问地址',
dataIndex: 'url'
},
{
@ -637,7 +637,7 @@ export default {
},
handleSheetToMenu(sheet) {
this.menu['name'] = sheet.title
this.menu['url'] = `/s/${sheet.url}`
this.menu['url'] = `${sheet.fullPath}`
menuApi.create(this.menu).then(response => {
this.$message.success('添加到菜单成功!')
this.menu = {}

View File

@ -26,7 +26,7 @@
</a-form-item>
<a-form-item
label="页面路径:"
:help="options.blog_url+'/s/'+ (selectedSheet.url ? selectedSheet.url : '{auto_generate}')"
:help="options.blog_url+'/'+options.sheet_prefix+'/'+ (selectedSheet.url ? selectedSheet.url : '{auto_generate}')"
>
<a-input v-model="selectedSheet.url" />
</a-form-item>
@ -85,7 +85,7 @@
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">缩略</h3>
<h3 class="post-setting-drawer-title">封面</h3>
<div class="post-setting-drawer-item">
<div class="sheet-thumb">
<img
@ -98,7 +98,7 @@
<a-form-item>
<a-input
v-model="selectedSheet.thumbnail"
placeholder="点击缩略图选择图片,或者输入外部链接"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>

View File

@ -126,7 +126,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
@ -154,6 +157,40 @@
</a-col>
</a-row>
<div style="position: fixed;bottom: 30px;right: 30px;">
<a-button
type="primary"
shape="circle"
icon="setting"
size="large"
@click="()=>this.optionFormVisible=true"
></a-button>
</div>
<a-modal
v-model="optionFormVisible"
title="页面设置"
:afterClose="onOptionFormClose"
>
<template slot="footer">
<a-button
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template>
<a-form layout="vertical">
<a-form-item label="页面标题:" help="* 需要主题进行适配">
<a-input v-model="options.journals_title" />
</a-form-item>
<a-form-item label="每页显示条数:">
<a-input
type="number"
v-model="options.journals_page_size"
/>
</a-form-item>
</a-form>
</a-modal>
<!-- 编辑日志弹窗 -->
<a-modal v-model="visible">
<template slot="title">
@ -211,9 +248,10 @@
import TargetCommentDrawer from '../../comment/components/TargetCommentDrawer'
import AttachmentDrawer from '../../attachment/components/AttachmentDrawer'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { mapGetters } from 'vuex'
import { mapGetters, mapActions } from 'vuex'
import journalApi from '@/api/journal'
import journalCommentApi from '@/api/journalComment'
import optionApi from '@/api/option'
export default {
mixins: [mixin, mixinDevice],
components: { TargetCommentDrawer, AttachmentDrawer },
@ -225,6 +263,7 @@ export default {
visible: false,
journalCommentVisible: false,
attachmentDrawerVisible: false,
optionFormVisible: false,
pagination: {
page: 1,
size: 10,
@ -241,16 +280,19 @@ export default {
comments: [],
journal: {},
isPublic: true,
replyComment: {}
replyComment: {},
options: []
}
},
created() {
this.loadJournals()
this.loadFormOptions()
},
computed: {
...mapGetters(['user'])
},
methods: {
...mapActions(['loadOptions']),
loadJournals() {
this.listLoading = true
this.queryParam.page = this.pagination.page - 1
@ -262,6 +304,11 @@ export default {
this.listLoading = false
})
},
loadFormOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
},
@ -332,6 +379,17 @@ export default {
this.queryParam.keyword = null
this.queryParam.type = null
this.handlePaginationChange(1, this.pagination.size)
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
this.optionFormVisible = false
})
},
onOptionFormClose() {
this.optionFormVisible = false
}
}
}

View File

@ -195,11 +195,40 @@
</a-card>
</a-col>
</a-row>
<div style="position: fixed;bottom: 30px;right: 30px;">
<a-button
type="primary"
shape="circle"
icon="setting"
size="large"
@click="()=>this.optionFormVisible=true"
></a-button>
</div>
<a-modal
v-model="optionFormVisible"
title="页面设置"
:afterClose="onOptionFormClose"
>
<template slot="footer">
<a-button
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template>
<a-form layout="vertical">
<a-form-item label="页面标题:" help="* 需要主题进行适配">
<a-input v-model="options.links_title" />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import optionApi from '@/api/option'
import linkApi from '@/api/link'
const columns = [
{
@ -231,12 +260,14 @@ export default {
data() {
return {
formType: 'create',
optionFormVisible: false,
data: [],
loading: false,
columns,
links: [],
link: {},
teams: []
teams: [],
options: []
}
},
computed: {
@ -250,8 +281,10 @@ export default {
created() {
this.loadLinks()
this.loadTeams()
this.loadFormOptions()
},
methods: {
...mapActions(['loadOptions']),
loadLinks() {
this.loading = true
linkApi.listAll().then(response => {
@ -264,6 +297,11 @@ export default {
this.teams = response.data.data
})
},
loadFormOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
},
handleSaveClick() {
this.createOrUpdateLink()
},
@ -318,6 +356,17 @@ export default {
})
}
this.handleAddLink()
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
this.optionFormVisible = false
})
},
onOptionFormClose() {
this.optionFormVisible = false
}
}
}

View File

@ -59,7 +59,10 @@
</a-row>
</a-form>
</div>
<div class="table-operator" style="margin-bottom: 0;">
<div
class="table-operator"
style="margin-bottom: 0;"
>
<a-button
type="primary"
icon="plus"
@ -85,7 +88,10 @@
@click="showDrawer(item)"
>
<div class="photo-thumb">
<img :src="item.thumbnail" loading="lazy">
<img
:src="item.thumbnail"
loading="lazy"
>
</div>
<a-card-meta style="padding: 0.8rem;">
<ellipsis
@ -110,6 +116,39 @@
@showSizeChange="handlePaginationChange"
/>
</div>
<div style="position: fixed;bottom: 30px;right: 30px;">
<a-button
type="primary"
shape="circle"
icon="setting"
size="large"
@click="()=>this.optionFormVisible=true"
></a-button>
</div>
<a-modal
v-model="optionFormVisible"
title="页面设置"
:afterClose="onOptionFormClose"
>
<template slot="footer">
<a-button
key="submit"
type="primary"
@click="handleSaveOptions()"
>保存</a-button>
</template>
<a-form layout="vertical">
<a-form-item label="页面标题:" help="* 需要主题进行适配">
<a-input v-model="options.photos_title" />
</a-form-item>
<a-form-item label="每页显示条数:">
<a-input
type="number"
v-model="options.photos_page_size"
/>
</a-form-item>
</a-form>
</a-modal>
<a-drawer
title="图片详情"
:width="isMobile()?'100%':'460'"
@ -137,7 +176,7 @@
</div>
</a-skeleton>
</a-col>
<a-divider style="margin: 24px 0 12px 0;"/>
<a-divider style="margin: 24px 0 12px 0;" />
<a-col :span="24">
<a-skeleton
@ -289,9 +328,11 @@
</template>
<script>
import { mapActions } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import AttachmentSelectDrawer from '../../attachment/components/AttachmentSelectDrawer'
import photoApi from '@/api/photo'
import optionApi from '@/api/option'
export default {
components: {
@ -304,6 +345,7 @@ export default {
drawerLoading: false,
listLoading: true,
thumDrawerVisible: false,
optionFormVisible: false,
photo: {},
photos: [],
teams: [],
@ -319,14 +361,17 @@ export default {
sort: null,
keyword: null,
team: null
}
},
options: []
}
},
created() {
this.loadPhotos()
this.loadTeams()
this.loadFormOptions()
},
methods: {
...mapActions(['loadOptions']),
loadPhotos() {
this.listLoading = true
this.queryParam.page = this.pagination.page - 1
@ -338,6 +383,11 @@ export default {
this.listLoading = false
})
},
loadFormOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
},
@ -406,6 +456,17 @@ export default {
this.drawerVisible = false
this.photo = {}
this.editable = false
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
this.optionFormVisible = false
})
},
onOptionFormClose() {
this.optionFormVisible = false
}
}
}

View File

@ -20,7 +20,7 @@
<a-icon type="copy" />
</a>
</template>
<a-popconfirm
<!-- <a-popconfirm
slot="extra"
placement="left"
okText="确定"
@ -41,7 +41,7 @@
icon="cloud-download"
>
</a-button>
</a-popconfirm>
</a-popconfirm> -->
<ul style="margin: 0;padding: 0;list-style: none;">
<li>Server 版本{{ environments.version }}</li>
@ -55,10 +55,10 @@
href="https://github.com/halo-dev"
target="_blank"
style="margin-right: 10px;"
>开源地址
>开源组织
<a-icon type="link" /></a>
<a
href="https://halo.run/guide"
href="https://halo.run"
target="_blank"
style="margin-right: 10px;"
>用户文档
@ -75,39 +75,26 @@
title="开发者"
:bordered="false"
:bodyStyle="{ padding: '16px' }"
:loading="contributorsLoading"
>
<a
:href="item.github"
v-for="(item,index) in developers"
:href="item.html_url"
v-for="(item,index) in contributors"
:key="index"
target="_blank"
>
<a-tooltip
placement="top"
:title="item.name"
:title="item.login"
>
<a-avatar
size="large"
:src="item.avatar"
:style="{ marginRight: '10px' }"
:src="item.avatar_url"
:style="{ marginRight: '10px',marginBottom: '10px'}"
/>
</a-tooltip>
</a>
</a-card>
<a-card
title="时间轴"
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<a-timeline>
<a-timeline-item>...</a-timeline-item>
<a-timeline-item
v-for="(item, index) in steps"
:key="index"
>{{ item.date }} {{ item.content }}</a-timeline-item>
</a-timeline>
</a-card>
</a-card>
</a-col>
</a-row>
@ -122,104 +109,37 @@ export default {
return {
adminVersion: this.VERSION,
environments: {},
developers: [
contributors: [
{
name: 'Ryan Wang',
avatar: '//cn.gravatar.com/avatar/7cc7f29278071bd4dce995612d428834?s=256&d=mm',
website: 'https://ryanc.cc',
github: 'https://github.com/ruibaby'
},
{
name: 'John Niang',
avatar: '//cn.gravatar.com/avatar/1dcf60ef27363dae539385d5bae9b2bd?s=256&d=mm',
website: 'https://johnniang.me',
github: 'https://github.com/johnniang'
},
{
name: 'Aquan',
avatar: '//cn.gravatar.com/avatar/3958035fa354403fa9ca3fca36b08068?s=256&d=mm',
website: 'https://blog.eunji.cn',
github: 'https://github.com/aquanlerou'
},
{
name: 'appdev',
avatar: '//cn.gravatar.com/avatar/08cf681fb7c6ad1b4fe70a8269c2103c?s=256&d=mm',
website: 'https://www.apkdv.com',
github: 'https://github.com/appdev'
},
{
name: 'guqing',
avatar: '//cn.gravatar.com/avatar/ad062ba572c8b006bfd2cbfc43fdee5e?s=256&d=mm',
website: 'http://www.guqing.xyz',
github: 'https://github.com/guqing'
login: '',
id: 0,
node_id: '',
avatar_url: '',
gravatar_id: '',
url: '',
html_url: '',
followers_url: '',
following_url: '',
gists_url: '',
starred_url: '',
subscriptions_url: '',
organizations_url: '',
repos_url: '',
events_url: '',
received_events_url: '',
type: '',
site_admin: false,
contributions: 0
}
],
steps: [
{
date: '2019-09-11',
content: 'Halo v1.1.0 发布'
},
{
date: '2019-07-09',
content: 'Halo v1.0.3 发布'
},
{
date: '2019-07-08',
content: 'Star 数达到 6500'
},
{
date: '2019-06-01',
content: '1.0 正式版发布'
},
{
date: '2019-05-03',
content: 'Star 数达到 3300'
},
{
date: '2019-01-30',
content: 'John Niang 加入开发'
},
{
date: '2018-10-18',
content: '构建镜像到 Docker hub'
},
{
date: '2018-09-22',
content: 'Star 数达到 800'
},
{
date: '2018-05-02',
content: '第一条 Issue'
},
{
date: '2018-05-01',
content: 'Star 数达到 100'
},
{
date: '2018-04-29',
content: '第一个 Pull request'
},
{
date: '2018-04-28',
content: '正式开源'
},
{
date: '2018-03-21',
content: '确定命名为 Halo并上传到 Github'
}
],
updating: false
contributorsLoading: true
}
},
created() {
this.getEnvironments()
this.fetchContributors()
this.checkServerUpdate()
this.checkAdminUpdate()
},
computed: {
updateText() {
return this.updating ? '更新中...' : '更新'
}
// this.checkAdminUpdate()
},
methods: {
getEnvironments() {
@ -227,36 +147,49 @@ export default {
this.environments = response.data.data
})
},
confirmUpdate() {
this.updating = true
adminApi
.updateAdminAssets()
.then(response => {
this.$notification.success({
message: '更新成功',
description: '请刷新后体验最新版本!'
})
})
.finally(() => {
this.updating = false
})
},
// confirmUpdate() {
// this.updating = true
// adminApi
// .updateAdminAssets()
// .then(response => {
// this.$notification.success({
// message: '',
// description: ''
// })
// })
// .finally(() => {
// this.updating = false
// })
// },
handleCopyEnvironments() {
const text = `Server 版本:${this.environments.version}
Admin 版本${this.adminVersion}
数据库${this.environments.database}
运行模式${this.environments.mode}
UA 信息${navigator.userAgent}`
User Agent${navigator.userAgent}`
this.$copyText(text)
.then(message => {
console.log('copy', message)
this.$log.debug('copy', message)
this.$message.success('复制成功!')
})
.catch(err => {
console.log('copy.err', err)
this.$log.debug('copy.err', err)
this.$message.error('复制失败!')
})
},
async fetchContributors() {
this.contributorsLoading = true
const _this = this
axios
.get('https://api.github.com/repos/halo-dev/halo/contributors')
.then(response => {
_this.contributors = response.data
this.contributorsLoading = false
})
.catch(function(error) {
console.error('Fetch contributors error', error)
})
},
async checkServerUpdate() {
const _this = this
@ -300,48 +233,48 @@ UA 信息:${navigator.userAgent}`
console.error('Check update fail', error)
})
},
async checkAdminUpdate() {
const _this = this
// async checkAdminUpdate() {
// const _this = this
axios
.get('https://api.github.com/repos/halo-dev/halo-admin/releases/latest')
.then(response => {
const data = response.data
if (data.draft || data.prerelease) {
return
}
const current = _this.calculateIntValue(_this.adminVersion)
const latest = _this.calculateIntValue(data.name)
if (current >= latest) {
return
}
const title = '新版本提醒'
const content = '检测到 Admin 新版本:' + data.name + ',点击下方按钮可直接更新为最新版本。'
this.$notification.open({
message: title,
description: content,
icon: <a-icon type="smile" style="color: #108ee9" />,
btn: h => {
return h(
'a-button',
{
props: {
type: 'primary',
size: 'small'
},
on: {
click: () => _this.confirmUpdate()
}
},
'点击更新'
)
}
})
})
.catch(function(error) {
console.error('Check update fail', error)
})
},
// axios
// .get('https://api.github.com/repos/halo-dev/halo-admin/releases/latest')
// .then(response => {
// const data = response.data
// if (data.draft || data.prerelease) {
// return
// }
// const current = _this.calculateIntValue(_this.adminVersion)
// const latest = _this.calculateIntValue(data.name)
// if (current >= latest) {
// return
// }
// const title = ''
// const content = ' Admin ' + data.name + ''
// this.$notification.open({
// message: title,
// description: content,
// icon: <a-icon type="smile" style="color: #108ee9" />,
// btn: h => {
// return h(
// 'a-button',
// {
// props: {
// type: 'primary',
// size: 'small'
// },
// on: {
// click: () => _this.confirmUpdate()
// }
// },
// ''
// )
// }
// })
// })
// .catch(function(error) {
// console.error('Check update fail', error)
// })
// },
calculateIntValue(version) {
version = version.replace(/v/g, '')
const ss = version.split('.')

View File

@ -247,8 +247,8 @@ export default {
handleNextStep(e) {
e.preventDefault()
this.bloggerForm.validateFields((error, values) => {
console.log('error', error)
console.log('Received values of form: ', values)
this.$log.debug('error', error)
this.$log.debug('Received values of form: ', values)
if (error != null) {
} else {
this.stepCurrent++

View File

@ -3,7 +3,11 @@
<a-row>
<a-col :span="24">
<div class="card-container">
<a-tabs type="card">
<a-tabs
type="card"
class="general"
v-if="!advancedOptions"
>
<a-tab-pane key="general">
<span slot="tab">
<a-icon type="tool" />常规设置
@ -70,7 +74,7 @@
<a-form-item label="屏蔽搜索引擎:">
<a-switch v-model="options.seo_spider_disabled" />
</a-form-item>
<a-form-item label="关键词: ">
<a-form-item label="关键词:">
<a-input
v-model="options.seo_keywords"
placeholder="多个关键词以英文状态下的逗号隔开"
@ -247,6 +251,7 @@
<a-input-password
v-model="options.smms_api_secret_token"
placeholder="需要到 sm.ms 官网注册后获取"
autocomplete="new-password"
/>
</a-form-item>
</div>
@ -273,7 +278,10 @@
<a-input v-model="options.oss_upyun_operator" />
</a-form-item>
<a-form-item label="操作员密码:">
<a-input-password v-model="options.oss_upyun_password" />
<a-input-password
v-model="options.oss_upyun_password"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="文件目录:">
<a-input v-model="options.oss_upyun_source" />
@ -315,10 +323,16 @@
/>
</a-form-item>
<a-form-item label="Access Key">
<a-input-password v-model="options.oss_qiniu_access_key" />
<a-input-password
v-model="options.oss_qiniu_access_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="Secret Key">
<a-input-password v-model="options.oss_qiniu_secret_key" />
<a-input-password
v-model="options.oss_qiniu_secret_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="文件目录:">
<a-input
@ -371,10 +385,16 @@
<a-input v-model="options.oss_ali_endpoint" />
</a-form-item>
<a-form-item label="Access Key">
<a-input-password v-model="options.oss_ali_access_key" />
<a-input-password
v-model="options.oss_ali_access_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="Access Secret">
<a-input-password v-model="options.oss_ali_access_secret" />
<a-input-password
v-model="options.oss_ali_access_secret"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="文件目录:">
<a-input
@ -421,10 +441,16 @@
<a-input v-model="options.bos_baidu_endpoint" />
</a-form-item>
<a-form-item label="Access Key">
<a-input-password v-model="options.bos_baidu_access_key" />
<a-input-password
v-model="options.bos_baidu_access_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="Secret Key">
<a-input-password v-model="options.bos_baidu_secret_key" />
<a-input-password
v-model="options.bos_baidu_secret_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="图片处理策略:">
<a-input
@ -469,10 +495,16 @@
/>
</a-form-item>
<a-form-item label="Secret Id">
<a-input-password v-model="options.cos_tencent_secret_id" />
<a-input-password
v-model="options.cos_tencent_secret_id"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="Secret Key">
<a-input-password v-model="options.cos_tencent_secret_key" />
<a-input-password
v-model="options.cos_tencent_secret_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="文件目录:">
<a-input
@ -534,6 +566,7 @@
<a-input-password
v-model="options.email_password"
placeholder="部分邮箱可能是授权码"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="发件人:">
@ -579,28 +612,6 @@
</a-tabs>
</div>
</a-tab-pane>
<a-tab-pane key="api">
<span slot="tab">
<a-icon type="thunderbolt" />API 设置
</span>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="API 服务:">
<a-switch v-model="options.api_enabled" />
</a-form-item>
<a-form-item label="Access key">
<a-input-password v-model="options.api_access_key" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="handleSaveOptions"
>保存</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="other">
<span slot="tab">
<a-icon type="align-left" />其他设置
@ -653,10 +664,158 @@
</a-form>
</a-tab-pane>
</a-tabs>
<a-tabs
type="card"
class="advanced"
v-else
>
<a-tab-pane key="permalink">
<span slot="tab">
<a-icon type="link" />固定链接
</span>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="文章固定链接类型:">
<template slot="help">
<span v-if="options.post_permalink_type === 'DEFAULT'">{{ options.blog_url }}/{{ options.archives_prefix }}/${url}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'DATE'">{{ options.blog_url }}{{ new Date() | moment_post_date }}${url}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'DAY'">{{ options.blog_url }}{{ new Date() | moment_post_day }}${url}{{ options.path_suffix }}</span>
<span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p=${id}</span>
</template>
<a-select v-model="options.post_permalink_type">
<a-select-option
v-for="item in Object.keys(permalinkType)"
:key="item"
:value="item"
>{{ permalinkType[item].text }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="自定义页面前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.sheet_prefix }}/${url}{{ options.path_suffix }}</span>
</template>
<a-input v-model="options.sheet_prefix" />
</a-form-item>
<a-form-item label="友情链接页面前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.links_prefix }}</span>
</template>
<a-input v-model="options.links_prefix" />
</a-form-item>
<a-form-item label="图库页面前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.photos_prefix }}</span>
</template>
<a-input v-model="options.photos_prefix" />
</a-form-item>
<a-form-item label="日志页面前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.journals_prefix }}</span>
</template>
<a-input v-model="options.journals_prefix" />
</a-form-item>
<a-form-item label="归档前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.archives_prefix }}{{ options.path_suffix }}</span>
</template>
<a-input v-model="options.archives_prefix" />
</a-form-item>
<a-form-item label="分类前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.categories_prefix }}/${slugName}{{ options.path_suffix }}</span>
</template>
<a-input v-model="options.categories_prefix" />
</a-form-item>
<a-form-item label="标签前缀:">
<template slot="help">
<span>{{ options.blog_url }}/{{ options.tags_prefix }}/${slugName}{{ options.path_suffix }}</span>
</template>
<a-input v-model="options.tags_prefix" />
</a-form-item>
<a-form-item label="路径后缀:">
<template slot="help">
<span>仅对内建路径有效</span>
</template>
<a-input v-model="options.path_suffix" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="handleSaveOptions"
>保存</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="api">
<span slot="tab">
<a-icon type="api" />API 设置
</span>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="API 服务:">
<a-switch v-model="options.api_enabled" />
</a-form-item>
<a-form-item label="Access key">
<a-input-password
v-model="options.api_access_key"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="handleSaveOptions"
>保存</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="advanced-other">
<span slot="tab">
<a-icon type="align-left" />其他设置
</span>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item
label="全局绝对路径:"
help="* 对网站上面的所有页面路径、本地附件路径、以及主题中的静态资源路径有效。"
>
<a-switch v-model="options.global_absolute_path_enabled" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="handleSaveOptions"
>保存</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
</div>
</a-col>
</a-row>
<div style="position: fixed;bottom: 30px;right: 30px;">
<a-tooltip placement="top">
<template slot="title">
<span>{{ advancedOptions?'基础选项':'高级选项' }}</span>
</template>
<a-button
type="primary"
shape="circle"
:icon="`${advancedOptions?'setting':'thunderbolt'}`"
size="large"
@click="handleAdvancedOptions()"
></a-button>
</a-tooltip>
</div>
<AttachmentSelectDrawer
v-model="logoDrawerVisible"
@listenToSelect="handleSelectLogo"
@ -675,6 +834,7 @@ import { mapActions } from 'vuex'
import optionApi from '@/api/option'
import mailApi from '@/api/mail'
import attachmentApi from '@/api/attachment'
import postApi from '@/api/post'
export default {
components: {
@ -683,6 +843,7 @@ export default {
data() {
return {
attachmentType: attachmentApi.type,
permalinkType: postApi.permalinkType,
wrapperCol: {
xl: { span: 8 },
lg: { span: 8 },
@ -693,6 +854,7 @@ export default {
faviconDrawerVisible: false,
options: [],
mailParam: {},
advancedOptions: false,
tencentCosRegions: [
{
text: '北京一区',
@ -1069,6 +1231,9 @@ export default {
handleSelectFavicon(data) {
this.options.blog_favicon = encodeURI(data.path)
this.faviconDrawerVisible = false
},
handleAdvancedOptions() {
this.advancedOptions = !this.advancedOptions
}
}
}

View File

@ -18,7 +18,7 @@
<div slot="title">
<a-icon type="experiment" /> 开发者选项
</div>
<p>点击进入开发者选项页面</p>
<p style="min-height: 50px;">点击进入开发者选项页面</p>
<a-button
type="primary"
style="float:right"
@ -26,6 +26,29 @@
>进入</a-button>
</a-card>
</a-col>
<a-col
:xl="6"
:lg="6"
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
>
<a-card
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
<div slot="title">
<a-icon type="html5" /> 静态部署
</div>
<p style="min-height: 50px;">生成静态页面并部署到 Github Pages 之类的托管平台</p>
<a-button
type="primary"
style="float:right"
@click="handleToStaticPagesManage"
>管理</a-button>
</a-card>
</a-col>
<a-col
:xl="6"
:lg="6"
@ -41,7 +64,7 @@
<div slot="title">
<a-icon type="hdd" /> 博客备份
</div>
<p>支持备份全站数据</p>
<p style="min-height: 50px;">备份全站数据支持下载到本地</p>
<a-button
type="primary"
style="float:right"
@ -64,7 +87,7 @@
<div slot="title">
<a-icon type="file-markdown" /> Markdown 文章导入
</div>
<p>支持 Hexo/Jekyll 文章导入并解析元数据</p>
<p style="min-height: 50px;">支持 Hexo/Jekyll 文章导入并解析元数据</p>
<a-button
type="primary"
style="float:right"
@ -117,7 +140,7 @@ export default {
handleChange(info) {
const status = info.file.status
if (status !== 'uploading') {
console.log(info.file, info.fileList)
this.$log.debug(info.file, info.fileList)
}
if (status === 'done') {
this.$message.success(`${info.file.name} 导入成功!`)
@ -128,6 +151,9 @@ export default {
handleToDeveloperOptions() {
this.$router.push({ name: 'DeveloperOptions' })
},
handleToStaticPagesManage() {
this.$router.push({ name: 'StaticPagesManage' })
},
onUploadClose() {
this.$refs.upload.handleClearFileList()
}

View File

@ -1,5 +1,8 @@
<template>
<a-form layout="vertical">
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="开发者选项:">
<a-switch v-model="options.developer_mode" />
</a-form-item>
@ -18,7 +21,13 @@ export default {
name: 'SettingsForm',
data() {
return {
options: []
options: [],
wrapperCol: {
xl: { span: 8 },
lg: { span: 8 },
sm: { span: 12 },
xs: { span: 24 }
}
}
},
created() {

View File

@ -27,7 +27,7 @@
</div>
<div style="margin-top:15px">
<a-table
:rowKey="record => record.name"
:rowKey="record => record.id"
:columns="columns"
:dataSource="sortedStatics"
:pagination="false"

View File

@ -0,0 +1,43 @@
<template>
<div>
<a-row>
<a-col :span="24">
<div class="card-container">
<a-tabs type="card">
<a-tab-pane key="environment">
<span slot="tab">
<a-icon type="folder" />文件列表
</span>
<StaticPagesList />
</a-tab-pane>
<a-tab-pane key="runtimeLogs">
<span slot="tab">
<a-icon type="appstore" />部署平台
</span>
<DeploySettingsForm />
</a-tab-pane>
<a-tab-pane key="optionsList">
<span slot="tab">
<a-icon type="setting" />配置
</span>
<SettingsForm />
</a-tab-pane>
</a-tabs>
</div>
</a-col>
</a-row>
</div>
</template>
<script>
import StaticPagesList from './tabs/StaticPagesList'
import DeploySettingsForm from './tabs/DeploySettingsForm'
import SettingsForm from './tabs/SettingsForm'
export default {
name: 'StaticPagesManage',
components: {
StaticPagesList,
DeploySettingsForm,
SettingsForm
}
}
</script>

View File

@ -0,0 +1,106 @@
<template>
<a-form
layout="vertical"
:wrapperCol="wrapperCol"
>
<a-form-item label="部署平台:">
<a-select v-model="options.static_deploy_type">
<a-select-option
v-for="item in Object.keys(deployType)"
:key="item"
:value="item"
>{{ deployType[item].text }}</a-select-option>
</a-select>
</a-form-item>
<div
id="gitForm"
v-show="options.static_deploy_type === 'GIT'"
>
<a-form-item label="域名:">
<a-input v-model="options.git_static_deploy_domain" />
</a-form-item>
<a-form-item label="仓库:">
<a-input v-model="options.git_static_deploy_repository" />
</a-form-item>
<a-form-item label="分支:">
<a-input v-model="options.git_static_deploy_branch" />
</a-form-item>
<a-form-item label="仓库用户名:">
<a-input v-model="options.git_static_deploy_username" />
</a-form-item>
<a-form-item label="邮箱:">
<a-input v-model="options.git_static_deploy_email" />
</a-form-item>
<a-form-item label="Token">
<a-input-password
v-model="options.git_static_deploy_token"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="CNAME">
<a-input v-model="options.git_static_deploy_cname" />
</a-form-item>
</div>
<div
id="netlifyForm"
v-show="options.static_deploy_type === 'NETLIFY'"
>
<a-form-item label="域名:">
<a-input v-model="options.netlify_static_deploy_domain" />
</a-form-item>
<a-form-item label="Site ID">
<a-input v-model="options.netlify_static_deploy_site_id" />
</a-form-item>
<a-form-item label="Token">
<a-input-password
v-model="options.netlify_static_deploy_token"
autocomplete="new-password"
/>
</a-form-item>
</div>
<a-form-item>
<a-button
type="primary"
@click="handleSaveOptions"
>保存</a-button>
</a-form-item>
</a-form>
</template>
<script>
import staticPageApi from '@/api/staticPage'
import optionApi from '@/api/option'
import { mapActions } from 'vuex'
export default {
name: 'DeploySettingsForm',
data() {
return {
deployType: staticPageApi.deployType,
wrapperCol: {
xl: { span: 8 },
lg: { span: 8 },
sm: { span: 12 },
xs: { span: 24 }
},
options: []
}
},
mounted() {
this.loadFormOptions()
},
methods: {
...mapActions(['loadOptions']),
loadFormOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
})
}
}
}
</script>

View File

@ -0,0 +1,10 @@
<template>
<div>
SettingsForm
</div>
</template>
<script>
export default {
name: 'SettingsForm'
}
</script>

View File

@ -0,0 +1,113 @@
<template>
<div class="option-tab-wrapper">
<a-card
:bordered="false"
:bodyStyle="{ padding: 0 }"
>
<div class="table-operator">
<a-button
type="primary"
icon="reload"
@click="handleGenerate"
>生成</a-button>
<a-button
icon="cloud-upload"
@click="handleDeploy"
:loading="deployLoading"
:disabled="deployLoading"
>
部署
</a-button>
<a-button
icon="sync"
@click="loadStaticPageList"
:loading="loading"
:disabled="loading"
>
刷新
</a-button>
</div>
<div style="margin-top:15px">
<a-table
:rowKey="record => record.id"
:columns="columns"
:dataSource="staticPages"
:pagination="false"
size="middle"
:loading="loading"
>
<span
slot="name"
slot-scope="name"
>
<ellipsis
:length="64"
tooltip
>
{{ name }}
</ellipsis>
</span>
</a-table>
</div>
</a-card>
</div>
</template>
<script>
import staticPageApi from '@/api/staticPage'
const columns = [
{
title: '文件名',
dataIndex: 'name',
scopedSlots: { customRender: 'name' }
}
]
export default {
name: 'StaticPagesList',
data() {
return {
columns: columns,
staticPages: [],
loading: false,
deployLoading: false
}
},
created() {
this.loadStaticPageList()
},
methods: {
loadStaticPageList() {
this.loading = true
staticPageApi.list().then(response => {
this.staticPages = response.data.data
this.loading = false
})
},
handleGenerate() {
this.loading = true
const hide = this.$message.loading('生成中...', 0)
staticPageApi
.generate()
.then(response => {
this.$message.success('生成成功!')
})
.finally(response => {
this.loadStaticPageList()
hide()
})
},
handleDeploy() {
this.deployLoading = true
const hide = this.$message.loading('部署中...', 0)
staticPageApi
.deploy()
.then(response => {
this.$message.success('部署成功!')
})
.finally(response => {
this.deployLoading = false
hide()
})
}
}
}
</script>

View File

@ -47,6 +47,7 @@
:style="{'animation-delay': '0.3s'}"
>
<a-button
:loading="landing"
type="primary"
:block="true"
@click="handleLogin"
@ -139,7 +140,8 @@ export default {
apiModifyVisible: false,
defaultApiBefore: window.location.protocol + '//',
apiUrl: window.location.host,
resetPasswordButton: false
resetPasswordButton: false,
landing: false
}
},
computed: {
@ -169,11 +171,17 @@ export default {
this.$message.warn('密码不能为空!')
return
}
this.login({ username: this.username, password: this.password }).then(response => {
// Go to dashboard
this.loginSuccess()
})
this.landing = true
this.login({ username: this.username, password: this.password })
.then(response => {
// Go to dashboard
this.loginSuccess()
})
.finally(() => {
setTimeout(() => {
this.landing = false
}, 500)
})
},
loginSuccess() {
// Cache the user info

View File

@ -107,13 +107,22 @@
</span>
<a-form layout="vertical">
<a-form-item label="原密码:">
<a-input-password v-model="passwordParam.oldPassword" />
<a-input-password
v-model="passwordParam.oldPassword"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="新密码:">
<a-input-password v-model="passwordParam.newPassword" />
<a-input-password
v-model="passwordParam.newPassword"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item label="确认密码:">
<a-input-password v-model="passwordParam.confirmPassword" />
<a-input-password
v-model="passwordParam.confirmPassword"
autocomplete="new-password"
/>
</a-form-item>
<a-form-item>
<a-button

View File

@ -15,19 +15,19 @@ const assetsCDN = {
axios: 'axios',
marked: 'marked'
},
css: [
],
css: [],
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
'//cdn.jsdelivr.net/npm/marked@0.8.0/marked.min.js'
'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/marked@0.8.0/marked.min.js'
]
}
// vue.config.js
module.exports = {
publicPath: process.env.PUBLIC_PATH,
configureWebpack: {
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)