Merge branch 'master' of gitlab.com:halo_dev/halo-admin

pull/3445/head
johnniang 2019-04-08 16:15:47 +08:00
commit e4cf6f1ae2
24 changed files with 1136 additions and 755 deletions

View File

@ -9,6 +9,7 @@
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"animate.css": "^3.7.0",
"ant-design-vue": "~1.3.7",
"axios": "^0.18.0",
"enquire.js": "^2.1.6",

14
src/api/category.js Normal file
View File

@ -0,0 +1,14 @@
import service from '@/utils/service'
const baseUrl = '/admin/api/categories'
const categoryApi = {}
categoryApi.listAll = () => {
return service({
url: `${baseUrl}`,
method: 'get'
})
}
export default categoryApi

29
src/api/menu.js Normal file
View File

@ -0,0 +1,29 @@
import service from '@/utils/service'
const baseUrl = '/admin/api/menus'
const menuApi = {}
menuApi.listAll = () => {
return service({
url: baseUrl,
method: 'get'
})
}
menuApi.create = menu => {
return service({
url: baseUrl,
data: menu,
method: 'post'
})
}
menuApi.delete = menuId => {
return service({
url: `${baseUrl}/${menuId}`,
method: 'delete'
})
}
export default menuApi

View File

@ -4,6 +4,13 @@ const baseUrl = '/admin/api/themes'
const themeApi = {}
themeApi.listAll = () => {
return service({
url: `${baseUrl}`,
method: 'get'
})
}
themeApi.listFiles = () => {
return service({
url: `${baseUrl}/files`,
@ -11,4 +18,31 @@ themeApi.listFiles = () => {
})
}
themeApi.customTpls = () => {
return service({
url: `${baseUrl}/files/custom`,
method: 'get'
})
}
themeApi.active = theme => {
return service({
url: `${baseUrl}/active?theme=${theme}`,
method: 'get'
})
}
themeApi.delete = key => {
return service({
url: `${baseUrl}/${key}`,
method: 'delete'
})
}
themeApi.listOptions = theme => {
return service({
url: `${baseUrl}/configurations?name=${theme}`
})
}
export default themeApi

View File

@ -1,150 +0,0 @@
<template>
<div id="userLayout" :class="['user-layout-wrapper', device]">
<div class="container">
<div class="top">
<div class="header">
<a href="/">
<img src="~@/assets/logo.svg" class="logo" alt="logo">
<span class="title">Ant Design</span>
</a>
</div>
<div class="desc">
Ant Design 是西湖区最具影响力的 Web 设计规范
</div>
</div>
<route-view></route-view>
<div class="footer">
<div class="links">
<a href="_self">帮助</a>
<a href="_self">隐私</a>
<a href="_self">条款</a>
</div>
<div class="copyright">
Copyright &copy; 2018 白鹭学园技术组出品
</div>
</div>
</div>
</div>
</template>
<script>
import RouteView from '@/components/layouts/RouteView'
import { mixinDevice } from '@/utils/mixin.js'
export default {
name: 'UserLayout',
components: { RouteView },
mixins: [mixinDevice],
data() {
return {}
},
mounted() {
document.body.classList.add('userLayout')
},
beforeDestroy() {
document.body.classList.remove('userLayout')
}
}
</script>
<style lang="less" scoped>
#userLayout.user-layout-wrapper {
height: 100%;
&.mobile {
.container {
.main {
max-width: 368px;
width: 98%;
}
}
}
.container {
width: 100%;
min-height: 100%;
background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
background-size: 100%;
padding: 110px 0 144px;
position: relative;
a {
text-decoration: none;
}
.top {
text-align: center;
.header {
height: 44px;
line-height: 44px;
.badge {
position: absolute;
display: inline-block;
line-height: 1;
vertical-align: middle;
margin-left: -12px;
margin-top: -10px;
opacity: 0.8;
}
.logo {
height: 44px;
vertical-align: top;
margin-right: 16px;
border-style: none;
}
.title {
font-size: 33px;
color: rgba(0, 0, 0, .85);
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: 600;
position: relative;
top: 2px;
}
}
.desc {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
margin-top: 12px;
margin-bottom: 40px;
}
}
.main {
min-width: 260px;
width: 368px;
margin: 0 auto;
}
.footer {
position: absolute;
width: 100%;
bottom: 0;
padding: 0 16px;
margin: 48px 0 24px;
text-align: center;
.links {
margin-bottom: 8px;
font-size: 14px;
a {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}
}
</style>

View File

@ -1,7 +1,6 @@
import UserLayout from '@/components/layouts/UserLayout'
import BlankLayout from '@/components/layouts/BlankLayout'
import BasicLayout from '@/components/layouts/BasicLayout'
import RouteView from '@/components/layouts/RouteView'
import PageView from '@/components/layouts/PageView'
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }
export { BasicLayout, BlankLayout, RouteView, PageView }

View File

@ -3,12 +3,13 @@
<a-drawer
width="300"
placement="right"
:closable="false"
:closable="true"
@close="onClose"
:visible="visible"
:getContainer="() => $refs.settingDrawer"
:style="{}"
:mask="false"
:zIndex="9999"
>
<div class="setting-drawer-index-content">
<div :style="{ marginBottom: '24px' }">
@ -147,13 +148,6 @@
</div>
</div>
<a-divider/>
<div :style="{ marginBottom: '24px' }">
<a-list :split="false">
<a-list-item>
<a-icon type="double-right" @click="toggle" class="iconClose"/>
</a-list-item>
</a-list>
</div>
</div>
</a-drawer>
</div>
@ -289,10 +283,6 @@ export default {
font-size: 14px;
}
}
.iconClose {
font-size: 24px;
cursor: pointer;
}
}
.setting-drawer-index-handle {

View File

@ -1,5 +1,5 @@
// eslint-disable-next-line
import { UserLayout, BasicLayout, RouteView, BlankLayout, PageView } from '@/components/layouts'
import { BasicLayout, RouteView, BlankLayout, PageView } from '@/components/layouts'
export const asyncRouterMap = [
@ -24,7 +24,7 @@ export const asyncRouterMap = [
path: '/posts',
name: 'Posts',
redirect: '/posts/list',
component: RouteView,
component: PageView,
meta: { title: '文章', icon: 'form' },
children: [
{
@ -58,7 +58,7 @@ export const asyncRouterMap = [
{
path: '/pages',
name: 'Pages',
component: RouteView,
component: PageView,
redirect: '/pages/list',
meta: { title: '页面', icon: 'read' },
children: [
@ -97,7 +97,7 @@ export const asyncRouterMap = [
{
path: '/interface',
name: 'Interface',
component: RouteView,
component: PageView,
redirect: '/interface/themes',
meta: { title: '外观', icon: 'skin' },
children: [
@ -126,7 +126,7 @@ export const asyncRouterMap = [
{
path: '/user',
name: 'User',
component: RouteView,
component: PageView,
redirect: '/user/profile',
meta: { title: '用户', icon: 'user' },
children: [
@ -143,7 +143,7 @@ export const asyncRouterMap = [
{
path: '/system',
name: 'System',
component: RouteView,
component: PageView,
redirect: '/system/options',
meta: { title: '系统', icon: 'setting' },
children: [
@ -191,10 +191,12 @@ export const constantRouterMap = [
}
]
},
{
path: '/login',
component: () => import('@/views/user/Login')
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404')
}
]

View File

@ -9,10 +9,12 @@ import './core/use'
import bootstrap from './core/bootstrap'
// import '@/permission' // permission control
import '@/utils/filter' // global filter
import animated from 'animate.css'
Vue.config.productionTip = false
Vue.use(router)
Vue.use(animated)
new Vue({
router,

View File

@ -1,20 +1,118 @@
<template>
<div class="page-header-index-wide">
<a-card title="Card Title">
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
<a-card-grid style="width:25%;textAlign:'center'">Content</a-card-grid>
</a-card>
<a-row :gutter="12" type="flex" align="middle">
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
<a-col
class="attachment-item"
:xl="4"
:lg="4"
:md="12"
:sm="12"
:xs="24"
>
<a-card :bodyStyle="{ padding: '14px' }">
<img :src="src" slot="cover" />
</a-card>
</a-col>
</a-row>
</div>
</template>
<script>
export default {}
export default {
data() {
return {
src: 'https://cdn.ryanc.cc/img/blog/thumbnails/c86b59623c4a9bfeac403824dec3a789.jpg'
}
}
}
</script>
<style scoped>
<style lang="less" scoped>
.attachment-item {
padding-bottom: 12px;
}
</style>

View File

@ -11,7 +11,7 @@
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="评论状态">
<a-select v-model="queryParam.status" placeholder="请选择" default-value="0">
<a-select v-model="queryParam.status" placeholder="请选择" defaultValue="0">
<a-select-option value="0">已发布</a-select-option>
<a-select-option value="1">待审核</a-select-option>
<a-select-option value="2">回收站</a-select-option>
@ -41,18 +41,11 @@
</a-dropdown>
</div>
<div style="margin-top:15px">
<a-table :columns="columns" :dataSource="data">
<a slot="name" slot-scope="text" href="javascript:;">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o" /> Name</span>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" color="blue" :key="tag">{{ tag }}</a-tag>
</span>
<a-table :columns="columns">
<span slot="action" slot-scope="text, record">
<a href="javascript:;">Invite {{ record.name }}</a>
<a href="javascript:;" @click="editComment(record.id)"></a>
<a-divider type="vertical" />
<a href="javascript:;">Delete</a>
<a-divider type="vertical" />
<a href="javascript:;" class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
<a href="javascript:;" @click="deleteComment(record.id)"></a>
</span>
</a-table>
</div>
@ -61,51 +54,57 @@
</template>
<script>
const columns = [
{
title: '#',
scopedSlots: { customRender: 'serial' }
},
{
title: '评论者',
dataIndex: 'author'
},
{
title: '内容',
dataIndex: 'content'
},
{
title: '评论页面',
dataIndex: 'post.id'
},
{
title: '日期',
dataIndex: 'createTime'
},
{
title: '操作',
dataIndex: 'action',
width: '150px',
scopedSlots: { customRender: 'action' }
}
]
export default {
name: 'PostList',
name: 'CommentList',
components: {},
data() {
return {
mdl: {},
//
queryParam: {},
//
columns: [
{
title: '#',
scopedSlots: { customRender: 'serial' }
},
{
title: '评论者',
dataIndex: 'author'
},
{
title: '内容',
dataIndex: 'content'
},
{
title: '评论页面',
dataIndex: 'post.id'
},
{
title: '日期',
dataIndex: 'createTime'
},
{
title: '操作',
dataIndex: 'action',
width: '150px',
scopedSlots: { customRender: 'action' }
}
],
columns,
loadData: parameter => {},
selectedRowKeys: [],
selectedRows: [],
options: {},
optionAlertShow: false
options: {}
}
},
created() {},
methods: {}
methods: {
editComment(id) {
this.$message.success('编辑')
},
deleteComment(id) {
this.$message.success('删除')
}
}
}
</script>

View File

@ -1,41 +1,69 @@
<template>
<div class="page-header-index-wide">
<a-row :gutter="12">
<a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24">
<a-col
:xl="10"
:lg="10"
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="添加菜单">
<a-form layout="horizontal">
<a-form-item label="名称:" help="*页面上所显示的名称">
<a-input />
<a-input v-model="menuToCreate.name" />
</a-form-item>
<a-form-item label="路径:" help="*菜单的路径">
<a-input />
<a-input v-model="menuToCreate.url" />
</a-form-item>
<a-form-item label="上级菜单:">
<a-select>
<a-select-option value="1">上级菜单</a-select-option>
<a-select v-model="menuToCreate.parentId">
<a-select-option v-for="(menu,index) in menus" :key="index" :value="menu.id">{{ menu.name }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="排序编号:">
<a-input type="number" />
<a-input type="number" v-model="menuToCreate.sort" />
</a-form-item>
<a-form-item label="图标:" help="*请根据主题的支持选填">
<a-input />
<a-input v-model="menuToCreate.icon" />
</a-form-item>
<a-form-item label="打开方式:">
<a-select defaultValue="_self">
<a-select defaultValue="_self" v-model="menuToCreate.target">
<a-select-option value="_self">当前窗口</a-select-option>
<a-select-option value="_blank">新窗口</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary">保存</a-button>
<a-button type="primary" @click="createMenu"></a-button>
</a-form-item>
</a-form>
</a-card>
</a-col>
<a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24">
<a-col
:xl="14"
:lg="14"
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="所有菜单">
<a-table :columns="columns" :dataSource="data" :loading="loading"> </a-table>
<a-table :columns="columns" :dataSource="menus" :loading="loading">
<ellipsis :length="30" tooltip slot="name" slot-scope="text">
{{ text }}
</ellipsis>
<span slot="action" slot-scope="text, record">
<a href="javascript:;" @click="editMenu(record.id)"></a>
<a-divider type="vertical" />
<a-popconfirm
:title="'你确定要删除【' + record.name + '】菜单?'"
@confirm="deleteMenu(record.id)"
okText="确定"
cancelText="取消"
>
<a href="javascript:;">删除</a>
</a-popconfirm>
</span>
</a-table>
</a-card>
</a-col>
</a-row>
@ -43,10 +71,13 @@
</template>
<script>
import { Ellipsis } from '@/components'
import menuApi from '@/api/menu'
const columns = [
{
title: '名称',
dataIndex: 'name'
dataIndex: 'name',
scopedSlots: { customRender: 'name' }
},
{
title: '路径',
@ -59,14 +90,49 @@ const columns = [
{
title: '图标',
dataIndex: 'icon'
},
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' }
}
]
export default {
components: {
Ellipsis
},
data() {
return {
data: [],
loading: false,
columns
loading: true,
columns,
menus: [],
menuToCreate: {}
}
},
created() {
this.loadMenus()
},
methods: {
loadMenus() {
menuApi.listAll().then(response => {
this.menus = response.data.data
this.loading = false
})
},
createMenu() {
menuApi.create(this.menuToCreate).then(response => {
this.loadMenus()
})
},
editMenu(id) {
this.$message.success('编辑' + id)
},
deleteMenu(id) {
menuApi.delete(id).then(response => {
this.$message.success('删除成功!')
this.loadMenus()
})
}
}
}

View File

@ -7,9 +7,16 @@
:md="18"
:sm="24"
:xs="24"
>
:style="{'padding-bottom':'12px'}">
<a-card>
<codemirror :value="value"></codemirror>
<a-form layout="vertical">
<a-form-item>
<codemirror :value="value"></codemirror>
</a-form-item>
<a-form-item>
<a-button type="primary">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</a-col>
<a-col
@ -18,10 +25,8 @@
:md="6"
:sm="24"
:xs="24"
>
<a-card
title="Anatole 主题"
>
:style="{'padding-bottom':'12px'}">
<a-card title="Anatole 主题">
<theme-file :files="files" />
</a-card>
</a-col>
@ -63,9 +68,14 @@ export default {
}
</script>
<style>
<style lang="less">
.CodeMirror-gutters {
border-right: 1px solid #fff3f3;
background-color: #ffffff;
}
.ant-card {
.ant-card-body {
padding: 16px;
}
}
</style>

View File

@ -1,73 +1,183 @@
<template>
<div class="page-header-index-wide">
<a-row :gutter="12" type="flex" align="middle">
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="12" :xs="24">
<a-card>
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
<template class="ant-card-actions" slot="actions">
<a-icon type="check"/>
<a-icon type="setting"/>
</template>
<a-card-meta title="Anatole"></a-card-meta>
<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 :bodyStyle="{ padding: '14px' }">
<img
:alt="theme.properties.name"
:src="'http://localhost:8090/' + theme.key + '/screenshot.png'"
slot="cover"
>
<a-divider></a-divider>
<div class="theme-control">
<span class="theme-title">{{ theme.properties.name }}</span>
<a-button-group class="theme-button">
<a-button type="primary" v-if="activatedTheme == theme.key" disabled>已启用</a-button>
<a-button type="primary" @click="activeTheme(theme.key)" v-else></a-button>
<a-button @click="optionModal(theme.key)" v-if="activatedTheme == theme.key"></a-button>
<a-popconfirm
:title="'确定删除【' + theme.properties.name + '】主题?'"
@confirm="deleteTheme(theme.key)"
okText="确定"
cancelText="取消"
v-else
>
<a-button type="dashed">删除</a-button>
</a-popconfirm>
</a-button-group>
</div>
</a-card>
</a-col>
</a-row>
<a-modal :title="optionTheme + ' 主题设置'" width="90%" v-model="visible">
<a-row>
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<div class="card-container">
<a-tabs type="card">
<a-tab-pane
v-for="(group,index) in themeOptions"
:key="index"
:tab="group.description"
>
<a-form layout="vertical">
<a-form-item
v-for="(item,index1) in group.items"
:label="item.description+''"
:key="index1"
:wrapper-col="wrapperCol"
>
<a-input :v-model="'options.'+item.description" v-if="item.type=='text'"/>
<a-input
type="textarea"
:autosize="{ minRows: 5 }"
:v-model="'options.'+item.description"
v-else-if="item.type=='textarea'"
/>
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
:v-model="'options.'+item.description"
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="'options.'+item.description"
v-else-if="item.type=='select'"
>
<a-select-option
v-for="(option,index3) in item.options"
:key="index3"
:value="option.value"
>{{ option.label }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
</div>
</a-col>
</a-row>
</a-modal>
</div>
</template>
<script>
export default {}
import themeApi from '@/api/theme'
import optionApi from '@/api/option'
export default {
data() {
return {
wrapperCol: {
xl: { span: 8 },
sm: { span: 8 },
xs: { span: 24 }
},
themes: [],
visible: false,
optionTheme: '',
optionUrl: 'https://ryanc.cc',
// TODO api使
activatedTheme: 'anatole',
themeOptions: [],
options: []
}
},
created() {
this.loadThemes()
},
methods: {
loadThemes() {
themeApi.listAll().then(response => {
this.themes = response.data.data
})
},
optionModal(theme) {
themeApi.listOptions('anatole').then(response => {
this.themeOptions = response.data.data
this.optionTheme = theme
this.visible = true
this.loadOptions()
})
},
activeTheme(theme) {
themeApi.active(theme).then(response => {
this.activatedTheme = theme
this.$message.success('设置成功!')
this.loadThemes()
})
},
deleteTheme(key) {
themeApi.delete(key).then(response => {
this.$message.success('删除成功!')
this.loadThemes()
})
},
saveOptions() {
optionApi.save(this.options).then(response => {
this.loadOptions()
this.$message.success('保存成功!')
})
},
loadOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
}
}
}
</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;
}
</style>

View File

@ -43,10 +43,15 @@ export default {
})
)
}
return h('p', 'No files')
return h('p', '没有文件')
}
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.ant-tree-child-tree {
li {
overflow: hidden;
}
}
</style>

View File

@ -1,10 +1,166 @@
<template>
<div class="page-header-index-wide"> Page Edit </div>
<div class="page-header-index-wide">
<a-row :gutter="12">
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<a-card>
<div style="margin-bottom: 16px">
<a-input
v-decorator="['title', { rules: [{ required: true, message: '请输入页面标题' }] }]"
size="large"
placeholder="请输入页面标题"
/>
</div>
<a-button type="primary" @click="showDrawer"></a-button>
</a-card>
<a-card>
<div id="editor">
<mavon-editor v-model="value"/>
</div>
</a-card>
</a-col>
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<a-drawer
title="页面设置"
:width="drawerWidth"
:closable="true"
@close="onClose"
:visible="visible"
>
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item label="页面路径:" :help="'https://localhost:8090/p/'+postUrl">
<a-input v-model="postUrl"/>
</a-form-item>
<a-form-item label="页面密码:">
<a-input type="password"/>
</a-form-item>
<a-form-item label="是否开启评论:">
<a-select defaultValue="1">
<a-select-option value="1"></a-select-option>
<a-select-option value="0"></a-select-option>
</a-select>
</a-form-item>
<a-form-item label="自定义模板:">
<a-select>
<a-select-option v-for="tpl in customTpls" :key="tpl" :value="tpl">{{ tpl }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
</div>
<a-divider/>
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">缩略图</h3>
<div class="post-setting-drawer-item">
<div class="post-thum">
<img class="img" src="https://os.alipayobjects.com/rmsportal/mgesTPFxodmIwpi.png">
</div>
</div>
</div>
<a-divider/>
</div>
<div class="postControl">
<a-button style="marginRight: 8px" @click="onClose">稿</a-button>
<a-button @click="onClose" type="primary">发布</a-button>
</div>
</a-drawer>
</a-col>
</a-row>
</div>
</template>
<script>
export default {}
</script>
import { mavonEditor } from 'mavon-editor'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import 'mavon-editor/dist/css/index.css'
import tagApi from '@/api/theme'
export default {
name: 'Editor',
components: {
mavonEditor
},
mixins: [mixin, mixinDevice],
data() {
return {
wrapperCol: {
xl: { span: 24 },
sm: { span: 24 },
xs: { span: 24 }
},
value: 'Hello World',
visible: false,
drawerWidth: '460',
postUrl: 'hello-world',
customTpls: []
}
},
mounted() {
if (this.isMobile()) {
this.drawerWidth = '100%'
} else {
this.drawerWidth = '460'
}
},
created() {
this.loadCustomTpls()
},
methods: {
loadCustomTpls() {
tagApi.customTpls().then(response => {
this.customTpls = response.data.data
})
},
showDrawer() {
this.visible = true
},
onClose() {
this.visible = false
}
}
}
</script>
<style scoped>
#editor {
margin: auto;
width: 100%;
}
.v-note-wrapper {
z-index: 1000;
}
.ant-card {
margin-bottom: 16px;
}
.ant-form-vertical .ant-form-item {
padding-bottom: 0;
}
.postControl {
position: absolute;
bottom: 0px;
width: 100%;
border-top: 1px solid rgb(232, 232, 232);
padding: 10px 16px;
text-align: right;
left: 0px;
background: rgb(255, 255, 255);
border-radius: 0px 0px 4px 4px;
}
.ant-form-vertical .ant-form-item {
padding-bottom: 0;
}
.post-thum .img {
width: 100%;
}
</style>

View File

@ -1,7 +1,13 @@
<template>
<div class="page-header-index-wide">
<a-row :gutter="12">
<a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24">
<a-col
:xl="10"
:lg="10"
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="添加分类目录">
<a-form layout="horizontal">
<a-form-item label="名称:" help="*页面上所显示的名称">
@ -24,7 +30,13 @@
</a-form>
</a-card>
</a-col>
<a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24">
<a-col
:xl="14"
:lg="14"
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="所有分类">
<a-table :columns="columns" :dataSource="data" :loading="loading"> </a-table>
</a-card>

View File

@ -1,7 +1,7 @@
<template>
<div class="page-header-index-wide">
<a-row :gutter="12">
<a-col :xl="18" :lg="18" :md="18" :sm="24" :xs="24">
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<a-card>
<div style="margin-bottom: 16px">
<a-input
@ -10,44 +10,81 @@
placeholder="请输入文章标题"
/>
</div>
<div style="margin-bottom: 16px">
<a-input
addonBefore="https://ryanc.cc/archives/"
defaultValue="mysite"
style="width: 360px"
/>
</div>
<a-button type="primary" @click="showDrawer"></a-button>
</a-card>
<a-card>
<div id="editor">
<mavon-editor v-model="value"/>
<mavon-editor :toolbars="markdownOption" v-model="value" :boxShadow="false" :ishljs="true"/>
</div>
</a-card>
</a-col>
<a-col :xl="6" :lg="6" :md="6" :sm="24" :xs="24">
<a-card title="发布">
<a-form layout="vertical">
<a-form-item label="开启评论:">
<a-select defaultValue="true">
<a-select-option value="true"></a-select-option>
<a-select-option value="false"></a-select-option>
</a-select>
</a-form-item>
<a-form-item label="文章密码:">
<a-input type="password"/>
</a-form-item>
<a-form-item>
<a-button type="primary">保存</a-button>
</a-form-item>
</a-form>
</a-card>
<a-card title="分类目录"></a-card>
<a-card title="标签"></a-card>
<a-card title="缩略图">
<img alt="example" src="https://ryanc.cc/material/screenshot.png" slot="cover">
</a-card>
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<a-drawer title="文章设置" :width="drawerWidth" :closable="true" @close="onClose" :visible="visible">
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item label="文章路径:" :help="'https://localhost:8090/archives/' + postUrl">
<a-input v-model="postUrl" />
</a-form-item>
<a-form-item label="文章密码:">
<a-input type="password" />
</a-form-item>
<a-form-item label="是否开启评论:">
<a-select defaultValue="1">
<a-select-option value="1"></a-select-option>
<a-select-option value="0"></a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">分类目录</h3>
<div class="post-setting-drawer-item">
<a-tree checkable :treeData="treeData" :defaultExpandAll="true">
<span slot="title0010" style="color: #1890ff">sss</span>
</a-tree>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">标签</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<a-select mode="tags" placeholder="选择或输入标签">
<a-select-option v-for="tag in tags" :key="tag.id" :value="tag.id.toString()">{{
tag.name
}}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">缩略图</h3>
<div class="post-setting-drawer-item">
<div class="post-thum">
<img class="img" src="https://os.alipayobjects.com/rmsportal/mgesTPFxodmIwpi.png" />
</div>
</div>
</div>
<a-divider />
</div>
<div class="postControl">
<a-button style="marginRight: 8px" @click="onClose">稿</a-button>
<a-button @click="onClose" type="primary">发布</a-button>
</div>
</a-drawer>
</a-col>
</a-row>
</div>
@ -55,12 +92,58 @@
<script>
import { mavonEditor } from 'mavon-editor'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import 'mavon-editor/dist/css/index.css'
import tagApi from '@/api/tag'
const toolbars = {
bold: true, //
italic: true, //
header: true, //
underline: true, // 线
strikethrough: true, // 线
quote: true, //
ol: true, //
ul: true, //
link: true, //
imagelink: true, //
code: true, // code
table: true, //
readmodel: true, //
undo: true, //
redo: true, //
navigation: true, //
subfield: true, //
preview: true //
}
const treeData = [
{
title: '学习记录',
key: '0-0',
children: [
{
title: 'Java 学习',
key: '0-0-0',
children: [{ title: 'JVM', key: '0-0-0-0' }, { title: 'Spring', key: '0-0-0-1' }]
},
{
title: 'PHP 学习',
key: '0-0-1',
children: [{ key: '0-0-1-0', title: 'Composer' }, { key: '0-0-1-1', title: 'MySQL' }]
}
]
},
{
title: '生活记录',
key: '1-0'
}
]
export default {
name: 'Editor',
components: {
mavonEditor
},
mixins: [mixin, mixinDevice],
data() {
return {
wrapperCol: {
@ -68,7 +151,36 @@ export default {
sm: { span: 24 },
xs: { span: 24 }
},
value: 'Hello World'
value: 'Hello World',
visible: false,
drawerWidth: '460',
postUrl: 'hello-world',
tags: [],
treeData,
markdownOption: toolbars
}
},
mounted() {
if (this.isMobile()) {
this.drawerWidth = '100%'
} else {
this.drawerWidth = '460'
}
},
created() {
this.loadTags()
},
methods: {
loadTags() {
tagApi.listAll(true).then(response => {
this.tags = response.data.data
})
},
showDrawer() {
this.visible = true
},
onClose() {
this.visible = false
}
}
}
@ -90,4 +202,29 @@ export default {
.ant-form-vertical .ant-form-item {
padding-bottom: 0;
}
.postControl {
position: absolute;
bottom: 0px;
width: 100%;
border-top: 1px solid rgb(232, 232, 232);
padding: 10px 16px;
text-align: right;
left: 0px;
background: rgb(255, 255, 255);
border-radius: 0px 0px 4px 4px;
}
.ant-form-vertical .ant-form-item {
padding-bottom: 0;
}
.post-thum .img {
width: 100%;
}
.mavonEditor {
width: 100%;
height: 560px;
}
</style>

View File

@ -11,7 +11,7 @@
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="文章状态">
<a-select v-model="queryParam.status" placeholder="请选择" default-value="0">
<a-select v-model="queryParam.status" placeholder="请选择文章状态" defaultValue="0">
<a-select-option value="0">已发布</a-select-option>
<a-select-option value="1">草稿箱</a-select-option>
<a-select-option value="2">回收站</a-select-option>
@ -20,10 +20,8 @@
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="分类目录">
<a-select v-model="queryParam.status" placeholder="请选择" default-value="0">
<a-select-option value="0">xx</a-select-option>
<a-select-option value="1">xx</a-select-option>
<a-select-option value="2">xx</a-select-option>
<a-select v-model="queryParam.categoryId" placeholder="请选择分类">
<a-select-option v-for="category in categories" :key="category.id">{{ category.name }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
@ -39,10 +37,13 @@
</div>
<div class="table-operator">
<a-button type="primary" icon="plus" @click="handleEdit()"></a-button>
<router-link :to="{name:'PostEdit'}">
<a-button type="primary" icon="plus">写文章</a-button>
</router-link>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1"> <a-icon type="delete" />回收站 </a-menu-item>
<a-menu-item key="1" v-if="postStatus==0 || postStatus==1"> <a-icon type="delete" />移到回收站 </a-menu-item>
<a-menu-item key="1" v-else-if="postStatus==2"> <a-icon type="delete" />永久删除 </a-menu-item>
</a-menu>
<a-button style="margin-left: 8px;">
批量操作
@ -51,18 +52,11 @@
</a-dropdown>
</div>
<div style="margin-top:15px">
<a-table :columns="columns" :dataSource="data">
<a slot="name" slot-scope="text" href="javascript:;">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o" /> Name</span>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" color="blue" :key="tag">{{ tag }}</a-tag>
</span>
<a-table :columns="columns">
<span slot="action" slot-scope="text, record">
<a href="javascript:;">Invite {{ record.name }}</a>
<a href="javascript:;" @click="editPost(record.id)"></a>
<a-divider type="vertical" />
<a href="javascript:;">Delete</a>
<a-divider type="vertical" />
<a href="javascript:;" class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
<a href="javascript:;" @click="deletePost(record.id)"></a>
</span>
</a-table>
</div>
@ -71,6 +65,7 @@
</template>
<script>
import categoryApi from '@/api/category'
export default {
name: 'PostList',
components: {},
@ -120,10 +115,26 @@ export default {
selectedRowKeys: [],
selectedRows: [],
options: {},
optionAlertShow: false
optionAlertShow: false,
categories: [],
postStatus: 0
}
},
created() {},
methods: {}
created() {
this.loadCategories()
},
methods: {
loadCategories() {
categoryApi.listAll().then(response => {
this.categories = response.data.data
})
},
editPost(id) {
this.$message.success('编辑')
},
deletePost(id) {
this.$message.success('删除')
}
}
}
</script>

View File

@ -1,7 +1,13 @@
<template>
<div class="page-header-index-wide">
<a-row :gutter="12">
<a-col :xl="10" :lg="10" :md="10" :sm="24" :xs="24">
<a-col
:xl="10"
:lg="10"
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="添加标签">
<a-form layout="horizontal">
<a-form-item label="名称:" help="*页面上所显示的名称">
@ -16,7 +22,13 @@
</a-form>
</a-card>
</a-col>
<a-col :xl="14" :lg="14" :md="14" :sm="24" :xs="24">
<a-col
:xl="14"
:lg="14"
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }">
<a-card title="所有标签">
<a-tooltip placement="topLeft" v-for="tag in tags" :key="tag.id">
<template slot="title">

View File

@ -1,179 +1,81 @@
<template>
<div class="page-header-index-wide">
<a-row>
<a-col
:xl="24"
:lg="24"
:md="24"
:sm="24"
:xs="24"
>
<a-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<div class="card-container">
<a-tabs type="card">
<a-tab-pane
tab="常规设置"
key="general"
>
<a-tab-pane tab="常规设置" key="general">
<a-form layout="vertical">
<a-form-item
label="博客标题:"
:wrapper-col="wrapperCol"
>
<a-form-item label="博客标题:" :wrapper-col="wrapperCol">
<a-input v-model="options.blog_title" />
</a-form-item>
<a-form-item
label="博客地址:"
:wrapper-col="wrapperCol"
>
<a-form-item label="博客地址:" :wrapper-col="wrapperCol">
<a-input v-model="options.blog_url" />
</a-form-item>
<a-form-item
label="LOGO"
:wrapper-col="wrapperCol"
>
<a-form-item label="LOGO" :wrapper-col="wrapperCol">
<a-input v-model="options.blog_logo" />
</a-form-item>
<a-form-item
label="Favicon"
:wrapper-col="wrapperCol"
>
<a-form-item label="Favicon" :wrapper-col="wrapperCol">
<a-input v-model="options.blog_favicon" />
</a-form-item>
<a-form-item
label="页脚信息:"
:wrapper-col="wrapperCol"
>
<a-input
type="textarea"
:autosize="{ minRows: 5 }"
v-model="options.blog_footer_info"
/>
<a-form-item label="页脚信息:" :wrapper-col="wrapperCol">
<a-input type="textarea" :autosize="{ minRows: 5 }" v-model="options.blog_footer_info" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="SEO设置"
key="seo"
>
<a-tab-pane tab="SEO设置" key="seo">
<a-form layout="vertical">
<a-form-item
label="关键词: "
:wrapper-col="wrapperCol"
>
<a-tooltip
:trigger="['focus']"
placement="right"
title="多个关键词以英文逗号隔开"
>
<a-form-item label="关键词: " :wrapper-col="wrapperCol">
<a-tooltip :trigger="['focus']" placement="right" title="多个关键词以英文逗号隔开">
<a-input v-model="options.seo_keywords" />
</a-tooltip>
</a-form-item>
<a-form-item
label="博客描述:"
:wrapper-col="wrapperCol"
>
<a-form-item label="博客描述:" :wrapper-col="wrapperCol">
<a-input v-model="options.seo_description" />
</a-form-item>
<a-form-item
label="百度推送 Token "
:wrapper-col="wrapperCol"
>
<a-form-item label="百度推送 Token " :wrapper-col="wrapperCol">
<a-input v-model="options.seo_baidu_token" />
</a-form-item>
<a-form-item
label="百度站点验证:"
:wrapper-col="wrapperCol"
>
<a-form-item label="百度站点验证:" :wrapper-col="wrapperCol">
<a-input v-model="options.seo_verification_baidu" />
</a-form-item>
<a-form-item
label="Google 站点验证:"
:wrapper-col="wrapperCol"
>
<a-form-item label="Google 站点验证:" :wrapper-col="wrapperCol">
<a-input v-model="options.seo_verification_google" />
</a-form-item>
<a-form-item
label="Bing 站点验证:"
:wrapper-col="wrapperCol"
>
<a-form-item label="Bing 站点验证:" :wrapper-col="wrapperCol">
<a-input v-model="options.seo_verification_bing" />
</a-form-item>
<a-form-item
label="360 站点验证:"
:wrapper-col="wrapperCol"
>
<a-form-item label="360 站点验证:" :wrapper-col="wrapperCol">
<a-input v-model="options.seo_verification_qihu" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="文章设置"
key="post"
>
<a-tab-pane tab="文章设置" key="post">
<a-form layout="vertical">
<a-form-item
label="首页显示条数:"
:wrapper-col="wrapperCol"
>
<a-input
type="number"
defaultValue="10"
v-model="options.post_index_page_size"
/>
<a-form-item label="首页显示条数:" :wrapper-col="wrapperCol">
<a-input type="number" defaultValue="10" v-model="options.post_index_page_size" />
</a-form-item>
<a-form-item
label="RSS 显示条数:"
:wrapper-col="wrapperCol"
>
<a-input
type="number"
defaultValue="10"
v-model="options.rss_page_size"
/>
<a-form-item label="RSS 显示条数:" :wrapper-col="wrapperCol">
<a-input type="number" defaultValue="10" v-model="options.rss_page_size" />
</a-form-item>
<a-form-item
label="文章摘要字数:"
:wrapper-col="wrapperCol"
>
<a-input
type="number"
defaultValue="200"
v-model="options.post_summary_length"
/>
<a-form-item label="文章摘要字数:" :wrapper-col="wrapperCol">
<a-input type="number" defaultValue="200" v-model="options.post_summary_length" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="评论设置"
key="comment"
>
<a-tab-pane tab="评论设置" key="comment">
<a-form layout="vertical">
<a-form-item
label="评论者头像:"
:wrapper-col="wrapperCol"
>
<a-select
defaultValue="mm"
v-model="options.comment_gavatar_default"
>
<a-form-item label="评论者头像:" :wrapper-col="wrapperCol">
<a-select defaultValue="mm" v-model="options.comment_gavatar_default">
<a-select-option value="mm">默认</a-select-option>
<a-select-option value="identicon">抽象几何图形</a-select-option>
<a-select-option value="monsterid">小怪物</a-select-option>
@ -183,10 +85,7 @@
<a-select-option value="blank">不显示头像</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="评论审核后才显示:"
:wrapper-col="wrapperCol"
>
<a-form-item label="评论审核后才显示:" :wrapper-col="wrapperCol">
<a-radio-group
v-decorator="['radio-group']"
defaultValue="true"
@ -196,23 +95,13 @@
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="新评论通知:"
:wrapper-col="wrapperCol"
>
<a-radio-group
v-decorator="['radio-group']"
defaultValue="true"
v-model="options.comment_new_notice"
>
<a-form-item label="新评论通知:" :wrapper-col="wrapperCol">
<a-radio-group v-decorator="['radio-group']" defaultValue="true" v-model="options.comment_new_notice">
<a-radio value="true">启用</a-radio>
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="评论审核通过通知对方:"
:wrapper-col="wrapperCol"
>
<a-form-item label="评论审核通过通知对方:" :wrapper-col="wrapperCol">
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
@ -222,10 +111,7 @@
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="评论回复通知对方:"
:wrapper-col="wrapperCol"
>
<a-form-item label="评论回复通知对方:" :wrapper-col="wrapperCol">
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
@ -235,10 +121,7 @@
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="API 评论开关:"
:wrapper-col="wrapperCol"
>
<a-form-item label="API 评论开关:" :wrapper-col="wrapperCol">
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
@ -248,119 +131,56 @@
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="每页显示条数: "
:wrapper-col="wrapperCol"
>
<a-input
type="number"
defaultValue="10"
v-model="options.comment_page_size"
/>
<a-form-item label="每页显示条数: " :wrapper-col="wrapperCol">
<a-input type="number" defaultValue="10" v-model="options.comment_page_size" />
</a-form-item>
<a-form-item
label="占位提示:"
:wrapper-col="wrapperCol"
>
<a-form-item label="占位提示:" :wrapper-col="wrapperCol">
<a-input v-model="options.comment_content_placeholder" />
</a-form-item>
<a-form-item
label="自定义样式:"
:wrapper-col="wrapperCol"
>
<a-input
type="textarea"
:autosize="{ minRows: 5 }"
v-model="options.comment_custom_style"
/>
<a-form-item label="自定义样式:" :wrapper-col="wrapperCol">
<a-input type="textarea" :autosize="{ minRows: 5 }" v-model="options.comment_custom_style" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="附件设置"
key="attachment"
>
<a-tab-pane tab="附件设置" key="attachment">
<a-form layout="vertical">
<a-form-item
label="存储位置:"
:wrapper-col="wrapperCol"
>
<a-select
defaultValue="local"
@change="handleAttachChange"
v-model="options.attachment_type"
>
<a-form-item label="存储位置:" :wrapper-col="wrapperCol">
<a-select defaultValue="local" @change="handleAttachChange" v-model="options.attachment_type">
<a-select-option value="local">本地</a-select-option>
<a-select-option value="smms">SM.MS</a-select-option>
<a-select-option value="ypyun">又拍云</a-select-option>
<a-select-option value="qnyun">七牛云</a-select-option>
<a-select-option value="smms">SM.MS</a-select-option>
<a-select-option value="aliyun">阿里云</a-select-option>
</a-select>
</a-form-item>
<div
class="upyunForm"
v-show="upyunFormHidden"
>
<a-form-item
label="域名:"
:wrapper-col="wrapperCol"
>
<a-tooltip
:trigger="['focus']"
placement="right"
title="需要加上 http:// 或者 https://"
>
<div class="upyunForm" v-show="upyunFormHidden">
<a-form-item label="域名:" :wrapper-col="wrapperCol">
<a-tooltip :trigger="['focus']" placement="right" title="需要加上 http:// 或者 https://">
<a-input v-model="options.oss_upyun_domain" />
</a-tooltip>
</a-form-item>
<a-form-item
label="空间名称:"
:wrapper-col="wrapperCol"
>
<a-form-item label="空间名称:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_upyun_bucket" />
</a-form-item>
<a-form-item
label="操作员名称:"
:wrapper-col="wrapperCol"
>
<a-form-item label="操作员名称:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_upyun_operator" />
</a-form-item>
<a-form-item
label="操作员密码:"
:wrapper-col="wrapperCol"
>
<a-form-item label="操作员密码:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_upyun_password" />
</a-form-item>
<a-form-item
label="文件目录:"
:wrapper-col="wrapperCol"
>
<a-form-item label="文件目录:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_upyun_source" />
</a-form-item>
<a-form-item
label="处理策略:"
:wrapper-col="wrapperCol"
>
<a-form-item label="处理策略:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_upyun_small_url" />
</a-form-item>
</div>
<div
class="qiniuForm"
v-show="qiniuFormHidden"
>
<a-form-item
label="区域:"
:wrapper-col="wrapperCol"
>
<a-select
defaultValue="auto"
v-model="options.oss_qiniu_zone"
>
<div class="qnyunForm" v-show="qnyunFormHidden">
<a-form-item label="区域:" :wrapper-col="wrapperCol">
<a-select defaultValue="auto" v-model="options.oss_qiniu_zone">
<a-select-option value="auto">自动选择</a-select-option>
<a-select-option value="z0">华东</a-select-option>
<a-select-option value="z1">华北</a-select-option>
@ -369,149 +189,90 @@
<a-select-option value="as0">东南亚</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="域名:"
:wrapper-col="wrapperCol"
>
<a-tooltip
:trigger="['focus']"
placement="right"
title="需要加上 http:// 或者 https://"
>
<a-form-item label="域名:" :wrapper-col="wrapperCol">
<a-tooltip :trigger="['focus']" placement="right" title="需要加上 http:// 或者 https://">
<a-input v-model="options.oss_qiniu_domain" />
</a-tooltip>
</a-form-item>
<a-form-item
label="Access Key"
:wrapper-col="wrapperCol"
>
<a-form-item label="Access Key" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_qiniu_access_key" />
</a-form-item>
<a-form-item
label="Secret Key"
:wrapper-col="wrapperCol"
>
<a-form-item label="Secret Key" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_qiniu_secret_key" />
</a-form-item>
<a-form-item
label="Bucket"
:wrapper-col="wrapperCol"
>
<a-form-item label="Bucket" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_qiniu_bucket" />
</a-form-item>
<a-form-item
label="处理策略:"
:wrapper-col="wrapperCol"
>
<a-form-item label="处理策略:" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_qiniu_small_url" />
</a-form-item>
</div>
<div class="aliyunForm" v-show="aliyunFormHidden">
<a-form-item label="域名:" :wrapper-col="wrapperCol">
<a-tooltip :trigger="['focus']" placement="right" title="需要加上 http:// 或者 https://">
<a-input v-model="options.oss_aliyun_endpoint" />
</a-tooltip>
</a-form-item>
<a-form-item label="Access Key" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_aliyun_access_key" />
</a-form-item>
<a-form-item label="Access Secret" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_aliyun_access_secret" />
</a-form-item>
<a-form-item label="Bucket" :wrapper-col="wrapperCol">
<a-input v-model="options.oss_aliyun_bucket_name" />
</a-form-item>
</div>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="SMTP 服务"
key="smtp"
>
<a-tab-pane tab="SMTP 服务" key="smtp">
<a-tabs defaultActiveKey="1">
<a-tab-pane
tab="发信设置"
key="1"
>
<a-tab-pane tab="发信设置" key="1">
<a-form layout="vertical">
<a-form-item
label="是否启用:"
:wrapper-col="wrapperCol"
>
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
v-model="options.email_enabled"
>
<a-form-item label="是否启用:" :wrapper-col="wrapperCol">
<a-radio-group v-decorator="['radio-group']" defaultValue="false" v-model="options.email_enabled">
<a-radio value="true">启用</a-radio>
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="SMTP 地址:"
:wrapper-col="wrapperCol"
>
<a-form-item label="SMTP 地址:" :wrapper-col="wrapperCol">
<a-input v-model="options.email_host" />
</a-form-item>
<a-form-item
label="发送协议:"
:wrapper-col="wrapperCol"
>
<a-form-item label="发送协议:" :wrapper-col="wrapperCol">
<a-input v-model="options.email_protocol" />
</a-form-item>
<a-form-item
label="SSL 端口:"
:wrapper-col="wrapperCol"
>
<a-form-item label="SSL 端口:" :wrapper-col="wrapperCol">
<a-input v-model="options.email_ssl_port" />
</a-form-item>
<a-form-item
label="邮箱账号:"
:wrapper-col="wrapperCol"
>
<a-form-item label="邮箱账号:" :wrapper-col="wrapperCol">
<a-input v-model="options.email_username" />
</a-form-item>
<a-form-item
label="邮箱密码:"
:wrapper-col="wrapperCol"
>
<a-tooltip
:trigger="['focus']"
placement="right"
title="部分邮箱可能是授权码"
>
<a-form-item label="邮箱密码:" :wrapper-col="wrapperCol">
<a-tooltip :trigger="['focus']" placement="right" title="部分邮箱可能是授权码">
<a-input v-model="options.email_password" />
</a-tooltip>
</a-form-item>
<a-form-item
label="发件人:"
:wrapper-col="wrapperCol"
>
<a-form-item label="发件人:" :wrapper-col="wrapperCol">
<a-input v-model="options.email_from_name" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane
tab="发送测试"
key="2"
>
<a-tab-pane tab="发送测试" key="2">
<a-form layout="vertical">
<a-form-item
label="收件人:"
:wrapper-col="wrapperCol"
>
<a-form-item label="收件人:" :wrapper-col="wrapperCol">
<a-input />
</a-form-item>
<a-form-item
label="主题:"
:wrapper-col="wrapperCol"
>
<a-form-item label="主题:" :wrapper-col="wrapperCol">
<a-input />
</a-form-item>
<a-form-item
label="内容:"
:wrapper-col="wrapperCol"
>
<a-input
type="textarea"
:autosize="{ minRows: 5 }"
/>
<a-form-item label="内容:" :wrapper-col="wrapperCol">
<a-input type="textarea" :autosize="{ minRows: 5 }" />
</a-form-item>
<a-form-item>
<a-button type="primary">发送</a-button>
@ -520,45 +281,22 @@
</a-tab-pane>
</a-tabs>
</a-tab-pane>
<a-tab-pane
tab="其他设置"
key="other"
>
<a-tab-pane tab="其他设置" key="other">
<a-form layout="vertical">
<a-form-item
label="API服务"
:wrapper-col="wrapperCol"
>
<a-radio-group
v-decorator="['radio-group']"
defaultValue="false"
v-model="options.api_enabled"
>
<a-form-item label="API服务" :wrapper-col="wrapperCol">
<a-radio-group v-decorator="['radio-group']" defaultValue="false" v-model="options.api_enabled">
<a-radio value="true">启用</a-radio>
<a-radio value="false">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="Access key"
:wrapper-col="wrapperCol"
>
<a-form-item label="Access key" :wrapper-col="wrapperCol">
<a-input v-model="options.blog_api_access_key" />
</a-form-item>
<a-form-item
label="统计代码:"
:wrapper-col="wrapperCol"
>
<a-input
type="textarea"
:autosize="{ minRows: 5 }"
v-model="options.blog_statistics_code"
/>
<a-form-item label="统计代码:" :wrapper-col="wrapperCol">
<a-input type="textarea" :autosize="{ minRows: 5 }" v-model="options.blog_statistics_code" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="saveOptions"
>保存</a-button>
<a-button type="primary" @click="saveOptions"></a-button>
</a-form-item>
</a-form>
</a-tab-pane>
@ -579,7 +317,8 @@ export default {
xs: { span: 24 }
},
upyunFormHidden: false,
qiniuFormHidden: false,
qnyunFormHidden: false,
aliyunFormHidden: false,
options: []
}
},
@ -603,14 +342,22 @@ export default {
case 'local':
case 'smms':
this.upyunFormHidden = false
this.qiniuFormHidden = false
this.qnyunFormHidden = false
this.aliyunFormHidden = false
break
case 'ypyun':
this.upyunFormHidden = true
this.qiniuFormHidden = false
this.qnyunFormHidden = false
this.aliyunFormHidden = false
break
case 'qnyun':
this.qiniuFormHidden = true
this.qnyunFormHidden = true
this.upyunFormHidden = false
this.aliyunFormHidden = false
break
case 'aliyun':
this.aliyunFormHidden = true
this.qnyunFormHidden = false
this.upyunFormHidden = false
break
}

70
src/views/user/Login.vue Normal file
View File

@ -0,0 +1,70 @@
<template>
<div class="container loginForm">
<div class="loginLogo animated fadeInUp">
Halo
</div>
<div class="loginBody animated">
<a-form layout="vertical">
<a-form-item class="animated fadeInUp" :style="{'animation-delay': '0.1s'}">
<a-input placeholder="用户名/邮箱"></a-input>
</a-form-item>
<a-form-item class="animated fadeInUp" :style="{'animation-delay': '0.2s'}">
<a-input type="password" placeholder="密码"></a-input>
</a-form-item>
<a-form-item class="animated fadeInUp" :style="{'animation-delay': '0.3s'}">
<a-button type="primary" block="true">登录</a-button>
</a-form-item>
</a-form>
</div>
</div>
</template>
<script>
export default {
beforeCreate() {
this.form = this.$form.createForm(this)
},
methods: {
handleSubmit(e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values)
}
})
}
}
}
</script>
<style>
body {
background-color: #f5f5f5;
}
* {
outline: 0;
}
.container {
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.loginForm {
max-width: 380px;
margin-top: 10%;
}
.loginLogo {
font-size: 56px;
text-align: center;
margin-bottom: 25px;
font-weight: 500;
color: #444;
text-shadow: #b2baba 0.1em 0.1em 0.2em;
}
.loginBody {
padding: 20px;
background-color: #fff;
-o-box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1);
box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1);
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<div class="page-header-index-wide page-header-wrapper-grid-content-main">
<a-row :gutter="24">
<a-col :md="24" :lg="10">
<a-card :bordered="false" :bodyStyle="{ padding: '16' }">
<a-col :lg="10" :md="24" :style="{ 'padding-bottom': '12px' }">
<a-card :bodyStyle="{ padding: '16' }" :bordered="false">
<div class="profile-center-avatarHolder">
<div class="avatar">
<img :src="user.avatar || 'https://gravatar.loli.net/avatar/?s=256&d=mm'" />
@ -22,7 +22,7 @@
</div>
<a-divider />
<div class="general-profile">
<a-list itemLayout="horizontal" :loading="countsLoading">
<a-list :loading="countsLoading" itemLayout="horizontal">
<a-list-item>累计发表了 {{ counts.postCount || 0 }} 篇文章</a-list-item>
<a-list-item>累计创建了 {{ counts.linkCount || 0 }} 个标签</a-list-item>
<a-list-item>累计获得了 {{ counts.commentCount || 0 }} 条评论</a-list-item>
@ -33,11 +33,11 @@
</div>
</a-card>
</a-col>
<a-col :md="24" :lg="14">
<a-card :bordered="false" title="个人资料" :bodyStyle="{ padding: '0' }">
<a-col :lg="14" :md="24" :style="{ 'padding-bottom': '12px' }">
<a-card :bodyStyle="{ padding: '0' }" :bordered="false" title="个人资料">
<div class="card-container">
<a-tabs type="card">
<a-tab-pane tab="基本资料" key="1">
<a-tab-pane key="1" tab="基本资料">
<a-form layout="vertical">
<a-form-item label="用户名:">
<a-input v-model="user.username" />
@ -49,31 +49,31 @@
<a-input v-model="user.email" />
</a-form-item>
<a-form-item label="个人说明:">
<a-input type="textarea" :autosize="{ minRows: 5 }" v-model="user.description" />
<a-input :autosize="{ minRows: 5 }" type="textarea" v-model="user.description" />
</a-form-item>
<a-form-item>
<a-button type="primary" @click="updateProfile"></a-button>
<a-button @click="updateProfile" type="primary">保存</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane tab="密码" key="2">
<a-tab-pane key="2" tab="密码">
<a-form layout="vertical">
<a-form-item label="原密码:">
<a-input v-model="passwordParam.oldPassword" type="password" />
<a-input type="password" v-model="passwordParam.oldPassword" />
</a-form-item>
<a-form-item label="新密码:">
<a-input v-model="passwordParam.newPassword" type="password" />
<a-input type="password" v-model="passwordParam.newPassword" />
</a-form-item>
<a-form-item label="确认密码:">
<a-input v-model="passwordParam.confirmPassword" type="password" />
<a-input type="password" v-model="passwordParam.confirmPassword" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
:disabled="passwordUpdateButtonDisabled"
@click="updatePassword"
>确认更改</a-button
>
type="primary"
>确认更改
</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
@ -88,6 +88,7 @@
<script>
import userApi from '@/api/user'
import adminApi from '@/api/admin'
export default {
components: {},
data() {
@ -127,7 +128,7 @@ export default {
updatePassword() {
// Check confirm password
if (this.passwordParam.newPassword !== this.passwordParam.confirmPassword) {
this.$message.error('确认密码和新密码不匹配')
this.$message.error('确认密码和新密码不匹配')
return
}
@ -136,6 +137,7 @@ export default {
updateProfile() {
userApi.updateProfile(this.user).then(response => {
this.user = response.data.data
this.$message.success('资料更新成功!')
})
}
}
@ -148,9 +150,11 @@ export default {
height: 100%;
min-height: 100%;
transition: 0.3s;
.profile-center-avatarHolder {
text-align: center;
margin-bottom: 24px;
& > .avatar {
margin: 0 auto;
width: 104px;
@ -158,11 +162,13 @@ export default {
margin-bottom: 20px;
border-radius: 50%;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.username {
color: rgba(0, 0, 0, 0.85);
font-size: 20px;
@ -171,12 +177,14 @@ export default {
margin-bottom: 4px;
}
}
.profile-center-detail {
p {
margin-bottom: 8px;
padding-left: 26px;
position: relative;
}
i {
position: absolute;
height: 14px;
@ -192,6 +200,7 @@ export default {
.card-container {
background: #f5f5f5;
}
.card-container > .ant-tabs-card > .ant-tabs-content {
margin-top: -16px;
}
@ -214,6 +223,7 @@ export default {
border-color: #fff;
background: #fff;
}
.ant-form-vertical .ant-form-item {
padding-bottom: 0;
}

View File

@ -2766,6 +2766,11 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror@^5.22.0:
version "5.45.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.45.0.tgz#db5ebbb3bf44028c684053f3954d011efcec27ad"
integrity sha512-c19j644usCE8gQaXa0jqn2B/HN9MnB2u6qPIrrhrMkB+QAP42y8G4QnTwuwbVSoUS1jEl7JU9HZMGhCDL0nsAw==
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@ -10346,6 +10351,13 @@ vue-clipboard2@^0.3.0:
dependencies:
clipboard "^2.0.0"
vue-codemirror-lite@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/vue-codemirror-lite/-/vue-codemirror-lite-1.0.4.tgz#48a5cd7d17c0914503c8cd9d9b56b438e49c3410"
integrity sha1-SKXNfRfAkUUDyM2dm1a0OOScNBA=
dependencies:
codemirror "^5.22.0"
vue-cropper@0.4.9:
version "0.4.9"
resolved "https://registry.yarnpkg.com/vue-cropper/-/vue-cropper-0.4.9.tgz#fe650f32516ecf29014bbd4a9079191c8dc5a5ae"
@ -10375,6 +10387,11 @@ vue-eslint-parser@^5.0.0:
esquery "^1.0.1"
lodash "^4.17.11"
vue-fragment@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/vue-fragment/-/vue-fragment-1.5.0.tgz#7739463b7680633f2b671aec9f1bfdd9a436da33"
integrity sha512-nobmbbOSOx59fm7U00BDz14Yvqitwx7NPQGYDTKg3+dNDGTDCRNy/q2kfr5hV4S0l4fQG0kvC+rbCmENLmHUSA==
vue-hot-reload-api@^2.3.0:
version "2.3.3"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"