mirror of https://github.com/halo-dev/halo-admin
目录结构更改
parent
c606c8917b
commit
85b449d4bb
|
@ -7252,7 +7252,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7273,12 +7274,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -7293,17 +7296,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -7420,7 +7426,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -7432,6 +7439,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -7446,6 +7454,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -7453,12 +7462,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -7477,6 +7488,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -7557,7 +7569,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -7569,6 +7582,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -7654,7 +7668,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -7690,6 +7705,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -7709,6 +7725,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -7752,12 +7769,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -10691,6 +10710,11 @@
|
|||
"integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.kebabcase": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "http://registry.npm.taobao.org/lodash.kebabcase/download/lodash.kebabcase-4.1.1.tgz",
|
||||
|
@ -13525,7 +13549,8 @@
|
|||
"version": "4.0.8",
|
||||
"resolved": "http://registry.npm.taobao.org/rx-lite/download/rx-lite-4.0.8.tgz",
|
||||
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"rx-lite-aggregates": {
|
||||
"version": "4.0.8",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"ant-design-vue": "~1.3.7",
|
||||
"axios": "^0.18.0",
|
||||
"enquire.js": "^2.1.6",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mavon-editor": "^2.7.2",
|
||||
"moment": "^2.24.0",
|
||||
"nprogress": "^0.2.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="['detail-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
|
||||
<div :class="['description-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
|
||||
<div v-if="title" class="title">{{ title }}</div>
|
||||
<a-row>
|
||||
<slot></slot>
|
||||
|
@ -24,7 +24,7 @@ const Item = {
|
|||
type: Number
|
||||
}
|
||||
},
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<Col {...{ props: responsive[this.col] }}>
|
||||
<div class="term">{this.$props.term}</div>
|
||||
|
@ -69,7 +69,7 @@ export default {
|
|||
default: 'horizontal'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
provide () {
|
||||
return {
|
||||
col: this.col > 4 ? 4 : this.col
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.detail-list {
|
||||
.description-list {
|
||||
|
||||
.title {
|
||||
color: rgba(0,0,0,.85);
|
|
@ -0,0 +1,2 @@
|
|||
import DescriptionList from './DescriptionList'
|
||||
export default DescriptionList
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div class="exception">
|
||||
<div class="img">
|
||||
<img :src="config[type].img"/>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1>{{ config[type].title }}</h1>
|
||||
<div class="desc">{{ config[type].desc }}</div>
|
||||
<div class="action">
|
||||
<a-button type="primary" @click="handleToHome">返回首页</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import types from './type'
|
||||
|
||||
export default {
|
||||
name: 'Exception',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: '404'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: types
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleToHome() {
|
||||
this.$router.push({ name: 'dashboard' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.exception {
|
||||
min-height: 500px;
|
||||
height: 80%;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin-top: 150px;
|
||||
.img {
|
||||
display: inline-block;
|
||||
padding-right: 52px;
|
||||
zoom: 1;
|
||||
img {
|
||||
height: 360px;
|
||||
max-width: 430px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: inline-block;
|
||||
flex: auto;
|
||||
h1 {
|
||||
color: #434e59;
|
||||
font-size: 72px;
|
||||
font-weight: 600;
|
||||
line-height: 72px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.desc {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.exception {
|
||||
margin-top: 30px;
|
||||
.img {
|
||||
padding-right: unset;
|
||||
|
||||
img {
|
||||
height: 40%;
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
import ExceptionPage from './ExceptionPage.vue'
|
||||
export default ExceptionPage
|
|
@ -0,0 +1,19 @@
|
|||
const types = {
|
||||
403: {
|
||||
img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
|
||||
title: '403',
|
||||
desc: '抱歉,你无权访问该页面'
|
||||
},
|
||||
404: {
|
||||
img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
|
||||
title: '404',
|
||||
desc: '抱歉,你访问的页面不存在或仍在开发中'
|
||||
},
|
||||
500: {
|
||||
img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
|
||||
title: '500',
|
||||
desc: '抱歉,服务器出错了'
|
||||
}
|
||||
}
|
||||
|
||||
export default types
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
固定在底部的工具栏。
|
||||
|
||||
|
||||
|
||||
## 何时使用
|
||||
|
||||
固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
|
@ -19,6 +22,8 @@ export default {
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示
|
||||
|
||||
```html
|
||||
|
@ -26,9 +31,7 @@ export default {
|
|||
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
||||
</footer-tool-bar>
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```html
|
||||
<footer-tool-bar extra="扩展信息提示">
|
||||
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
||||
|
@ -38,8 +41,8 @@ export default {
|
|||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------------- | -------------------- | -------------- | ------ |
|
||||
| children (slot) | 工具栏内容,向右对齐 | - | - |
|
||||
| extra | 额外信息,向左对齐 | String, Object | - |
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
children (slot) | 工具栏内容,向右对齐 | - | -
|
||||
extra | 额外信息,向左对齐 | String, Object | -
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import GlobalFooter from './GlobalFooter'
|
||||
export default GlobalFooter
|
|
@ -0,0 +1,123 @@
|
|||
<template>
|
||||
<transition name="showHeader">
|
||||
<div v-if="visible" class="header-animat">
|
||||
<a-layout-header
|
||||
v-if="visible"
|
||||
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
|
||||
:style="{ padding: '0' }">
|
||||
<div v-if="mode === 'sidemenu'" class="header">
|
||||
<a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/>
|
||||
<a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle"/>
|
||||
<user-menu></user-menu>
|
||||
</div>
|
||||
<div v-else :class="['top-nav-header-index', theme]">
|
||||
<div class="header-index-wide">
|
||||
<div class="header-index-left">
|
||||
<logo class="top-nav-header" :show-title="device !== 'mobile'"/>
|
||||
<s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" />
|
||||
<a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
|
||||
</div>
|
||||
<user-menu class="header-index-right"></user-menu>
|
||||
</div>
|
||||
</div>
|
||||
</a-layout-header>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UserMenu from '../Tools/UserMenu'
|
||||
import SMenu from '../Menu/'
|
||||
import Logo from '../Tools/Logo'
|
||||
import { mixin } from '@/utils/mixin'
|
||||
|
||||
export default {
|
||||
name: 'GlobalHeader',
|
||||
components: {
|
||||
UserMenu,
|
||||
SMenu,
|
||||
Logo
|
||||
},
|
||||
mixins: [mixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
// sidemenu, topmenu
|
||||
default: 'sidemenu'
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
device: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'desktop'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: true,
|
||||
oldScrollTop: 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.body.addEventListener('scroll', this.handleScroll, { passive: true })
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
if (!this.autoHideHeader) {
|
||||
return
|
||||
}
|
||||
|
||||
const scrollTop = document.body.scrollTop + document.documentElement.scrollTop
|
||||
if (!this.ticking) {
|
||||
this.ticking = true
|
||||
requestAnimationFrame(() => {
|
||||
if (this.oldScrollTop > scrollTop) {
|
||||
this.visible = true
|
||||
} else if (scrollTop > 300 && this.visible) {
|
||||
this.visible = false
|
||||
} else if (scrollTop < 300 && !this.visible) {
|
||||
this.visible = true
|
||||
}
|
||||
this.oldScrollTop = scrollTop
|
||||
this.ticking = false
|
||||
})
|
||||
}
|
||||
},
|
||||
toggle() {
|
||||
this.$emit('toggle')
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.body.removeEventListener('scroll', this.handleScroll, true)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.header-animat{
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
}
|
||||
.showHeader-enter-active {
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.showHeader-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
.showHeader-enter, .showHeader-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
import GlobalHeader from './GlobalHeader'
|
||||
export default GlobalHeader
|
|
@ -1,53 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-tabs>
|
||||
<a-tab-pane v-for="(v, i) in icons" :tab="v.title" :key="i">
|
||||
<ul>
|
||||
<li v-for="(icon, key) in v.icons" :key="`${v.title}-${key}`" :class="{ 'active': selectedIcon==icon }">
|
||||
<a-icon :type="icon" :style="{ fontSize: '36px' }" @click="handleSelectedIcon(icon)" />
|
||||
</li>
|
||||
</ul>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from './icons'
|
||||
|
||||
export default {
|
||||
name: 'IconSelect',
|
||||
data() {
|
||||
return {
|
||||
selectedIcon: '',
|
||||
icons
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectedIcon(icon) {
|
||||
this.selectedIcon = icon
|
||||
this.$emit('change', icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
ul{
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
overflow-y: scroll;
|
||||
height: 250px;
|
||||
li{
|
||||
display: inline-block;
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.active{
|
||||
box-shadow: 0px 0px 5px 2px #888888;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,48 +0,0 @@
|
|||
IconSelector
|
||||
====
|
||||
|
||||
> 图标选择组件,常用于为某一个数据设定一个图标时使用
|
||||
> eg: 设定菜单列表时,为每个菜单设定一个图标
|
||||
|
||||
该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装
|
||||
|
||||
|
||||
|
||||
### 使用方式
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<icon-selector @change="handleIconChange"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconSelector from '@/components/IconSelector'
|
||||
|
||||
export default {
|
||||
name: 'YourView',
|
||||
components: {
|
||||
IconSelector
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleIconChange (icon) {
|
||||
console.log('change Icon', icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 事件
|
||||
|
||||
|
||||
| 名称 | 说明 | 类型 | 默认值 |
|
||||
| ------ | -------------------------- | ------ | ------ |
|
||||
| change | 当改变了 `icon` 选中项触发 | String | - |
|
|
@ -1,26 +0,0 @@
|
|||
export default [
|
||||
{
|
||||
title: '方向性图标',
|
||||
icons: ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrows-alt', 'down', 'up', 'left', 'right', 'caret-up', 'caret-down', 'caret-left', 'caret-right', 'up-circle', 'down-circle', 'left-circle', 'right-circle', 'double-right', 'double-left', 'vertical-left', 'vertical-right', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap', 'swap-left', 'swap-right', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'play-circle', 'up-square', 'down-square', 'left-square', 'right-square', 'login', 'logout', 'menu-fold', 'menu-unfold', 'border-bottom', 'border-horizontal', 'border-inner', 'border-left', 'border-right', 'border-top', 'border-verticle', 'pic-center', 'pic-left', 'pic-right', 'radius-bottomleft', 'radius-bottomright', 'radius-upleft', 'fullscreen', 'fullscreen-exit']
|
||||
},
|
||||
{
|
||||
title: '提示建议性图标',
|
||||
icons: ['question', 'question-circle', 'plus', 'plus-circle', 'pause', 'pause-circle', 'minus', 'minus-circle', 'plus-square', 'minus-square', 'info', 'info-circle', 'exclamation', 'exclamation-circle', 'close', 'close-circle', 'close-square', 'check', 'check-circle', 'check-square', 'clock-circle', 'warning', 'issues-close', 'stop']
|
||||
},
|
||||
{
|
||||
title: '编辑类图标',
|
||||
icons: ['edit', 'form', 'copy', 'scissor', 'delete', 'snippets', 'diff', 'highlight', 'align-center', 'align-left', 'align-right', 'bg-colors', 'bold', 'italic', 'underline', 'strikethrough', 'redo', 'undo', 'zoom-in', 'zoom-out', 'font-colors', 'font-size', 'line-height', 'colum-height', 'dash', 'small-dash', 'sort-ascending', 'sort-descending', 'drag', 'ordered-list', 'radius-setting']
|
||||
},
|
||||
{
|
||||
title: '数据类图标',
|
||||
icons: ['area-chart', 'pie-chart', 'bar-chart', 'dot-chart', 'line-chart', 'radar-chart', 'heat-map', 'fall', 'rise', 'stock', 'box-plot', 'fund', 'sliders']
|
||||
},
|
||||
{
|
||||
title: '网站通用图标',
|
||||
icons: ['lock', 'unlock', 'bars', 'book', 'calendar', 'cloud', 'cloud-download', 'code', 'copy', 'credit-card', 'delete', 'desktop', 'download', 'ellipsis', 'file', 'file-text', 'file-unknown', 'file-pdf', 'file-word', 'file-excel', 'file-jpg', 'file-ppt', 'file-markdown', 'file-add', 'folder', 'folder-open', 'folder-add', 'hdd', 'frown', 'meh', 'smile', 'inbox', 'laptop', 'appstore', 'link', 'mail', 'mobile', 'notification', 'paper-clip', 'picture', 'poweroff', 'reload', 'search', 'setting', 'share-alt', 'shopping-cart', 'tablet', 'tag', 'tags', 'to-top', 'upload', 'user', 'video-camera', 'home', 'loading', 'loading-3-quarters', 'cloud-upload', 'star', 'heart', 'environment', 'eye', 'camera', 'save', 'team', 'solution', 'phone', 'filter', 'exception', 'export', 'customer-service', 'qrcode', 'scan', 'like', 'dislike', 'message', 'pay-circle', 'calculator', 'pushpin', 'bulb', 'select', 'switcher', 'rocket', 'bell', 'disconnect', 'database', 'compass', 'barcode', 'hourglass', 'key', 'flag', 'layout', 'printer', 'sound', 'usb', 'skin', 'tool', 'sync', 'wifi', 'car', 'schedule', 'user-add', 'user-delete', 'usergroup-add', 'usergroup-delete', 'man', 'woman', 'shop', 'gift', 'idcard', 'medicine-box', 'red-envelope', 'coffee', 'copyright', 'trademark', 'safety', 'wallet', 'bank', 'trophy', 'contacts', 'global', 'shake', 'api', 'fork', 'dashboard', 'table', 'profile', 'alert', 'audit', 'branches', 'build', 'border', 'crown', 'experiment', 'fire', 'money-collect', 'property-safety', 'read', 'reconciliation', 'rest', 'security-scan', 'insurance', 'interation', 'safety-certificate', 'project', 'thunderbolt', 'block', 'cluster', 'deployment-unit', 'dollar', 'euro', 'pound', 'file-done', 'file-exclamation', 'file-protect', 'file-search', 'file-sync', 'gateway', 'gold', 'robot', 'shopping']
|
||||
},
|
||||
{
|
||||
title: '品牌和标识',
|
||||
icons: ['android', 'apple', 'windows', 'ie', 'chrome', 'github', 'aliwangwang', 'dingding', 'weibo-square', 'weibo-circle', 'taobao-circle', 'html5', 'weibo', 'twitter', 'wechat', 'youtube', 'alipay-circle', 'taobao', 'skype', 'qq', 'medium-workmark', 'gitlab', 'medium', 'linkedin', 'google-plus', 'dropbox', 'facebook', 'codepen', 'code-sandbox', 'amazon', 'google', 'codepen-circle', 'alipay', 'ant-design', 'aliyun', 'zhihu', 'slack', 'slack-square', 'behance', 'behance-square', 'dribbble', 'dribbble-square', 'instagram', 'yuque', 'alibaba', 'yahoo']
|
||||
}
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
import IconSelector from './IconSelector'
|
||||
export default IconSelector
|
|
@ -18,14 +18,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ALayoutSider from 'ant-design-vue/es/layout/Sider'
|
||||
import Logo from '../tools/Logo'
|
||||
import Logo from '@/components/Tools/Logo'
|
||||
import SMenu from './index'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
|
||||
export default {
|
||||
name: 'SideMenu',
|
||||
components: { ALayoutSider, Logo, SMenu },
|
||||
components: { Logo, SMenu },
|
||||
mixins: [mixin, mixinDevice],
|
||||
props: {
|
||||
mode: {
|
|
@ -101,16 +101,15 @@ export default {
|
|||
},
|
||||
renderMenuItem(menu) {
|
||||
const target = menu.meta.target || null
|
||||
const props = {
|
||||
to: { name: menu.name },
|
||||
target: target
|
||||
}
|
||||
const tag = target && 'a' || 'router-link'
|
||||
const props = { to: { name: menu.name } }
|
||||
const attrs = { href: menu.path, target: menu.meta.target }
|
||||
return (
|
||||
<Item {...{ key: menu.path }}>
|
||||
<router-link {...{ props }}>
|
||||
<tag {...{ props, attrs }}>
|
||||
{this.renderIcon(menu.meta.icon)}
|
||||
<span>{menu.meta.title}</span>
|
||||
</router-link>
|
||||
</tag>
|
||||
</Item>
|
||||
)
|
||||
},
|
|
@ -18,3 +18,8 @@
|
|||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.topmenu.content-width-Fluid .@{multi-tab-wrapper-prefix-cls} {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<a-popover
|
||||
v-model="visible"
|
||||
trigger="click"
|
||||
placement="bottomRight"
|
||||
overlayClassName="header-notice-wrapper"
|
||||
:autoAdjustOverflow="true"
|
||||
:arrowPointAtCenter="true"
|
||||
:overlayStyle="{ width: '300px', top: '50px' }"
|
||||
>
|
||||
<template slot="content">
|
||||
<a-spin :spinning="loadding">
|
||||
<a-tabs>
|
||||
<a-tab-pane tab="通知" key="1">
|
||||
<a-list>
|
||||
<a-list-item>
|
||||
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
|
||||
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
|
||||
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
|
||||
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="消息" key="2">
|
||||
123
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="待办" key="3">
|
||||
123
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</template>
|
||||
<span @click="fetchNotice" class="header-notice">
|
||||
<a-badge count="12">
|
||||
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
|
||||
</a-badge>
|
||||
</span>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HeaderNotice',
|
||||
data() {
|
||||
return {
|
||||
loadding: false,
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchNotice() {
|
||||
if (!this.visible) {
|
||||
this.loadding = true
|
||||
setTimeout(() => {
|
||||
this.loadding = false
|
||||
}, 2000)
|
||||
} else {
|
||||
this.loadding = false
|
||||
}
|
||||
this.visible = !this.visible
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.header-notice-wrapper {
|
||||
top: 50px !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.header-notice{
|
||||
display: inline-block;
|
||||
transition: all 0.3s;
|
||||
|
||||
span {
|
||||
vertical-align: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
import NoticeIcon from './NoticeIcon'
|
||||
export default NoticeIcon
|
|
@ -1,16 +1,7 @@
|
|||
<template>
|
||||
<div class="page-header">
|
||||
<div class="page-header-index-wide">
|
||||
<a-breadcrumb class="breadcrumb">
|
||||
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
>{{ item.meta.title }}</router-link>
|
||||
<span v-else>{{ item.meta.title }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
|
||||
<s-breadcrumb />
|
||||
<div class="detail">
|
||||
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||
<div class="row">
|
||||
|
@ -22,7 +13,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div v-if="avatar" class="avatar">
|
||||
<a-avatar :src="avatar"/>
|
||||
<a-avatar :src="avatar" />
|
||||
</div>
|
||||
<div v-if="this.$slots.content" class="headerContent">
|
||||
<slot name="content"></slot>
|
||||
|
@ -41,7 +32,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Breadcrumb from '@/components/tools/Breadcrumb'
|
||||
import Breadcrumb from '@/components/Tools/Breadcrumb'
|
||||
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
|
@ -50,13 +41,8 @@ export default {
|
|||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
breadcrumb: {
|
||||
type: Array,
|
||||
default: null,
|
||||
type: [String, Boolean],
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
logo: {
|
||||
|
@ -71,36 +57,12 @@ export default {
|
|||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
breadList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
this.breadList = []
|
||||
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
|
||||
|
||||
this.name = this.$route.name
|
||||
this.$route.matched.forEach((item) => {
|
||||
// item.name !== 'index' && this.breadList.push(item)
|
||||
this.breadList.push(item)
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.getBreadcrumb()
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.page-header {
|
||||
background: #fff;
|
||||
padding: 16px 32px 0;
|
||||
|
@ -146,10 +108,9 @@ export default {
|
|||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 500;
|
||||
color: rgba(0,0,0,.85);
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin-bottom: 16px;
|
||||
flex: auto;
|
||||
|
||||
}
|
||||
.logo {
|
||||
width: 28px;
|
||||
|
@ -157,9 +118,10 @@ export default {
|
|||
border-radius: 4px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.content, .headerContent {
|
||||
.content,
|
||||
.headerContent {
|
||||
flex: auto;
|
||||
color: rgba(0,0,0,.45);
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
line-height: 22px;
|
||||
|
||||
.link {
|
||||
|
@ -192,9 +154,7 @@ export default {
|
|||
}
|
||||
|
||||
.mobile .page-header {
|
||||
|
||||
.main {
|
||||
|
||||
.row {
|
||||
flex-wrap: wrap;
|
||||
|
||||
|
@ -203,7 +163,8 @@ export default {
|
|||
margin: 0 2% 8px 0;
|
||||
}
|
||||
|
||||
.content, .headerContent {
|
||||
.content,
|
||||
.headerContent {
|
||||
flex: 0 1 70%;
|
||||
|
||||
.link {
|
|
@ -0,0 +1,2 @@
|
|||
import PageHeader from './PageHeader'
|
||||
export default PageHeader
|
|
@ -0,0 +1,10 @@
|
|||
import { Spin } from 'ant-design-vue'
|
||||
|
||||
export default {
|
||||
name: 'PageLoading',
|
||||
render() {
|
||||
return (<div style={{ paddingTop: 100, textAlign: 'center' }}>
|
||||
<Spin size="large" />
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -154,10 +154,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DetailList from '@/components/tools/DetailList'
|
||||
import SettingItem from '@/components/setting/SettingItem'
|
||||
import DetailList from '@/components/Tools/DetailList'
|
||||
import SettingItem from '@/components/SettingDrawer/SettingItem'
|
||||
import config from '@/config/defaultSettings'
|
||||
import { updateTheme, colorList } from '@/components/tools/setting'
|
||||
import { updateTheme, colorList } from '@/components/Tools/setting'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
|
||||
export default {
|
|
@ -0,0 +1,2 @@
|
|||
import SettingDrawer from './SettingDrawer'
|
||||
export default SettingDrawer
|
|
@ -0,0 +1,95 @@
|
|||
import { message } from 'ant-design-vue/es'
|
||||
// import defaultSettings from '../defaultSettings';
|
||||
|
||||
let lessNodesAppended
|
||||
|
||||
const colorList = [
|
||||
{
|
||||
key: '薄暮', color: '#F5222D'
|
||||
},
|
||||
{
|
||||
key: '火山', color: '#FA541C'
|
||||
},
|
||||
{
|
||||
key: '日暮', color: '#FAAD14'
|
||||
},
|
||||
{
|
||||
key: '明青', color: '#13C2C2'
|
||||
},
|
||||
{
|
||||
key: '极光绿', color: '#52C41A'
|
||||
},
|
||||
{
|
||||
key: '拂晓蓝(默认)', color: '#1890FF'
|
||||
},
|
||||
{
|
||||
key: '极客蓝', color: '#2F54EB'
|
||||
},
|
||||
{
|
||||
key: '酱紫', color: '#722ED1'
|
||||
}
|
||||
]
|
||||
|
||||
const updateTheme = primaryColor => {
|
||||
// Don't compile less in production!
|
||||
/* if (process.env.NODE_ENV === 'production') {
|
||||
return;
|
||||
} */
|
||||
// Determine if the component is remounted
|
||||
if (!primaryColor) {
|
||||
return
|
||||
}
|
||||
const hideMessage = message.loading('正在编译主题!', 0)
|
||||
function buildIt() {
|
||||
if (!window.less) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
window.less
|
||||
.modifyVars({
|
||||
'@primary-color': primaryColor
|
||||
})
|
||||
.then(() => {
|
||||
hideMessage()
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('Failed to update theme')
|
||||
hideMessage()
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
if (!lessNodesAppended) {
|
||||
// insert less.js and color.less
|
||||
const lessStyleNode = document.createElement('link')
|
||||
const lessConfigNode = document.createElement('script')
|
||||
const lessScriptNode = document.createElement('script')
|
||||
lessStyleNode.setAttribute('rel', 'stylesheet/less')
|
||||
lessStyleNode.setAttribute('href', '/color.less')
|
||||
lessConfigNode.innerHTML = `
|
||||
window.less = {
|
||||
async: true,
|
||||
env: 'production',
|
||||
javascriptEnabled: true
|
||||
};
|
||||
`
|
||||
lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js'
|
||||
lessScriptNode.async = true
|
||||
lessScriptNode.onload = () => {
|
||||
buildIt()
|
||||
lessScriptNode.onload = null
|
||||
}
|
||||
document.body.appendChild(lessStyleNode)
|
||||
document.body.appendChild(lessConfigNode)
|
||||
document.body.appendChild(lessScriptNode)
|
||||
lessNodesAppended = true
|
||||
} else {
|
||||
buildIt()
|
||||
}
|
||||
}
|
||||
|
||||
const updateColorWeak = colorWeak => {
|
||||
// document.body.className = colorWeak ? 'colorWeak' : '';
|
||||
colorWeak ? document.body.classList.add('colorWeak') : document.body.classList.remove('colorWeak')
|
||||
}
|
||||
|
||||
export { updateTheme, colorList, updateColorWeak }
|
|
@ -0,0 +1,333 @@
|
|||
Table 重封装组件说明
|
||||
====
|
||||
|
||||
|
||||
封装说明
|
||||
----
|
||||
|
||||
> 基础的使用方式与 API 与 [官方版(Table)](https://vuecomponent.github.io/ant-design-vue/components/table-cn/) 本一致,在其基础上,封装了加载数据的方法。
|
||||
>
|
||||
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 Table 组件传递绑定 `:data="Promise"` 对象即可
|
||||
|
||||
该 `table` 由 [@Saraka](https://github.com/saraka-tsukai) 完成封装
|
||||
|
||||
|
||||
例子1
|
||||
----
|
||||
(基础使用)
|
||||
|
||||
```vue
|
||||
|
||||
<template>
|
||||
<s-table
|
||||
ref="table"
|
||||
size="default"
|
||||
:rowKey="(record) => record.data.id"
|
||||
:columns="columns"
|
||||
:data="loadData"
|
||||
:rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
|
||||
>
|
||||
</s-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import STable from '@/components/table/'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
STable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
title: '规则编号',
|
||||
dataIndex: 'no'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description'
|
||||
},
|
||||
{
|
||||
title: '服务调用次数',
|
||||
dataIndex: 'callNo',
|
||||
sorter: true,
|
||||
needTotal: true,
|
||||
customRender: (text) => text + ' 次'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
needTotal: true
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
sorter: true
|
||||
}
|
||||
],
|
||||
// 查询条件参数
|
||||
queryParam: {},
|
||||
// 加载数据方法 必须为 Promise 对象
|
||||
loadData: parameter => {
|
||||
return this.$http.get('/service', {
|
||||
params: Object.assign(parameter, this.queryParam)
|
||||
}).then(res => {
|
||||
return res.result
|
||||
})
|
||||
},
|
||||
selectedRowKeys: [],
|
||||
selectedRows: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.selectedRows = selectedRows
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
例子2
|
||||
----
|
||||
|
||||
(简单的表格,最后一列是各种操作)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<s-table
|
||||
ref="table"
|
||||
size="default"
|
||||
:columns="columns"
|
||||
:data="loadData"
|
||||
>
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<a>编辑</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link">
|
||||
更多 <a-icon type="down"/>
|
||||
</a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">1st menu item</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">2nd menu item</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">3rd menu item</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</span>
|
||||
</s-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import STable from '@/components/table/'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
STable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
title: '规则编号',
|
||||
dataIndex: 'no'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description'
|
||||
},
|
||||
{
|
||||
title: '服务调用次数',
|
||||
dataIndex: 'callNo',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
},
|
||||
{
|
||||
table: '操作',
|
||||
dataIndex: 'action',
|
||||
scopedSlots: {customRender: 'action'},
|
||||
}
|
||||
],
|
||||
// 查询条件参数
|
||||
queryParam: {},
|
||||
// 加载数据方法 必须为 Promise 对象
|
||||
loadData: parameter => {
|
||||
return this.$http.get('/service', {
|
||||
params: Object.assign(parameter, this.queryParam)
|
||||
}).then(res => {
|
||||
return res.result
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
edit(row) {
|
||||
// axios 发送数据到后端 修改数据成功后
|
||||
// 调用 refresh() 重新加载列表数据
|
||||
// 这里 setTimeout 模拟发起请求的网络延迟..
|
||||
setTimeout(() => {
|
||||
this.$refs.table.refresh() // refresh() 不传参默认值 false 不刷新到分页第一页
|
||||
}, 1500)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
内置方法
|
||||
----
|
||||
|
||||
通过 `this.$refs.table` 调用
|
||||
|
||||
`this.$refs.table.refresh(true)` 刷新列表 (用户新增/修改数据后,重载列表数据)
|
||||
|
||||
> 注意:要调用 `refresh(bool)` 需要给表格组件设定 `ref` 值
|
||||
>
|
||||
> `refresh()` 方法可以传一个 `bool` 值,当有传值 或值为 `true` 时,则刷新时会强制刷新到第一页(常用户页面 搜索 按钮进行搜索时,结果从第一页开始分页)
|
||||
|
||||
|
||||
内置属性
|
||||
----
|
||||
> 除去 `a-table` 自带属性外,还而外提供了一些额外属性属性
|
||||
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| -------------- | ----------------------------------------------- | ----------------- | ------ |
|
||||
| alert | 设置是否显示表格信息栏 | [object, boolean] | null |
|
||||
| showPagination | 显示分页选择器,可传 'auto' \| boolean | [string, boolean] | 'auto' |
|
||||
| data | 加载数据方法 必须为 `Promise` 对象 **必须绑定** | Promise | - |
|
||||
|
||||
|
||||
`alert` 属性对象:
|
||||
|
||||
```javascript
|
||||
alert: {
|
||||
show: Boolean,
|
||||
clear: [Function, Boolean]
|
||||
}
|
||||
```
|
||||
|
||||
注意事项
|
||||
----
|
||||
|
||||
> 你可能需要为了与后端提供的接口返回结果一致而去修改以下代码:
|
||||
(需要注意的是,这里的修改是全局性的,意味着整个项目所有使用该 table 组件都需要遵守这个返回结果定义的字段。)
|
||||
|
||||
修改 `@/components/table/index.js` 第 132 行起
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
result.then(r => {
|
||||
this.localPagination = Object.assign({}, this.localPagination, {
|
||||
current: r.pageNo, // 返回结果中的当前分页数
|
||||
total: r.totalCount, // 返回结果中的总记录数
|
||||
showSizeChanger: this.showSizeChanger,
|
||||
pageSize: (pagination && pagination.pageSize) ||
|
||||
this.localPagination.pageSize
|
||||
})
|
||||
|
||||
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
|
||||
if (r.data.length == 0 && this.localPagination.current != 1) {
|
||||
this.localPagination.current--
|
||||
this.loadData()
|
||||
return
|
||||
}
|
||||
|
||||
// 这里用于判断接口是否有返回 r.totalCount 或 this.showPagination = false
|
||||
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
|
||||
!r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
|
||||
this.localDataSource = r.data // 返回结果中的数组数据
|
||||
this.localLoading = false
|
||||
});
|
||||
```
|
||||
返回 JSON 例子:
|
||||
```json
|
||||
{
|
||||
"message": "",
|
||||
"result": {
|
||||
"data": [{
|
||||
id: 1,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
|
||||
title: 'Alipay',
|
||||
description: '那是一种内在的东西, 他们到达不了,也无法触及的',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
|
||||
title: 'Angular',
|
||||
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
|
||||
title: 'Ant Design',
|
||||
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
|
||||
title: 'Ant Design Pro',
|
||||
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
|
||||
title: 'Bootstrap',
|
||||
description: '凛冬将至',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png',
|
||||
title: 'Vue',
|
||||
description: '生命就像一盒巧克力,结果往往出人意料',
|
||||
status: 1,
|
||||
updatedAt: '2018-07-26 00:00:00'
|
||||
}
|
||||
],
|
||||
"pageSize": 10,
|
||||
"pageNo": 0,
|
||||
"totalPage": 6,
|
||||
"totalCount": 57
|
||||
},
|
||||
"status": 200,
|
||||
"timestamp": 1534955098193
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
更新时间
|
||||
----
|
||||
|
||||
该文档最后更新于: 2019-01-21 AM 08:37
|
|
@ -0,0 +1,290 @@
|
|||
import T from 'ant-design-vue/es/table/Table'
|
||||
import get from 'lodash.get'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
needTotalList: [],
|
||||
|
||||
selectedRows: [],
|
||||
selectedRowKeys: [],
|
||||
|
||||
localLoading: false,
|
||||
localDataSource: [],
|
||||
localPagination: Object.assign({}, this.pagination)
|
||||
}
|
||||
},
|
||||
props: Object.assign({}, T.props, {
|
||||
rowKey: {
|
||||
type: [String, Function],
|
||||
default: 'id'
|
||||
},
|
||||
data: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
pageNum: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
pageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
showSizeChanger: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
/**
|
||||
* {
|
||||
* show: true,
|
||||
* clear: Function
|
||||
* }
|
||||
*/
|
||||
alert: {
|
||||
type: [Object, Boolean],
|
||||
default: null
|
||||
},
|
||||
rowSelection: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
/** @Deprecated */
|
||||
showAlertInfo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showPagination: {
|
||||
type: String | Boolean,
|
||||
default: 'auto'
|
||||
}
|
||||
}),
|
||||
watch: {
|
||||
'localPagination.current'(val) {
|
||||
this.$router.push({
|
||||
name: this.$route.name,
|
||||
params: Object.assign({}, this.$route.params, {
|
||||
pageNo: val
|
||||
})
|
||||
})
|
||||
},
|
||||
pageNum(val) {
|
||||
Object.assign(this.localPagination, {
|
||||
current: val
|
||||
})
|
||||
},
|
||||
pageSize(val) {
|
||||
Object.assign(this.localPagination, {
|
||||
pageSize: val
|
||||
})
|
||||
},
|
||||
showSizeChanger(val) {
|
||||
Object.assign(this.localPagination, {
|
||||
showSizeChanger: val
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {
|
||||
current: this.pageNum,
|
||||
pageSize: this.pageSize,
|
||||
showSizeChanger: this.showSizeChanger
|
||||
})
|
||||
this.needTotalList = this.initTotalList(this.columns)
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 表格重新加载方法
|
||||
* 如果参数为 true, 则强制刷新到第一页
|
||||
* @param Boolean bool
|
||||
*/
|
||||
refresh(bool = false) {
|
||||
bool && (this.localPagination = Object.assign({}, {
|
||||
current: 1, pageSize: this.pageSize
|
||||
}))
|
||||
this.loadData()
|
||||
},
|
||||
/**
|
||||
* 加载数据方法
|
||||
* @param {Object} pagination 分页选项器
|
||||
* @param {Object} filters 过滤条件
|
||||
* @param {Object} sorter 排序条件
|
||||
*/
|
||||
loadData(pagination, filters, sorter) {
|
||||
this.localLoading = true
|
||||
const parameter = Object.assign({
|
||||
pageNo: (pagination && pagination.current) ||
|
||||
this.localPagination.current || this.pageNum,
|
||||
pageSize: (pagination && pagination.pageSize) ||
|
||||
this.localPagination.pageSize || this.pageSize
|
||||
},
|
||||
(sorter && sorter.field && {
|
||||
sortField: sorter.field
|
||||
}) || {},
|
||||
(sorter && sorter.order && {
|
||||
sortOrder: sorter.order
|
||||
}) || {}, {
|
||||
...filters
|
||||
}
|
||||
)
|
||||
const result = this.data(parameter)
|
||||
// 对接自己的通用数据接口需要修改下方代码中的 r.pageNo, r.totalCount, r.data
|
||||
// eslint-disable-next-line
|
||||
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
|
||||
result.then(r => {
|
||||
this.localPagination = Object.assign({}, this.localPagination, {
|
||||
current: r.pageNo, // 返回结果中的当前分页数
|
||||
total: r.totalCount, // 返回结果中的总记录数
|
||||
showSizeChanger: this.showSizeChanger,
|
||||
pageSize: (pagination && pagination.pageSize) ||
|
||||
this.localPagination.pageSize
|
||||
})
|
||||
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
|
||||
if (r.data.length === 0 && this.localPagination.current !== 1) {
|
||||
this.localPagination.current--
|
||||
this.loadData()
|
||||
return
|
||||
}
|
||||
|
||||
// 这里用于判断接口是否有返回 r.totalCount 或 this.showPagination = false
|
||||
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
|
||||
|
||||
(!this.showPagination || !r.totalCount && this.showPagination === 'auto') && (this.localPagination.hideOnSinglePage = true)
|
||||
this.localDataSource = r.data // 返回结果中的数组数据
|
||||
this.localLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
initTotalList(columns) {
|
||||
const totalList = []
|
||||
columns && columns instanceof Array && columns.forEach(column => {
|
||||
if (column.needTotal) {
|
||||
totalList.push({
|
||||
...column,
|
||||
total: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
return totalList
|
||||
},
|
||||
/**
|
||||
* 用于更新已选中的列表数据 total 统计
|
||||
* @param selectedRowKeys
|
||||
* @param selectedRows
|
||||
*/
|
||||
updateSelect(selectedRowKeys, selectedRows) {
|
||||
this.selectedRows = selectedRows
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
const list = this.needTotalList
|
||||
this.needTotalList = list.map(item => {
|
||||
return {
|
||||
...item,
|
||||
total: selectedRows.reduce((sum, val) => {
|
||||
const total = sum + parseInt(get(val, item.dataIndex))
|
||||
return isNaN(total) ? 0 : total
|
||||
}, 0)
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 清空 table 已选中项
|
||||
*/
|
||||
clearSelected() {
|
||||
if (this.rowSelection) {
|
||||
this.rowSelection.onChange([], [])
|
||||
this.updateSelect([], [])
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用
|
||||
* @param callback
|
||||
* @returns {*}
|
||||
*/
|
||||
renderClear(callback) {
|
||||
if (this.selectedRowKeys.length <= 0) return null
|
||||
return (
|
||||
<a style="margin-left: 24px" onClick={() => {
|
||||
callback()
|
||||
this.clearSelected()
|
||||
}}>清空</a>
|
||||
)
|
||||
},
|
||||
renderAlert() {
|
||||
// 绘制统计列数据
|
||||
const needTotalItems = this.needTotalList.map((item) => {
|
||||
return (<span style="margin-right: 12px">
|
||||
{item.title}总计 <a style="font-weight: 600">{!item.customRender ? item.total : item.customRender(item.total)}</a>
|
||||
</span>)
|
||||
})
|
||||
|
||||
// 绘制 清空 按钮
|
||||
const clearItem = (typeof this.alert.clear === 'boolean' && this.alert.clear) ? (
|
||||
this.renderClear(this.clearSelected)
|
||||
) : (this.alert !== null && typeof this.alert.clear === 'function') ? (
|
||||
this.renderClear(this.alert.clear)
|
||||
) : null
|
||||
|
||||
// 绘制 alert 组件
|
||||
return (
|
||||
<a-alert showIcon={true} style="margin-bottom: 16px">
|
||||
<template slot="message">
|
||||
<span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span>
|
||||
{needTotalItems}
|
||||
{clearItem}
|
||||
</template>
|
||||
</a-alert>
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const props = {}
|
||||
const localKeys = Object.keys(this.$data)
|
||||
const showAlert = (typeof this.alert === 'object' && this.alert !== null && this.alert.show) && typeof this.rowSelection.selectedRowKeys !== 'undefined' || this.alert
|
||||
|
||||
Object.keys(T.props).forEach(k => {
|
||||
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
|
||||
if (localKeys.includes(localKey)) {
|
||||
props[k] = this[localKey]
|
||||
return props[k]
|
||||
}
|
||||
if (k === 'rowSelection') {
|
||||
if (showAlert && this.rowSelection) {
|
||||
// 如果需要使用alert,则重新绑定 rowSelection 事件
|
||||
props[k] = {
|
||||
selectedRows: this.selectedRows,
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
this.updateSelect(selectedRowKeys, selectedRows)
|
||||
typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows)
|
||||
}
|
||||
}
|
||||
return props[k]
|
||||
} else if (!this.rowSelection) {
|
||||
// 如果没打算开启 rowSelection 则清空默认的选择项
|
||||
props[k] = null
|
||||
return props[k]
|
||||
}
|
||||
}
|
||||
props[k] = this[k]
|
||||
return props[k]
|
||||
})
|
||||
const table = (
|
||||
<a-table {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.loadData}>
|
||||
{ Object.keys(this.$slots).map(name => (<template slot={name}>{this.$slots[name]}</template>)) }
|
||||
</a-table>
|
||||
)
|
||||
|
||||
return (
|
||||
<div class="table-wrapper">
|
||||
{ showAlert ? this.renderAlert() : null }
|
||||
{ table }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { Tag } from 'ant-design-vue'
|
||||
const { CheckableTag } = Tag
|
||||
|
||||
export default {
|
||||
name: 'TagSelectOption',
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-tag-select-option'
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, Object],
|
||||
default: ''
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localChecked: this.checked || false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'checked'(val) {
|
||||
this.localChecked = val
|
||||
},
|
||||
'$parent.items': {
|
||||
handler: function(val) {
|
||||
this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value])
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { $slots, value } = this
|
||||
const onChange = (checked) => {
|
||||
this.$emit('change', { value, checked })
|
||||
}
|
||||
return (<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}>
|
||||
{$slots.default}
|
||||
</CheckableTag>)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
import PropTypes from 'ant-design-vue/es/_util/vue-types'
|
||||
import Option from './TagSelectOption.jsx'
|
||||
import { filterEmpty } from '@/components/_util/util'
|
||||
|
||||
export default {
|
||||
Option,
|
||||
name: 'TagSelect',
|
||||
model: {
|
||||
prop: 'checked',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-tag-select'
|
||||
},
|
||||
defaultValue: {
|
||||
type: PropTypes.array,
|
||||
default: null
|
||||
},
|
||||
value: {
|
||||
type: PropTypes.array,
|
||||
default: null
|
||||
},
|
||||
expandable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideCheckAll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expand: false,
|
||||
localCheckAll: false,
|
||||
items: this.getItemsKey(filterEmpty(this.$slots.default)),
|
||||
val: this.value || this.defaultValue || []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(checked) {
|
||||
const key = Object.keys(this.items).filter(key => key === checked.value)
|
||||
this.items[key] = checked.checked
|
||||
const bool = Object.values(this.items).lastIndexOf(false)
|
||||
if (bool === -1) {
|
||||
this.localCheckAll = true
|
||||
} else {
|
||||
this.localCheckAll = false
|
||||
}
|
||||
},
|
||||
onCheckAll(checked) {
|
||||
Object.keys(this.items).forEach(v => {
|
||||
this.items[v] = checked.checked
|
||||
})
|
||||
},
|
||||
getItemsKey(items) {
|
||||
const totalItem = {}
|
||||
items.forEach(item => {
|
||||
totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false
|
||||
})
|
||||
return totalItem
|
||||
},
|
||||
// CheckAll Button
|
||||
renderCheckAll() {
|
||||
return !this.hideCheckAll && (<Option key={'total'} checked={this.localCheckAll} onChange={this.onCheckAll}>All</Option>) || null
|
||||
},
|
||||
// expandable
|
||||
renderExpandable() {
|
||||
|
||||
},
|
||||
// render option
|
||||
renderTags(items) {
|
||||
const listeners = {
|
||||
change: (checked) => {
|
||||
this.onChange(checked)
|
||||
this.$emit('change', checked)
|
||||
}
|
||||
}
|
||||
|
||||
return items.map(vnode => {
|
||||
const options = vnode.componentOptions
|
||||
options.listeners = listeners
|
||||
return vnode
|
||||
})
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { $props: { prefixCls } } = this
|
||||
const classString = {
|
||||
[`${prefixCls}`]: true
|
||||
}
|
||||
const tagItems = filterEmpty(this.$slots.default)
|
||||
return (
|
||||
<div class={classString}>
|
||||
{this.renderCheckAll()}
|
||||
{this.renderTags(tagItems)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<a-breadcrumb class="breadcrumb">
|
||||
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
|
||||
<router-link v-if="item.name != name" :to="{ path: item.path }">
|
||||
{{ item.meta.title }}
|
||||
</router-link>
|
||||
<a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
>{{ item.meta.title }}</router-link>
|
||||
<span v-else>{{ item.meta.title }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
|
@ -22,14 +23,12 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
console.log('this.$route.matched', this.$route.matched)
|
||||
|
||||
this.breadList = []
|
||||
this.breadList.push({ name: 'index', path: '/dashboard/', meta: { title: '首页' } })
|
||||
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
|
||||
|
||||
this.name = this.$route.name
|
||||
this.$route.matched.forEach((item) => {
|
||||
// item.meta.name === 'dashboard' ? item.path = '/dashboard' : this.$route.path === item.path
|
||||
this.$route.matched.forEach(item => {
|
||||
// item.name !== 'index' && this.breadList.push(item)
|
||||
this.breadList.push(item)
|
||||
})
|
||||
}
|
||||
|
@ -43,5 +42,4 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
/* WARNING: 兼容老引入,请勿继续使用 */
|
||||
import DescriptionList from '@/components/DescriptionList'
|
||||
export default DescriptionList
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="logo">
|
||||
<router-link :to="{name:'Dashboard'}">
|
||||
<router-link :to="{name:'dashboard'}">
|
||||
<LogoSvg alt="logo" />
|
||||
<h1 v-if="showTitle">{{ title }}</h1>
|
||||
</router-link>
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<script>
|
||||
import HeaderComment from './HeaderComment'
|
||||
import SettingDrawer from '@/components/setting/SettingDrawer'
|
||||
import SettingDrawer from '@/components/SettingDrawer/SettingDrawer'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
|
@ -0,0 +1,124 @@
|
|||
import { Menu, Icon, Input } from 'ant-design-vue'
|
||||
|
||||
const { Item, ItemGroup, SubMenu } = Menu
|
||||
const { Search } = Input
|
||||
|
||||
export default {
|
||||
name: 'Tree',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
openKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
search: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.localOpenKeys = this.openKeys.slice(0)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localOpenKeys: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePlus(item) {
|
||||
this.$emit('add', item)
|
||||
},
|
||||
handleTitleClick(...args) {
|
||||
this.$emit('titleClick', { args })
|
||||
},
|
||||
|
||||
renderSearch() {
|
||||
return (
|
||||
<Search
|
||||
placeholder="input search text"
|
||||
style="width: 100%; margin-bottom: 1rem"
|
||||
/>
|
||||
)
|
||||
},
|
||||
renderIcon(icon) {
|
||||
return icon && (<Icon type={icon} />) || null
|
||||
},
|
||||
renderMenuItem(item) {
|
||||
return (
|
||||
<Item key={item.key}>
|
||||
{ this.renderIcon(item.icon) }
|
||||
{ item.title }
|
||||
<a class="btn" style="width: 20px;z-index:1300" {...{ on: { click: () => this.handlePlus(item) } }}><a-icon type="plus"/></a>
|
||||
</Item>
|
||||
)
|
||||
},
|
||||
renderItem(item) {
|
||||
return item.children ? this.renderSubItem(item, item.key) : this.renderMenuItem(item, item.key)
|
||||
},
|
||||
renderItemGroup(item) {
|
||||
const childrenItems = item.children.map(o => {
|
||||
return this.renderItem(o, o.key)
|
||||
})
|
||||
|
||||
return (
|
||||
<ItemGroup key={item.key}>
|
||||
<template slot="title">
|
||||
<span>{ item.title }</span>
|
||||
<a-dropdown>
|
||||
<a class="btn"><a-icon type="ellipsis" /></a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="1">新增</a-menu-item>
|
||||
<a-menu-item key="2">合并</a-menu-item>
|
||||
<a-menu-item key="3">移除</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
{ childrenItems }
|
||||
</ItemGroup>
|
||||
)
|
||||
},
|
||||
renderSubItem(item, key) {
|
||||
const childrenItems = item.children && item.children.map(o => {
|
||||
return this.renderItem(o, o.key)
|
||||
})
|
||||
|
||||
const title = (
|
||||
<span slot="title">
|
||||
{ this.renderIcon(item.icon) }
|
||||
<span>{ item.title }</span>
|
||||
</span>
|
||||
)
|
||||
|
||||
if (item.group) {
|
||||
return this.renderItemGroup(item)
|
||||
}
|
||||
// titleClick={this.handleTitleClick(item)}
|
||||
return (
|
||||
<SubMenu key={key}>
|
||||
{ title }
|
||||
{ childrenItems }
|
||||
</SubMenu>
|
||||
)
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { dataSource, search } = this.$props
|
||||
|
||||
// this.localOpenKeys = openKeys.slice(0)
|
||||
const list = dataSource.map(item => {
|
||||
return this.renderItem(item)
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="tree-wrapper">
|
||||
{ search ? this.renderSearch() : null }
|
||||
<Menu mode="inline" class="custom-tree" {...{ on: { click: item => this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}>
|
||||
{ list }
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div :class="[prefixCls, reverseColor && 'reverse-color' ]">
|
||||
<span>
|
||||
<slot name="term"></slot>
|
||||
<span class="item-text">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</span>
|
||||
<span :class="[flag]"><a-icon :type="`caret-${flag}`"/></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Trend',
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-trend'
|
||||
},
|
||||
/**
|
||||
* 上升下降标识:up|down
|
||||
*/
|
||||
flag: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
/**
|
||||
* 颜色反转
|
||||
*/
|
||||
reverseColor: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "index";
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
import Trend from './Trend.vue'
|
||||
|
||||
export default Trend
|
|
@ -0,0 +1,42 @@
|
|||
@import "../index";
|
||||
|
||||
@trend-prefix-cls: ~"@{ant-pro-prefix}-trend";
|
||||
|
||||
.@{trend-prefix-cls} {
|
||||
display: inline-block;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
|
||||
.up,
|
||||
.down {
|
||||
margin-left: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
transform: scale(0.83);
|
||||
}
|
||||
}
|
||||
|
||||
.item-text {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
color: rgba(0,0,0,.85);
|
||||
}
|
||||
|
||||
.up {
|
||||
color: @red-6;
|
||||
}
|
||||
.down {
|
||||
color: @green-6;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
&.reverse-color .up {
|
||||
color: @green-6;
|
||||
}
|
||||
&.reverse-color .down {
|
||||
color: @red-6;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# Trend 趋势标记
|
||||
|
||||
趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import Trend from '@/components/Trend'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Trend
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<trend flag="up">5%</trend>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<trend flag="up">
|
||||
<span slot="term">工资</span>
|
||||
5%
|
||||
</trend>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<trend flag="up" term="工资">5%</trend>
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| flag | 上升下降标识:`up|down` | string | - |
|
||||
| reverseColor | 颜色反转 | Boolean | false |
|
||||
|
|
@ -3,9 +3,25 @@ import AvatarList from '@/components/AvatarList'
|
|||
import Ellipsis from '@/components/Ellipsis'
|
||||
import FooterToolbar from '@/components/FooterToolbar'
|
||||
import NumberInfo from '@/components/NumberInfo'
|
||||
import DetailList from '@/components/tools/DetailList'
|
||||
import DescriptionList from '@/components/DescriptionList'
|
||||
import Tree from '@/components/Tree/Tree'
|
||||
import Trend from '@/components/Trend'
|
||||
import STable from '@/components/Table'
|
||||
import MultiTab from '@/components/MultiTab'
|
||||
import Result from '@/components/Result'
|
||||
import IconSelector from '@/components/IconSelector'
|
||||
import ExceptionPage from '@/components/Exception'
|
||||
|
||||
export { AvatarList, Ellipsis, FooterToolbar, NumberInfo, DetailList, MultiTab, Result, IconSelector }
|
||||
export {
|
||||
AvatarList,
|
||||
Trend,
|
||||
Ellipsis,
|
||||
FooterToolbar,
|
||||
NumberInfo,
|
||||
DescriptionList,
|
||||
DescriptionList as DetailList,
|
||||
Tree,
|
||||
STable,
|
||||
MultiTab,
|
||||
Result,
|
||||
ExceptionPage
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<template>
|
||||
<global-layout>
|
||||
<multi-tab v-if="$store.getters.multiTab"></multi-tab>
|
||||
<transition name="page-transition">
|
||||
<route-view />
|
||||
</transition>
|
||||
</global-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RouteView from '@/components/layouts/RouteView'
|
||||
import MultiTab from '@/components/MultiTab'
|
||||
import GlobalLayout from '@/components/page/GlobalLayout'
|
||||
|
||||
export default {
|
||||
name: 'BasicLayout',
|
||||
components: {
|
||||
RouteView,
|
||||
MultiTab,
|
||||
GlobalLayout
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import url('../global.less');
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="page-transition" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the page transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.page-transition-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-enter .page-transition-container,
|
||||
.page-transition-leave-active .page-transition-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
|
@ -1,85 +0,0 @@
|
|||
<template>
|
||||
<page-layout :desc="description" :title="getTitle" :link-list="linkList" :search="search" :tabs="tabs">
|
||||
<div slot="extra" class="extra-img">
|
||||
<img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
|
||||
</div>
|
||||
<!-- keep-alive -->
|
||||
<route-view ref="content"></route-view>
|
||||
</page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageLayout from '../page/PageLayout'
|
||||
import RouteView from './RouteView'
|
||||
|
||||
export default {
|
||||
name: 'PageContent',
|
||||
components: {
|
||||
RouteView,
|
||||
PageLayout
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
description: '',
|
||||
linkList: [],
|
||||
extraImage: '',
|
||||
search: false,
|
||||
tabs: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPageHeaderInfo()
|
||||
},
|
||||
updated() {
|
||||
this.getPageHeaderInfo()
|
||||
},
|
||||
computed: {
|
||||
|
||||
getTitle() {
|
||||
return this.$route.meta.title
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
getPageHeaderInfo() {
|
||||
// eslint-disable-next-line
|
||||
this.title = this.$route.meta.title
|
||||
// 因为套用了一层 route-view 所以要取 ref 对象下的子节点的第一个对象
|
||||
const content = this.$refs.content && this.$refs.content.$children[0]
|
||||
|
||||
if (content) {
|
||||
this.description = content.description
|
||||
this.linkList = content.linkList
|
||||
this.extraImage = content.extraImage
|
||||
this.search = content.search === true
|
||||
this.tabs = content.tabs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.extra-img {
|
||||
margin-top: -60px;
|
||||
text-align: center;
|
||||
width: 195px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.extra-img{
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
width: 96px;
|
||||
|
||||
img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +0,0 @@
|
|||
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 { BasicLayout, BlankLayout, RouteView, PageView }
|
|
@ -1,108 +0,0 @@
|
|||
<template>
|
||||
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
|
||||
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :style="{ padding: '0' }">
|
||||
<div v-if="mode === 'sidemenu'" class="header">
|
||||
<a-icon
|
||||
v-if="device==='mobile'"
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||
@click="toggle"></a-icon>
|
||||
<a-icon
|
||||
v-else
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
|
||||
@click="toggle"/>
|
||||
|
||||
<user-menu></user-menu>
|
||||
</div>
|
||||
<div v-else :class="['top-nav-header-index', theme]">
|
||||
<div class="header-index-wide">
|
||||
<div class="header-index-left">
|
||||
<logo class="top-nav-header" :show-title="device !== 'mobile'" />
|
||||
<s-menu
|
||||
v-if="device !== 'mobile'"
|
||||
mode="horizontal"
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
></s-menu>
|
||||
<a-icon
|
||||
v-else
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||
@click="toggle"></a-icon>
|
||||
</div>
|
||||
<user-menu class="header-index-right"></user-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-layout-header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UserMenu from '../tools/UserMenu'
|
||||
import SMenu from '../menu/'
|
||||
import Logo from '../tools/Logo'
|
||||
|
||||
import { mixin } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'GlobalHeader',
|
||||
components: {
|
||||
UserMenu,
|
||||
SMenu,
|
||||
Logo
|
||||
},
|
||||
mixins: [mixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
// sidemenu, topmenu
|
||||
default: 'sidemenu'
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
device: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'desktop'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
headerBarFixed: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
if (this.autoHideHeader) {
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
|
||||
if (scrollTop > 100) {
|
||||
this.headerBarFixed = true
|
||||
} else {
|
||||
this.headerBarFixed = false
|
||||
}
|
||||
} else {
|
||||
this.headerBarFixed = false
|
||||
}
|
||||
},
|
||||
toggle() {
|
||||
this.$emit('toggle')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,156 +0,0 @@
|
|||
<template>
|
||||
<a-layout class="layout" :class="[device]">
|
||||
|
||||
<template v-if="isSideMenu()">
|
||||
<a-drawer
|
||||
v-if="isMobile()"
|
||||
:wrapClassName="'drawer-sider ' + navTheme"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
placement="left"
|
||||
@close="() => this.collapsed = false"
|
||||
>
|
||||
<side-menu
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="false"
|
||||
:collapsible="true"
|
||||
mode="inline"
|
||||
@menuSelect="menuSelect"></side-menu>
|
||||
</a-drawer>
|
||||
|
||||
<side-menu
|
||||
v-else
|
||||
mode="inline"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:collapsible="true"></side-menu>
|
||||
</template>
|
||||
<!-- 下次优化这些代码 -->
|
||||
<template v-else>
|
||||
<a-drawer
|
||||
v-if="isMobile()"
|
||||
:wrapClassName="'drawer-sider ' + navTheme"
|
||||
placement="left"
|
||||
@close="() => this.collapsed = false"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
>
|
||||
<side-menu
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="false"
|
||||
:collapsible="true"
|
||||
mode="inline"
|
||||
@menuSelect="menuSelect"></side-menu>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
|
||||
<!-- layout header -->
|
||||
<global-header
|
||||
:mode="layoutMode"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:device="device"
|
||||
@toggle="toggle"
|
||||
/>
|
||||
|
||||
<!-- layout content -->
|
||||
<a-layout-content :style="{ margin: $store.getters.multiTab ? '24px 24px 0' : '24px 24px 0', height: '100%', paddingTop: fixedHeader ? '64px' : '0' }">
|
||||
<slot></slot>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- layout footer -->
|
||||
<a-layout-footer style="padding: 0">
|
||||
<global-footer />
|
||||
</a-layout-footer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SideMenu from '@/components/menu/SideMenu'
|
||||
import GlobalHeader from '@/components/page/GlobalHeader'
|
||||
import GlobalFooter from '@/components/page/GlobalFooter'
|
||||
import { asyncRouterMap } from '@/config/router.config.js'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'GlobalLayout',
|
||||
components: {
|
||||
SideMenu,
|
||||
GlobalHeader,
|
||||
GlobalFooter
|
||||
},
|
||||
mixins: [mixin, mixinDevice],
|
||||
data() {
|
||||
return {
|
||||
collapsed: false,
|
||||
menus: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
// 主路由
|
||||
mainMenu: state => state.permission.addRouters
|
||||
}),
|
||||
contentPaddingLeft() {
|
||||
if (!this.fixSidebar || this.isMobile()) {
|
||||
return '0'
|
||||
}
|
||||
if (this.sidebarOpened) {
|
||||
return '256px'
|
||||
}
|
||||
return '80px'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sidebarOpened(val) {
|
||||
this.collapsed = !val
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.menus = asyncRouterMap.find((item) => item.path === '/').children
|
||||
// this.menus = this.mainMenu.find((item) => item.path === '/').children
|
||||
this.collapsed = !this.sidebarOpened
|
||||
},
|
||||
mounted() {
|
||||
const userAgent = navigator.userAgent
|
||||
if (userAgent.indexOf('Edge') > -1) {
|
||||
this.$nextTick(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
setTimeout(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
}, 16)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSidebar']),
|
||||
toggle() {
|
||||
this.collapsed = !this.collapsed
|
||||
this.setSidebar(!this.collapsed)
|
||||
triggerWindowResizeEvent()
|
||||
},
|
||||
paddingCalc() {
|
||||
let left = ''
|
||||
if (this.sidebarOpened) {
|
||||
left = this.isDesktop() ? '256px' : '80px'
|
||||
} else {
|
||||
left = this.isMobile() && '0' || (this.fixSidebar && '80px' || '0')
|
||||
}
|
||||
return left
|
||||
},
|
||||
menuSelect() {
|
||||
if (!this.isDesktop()) {
|
||||
this.collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,124 +0,0 @@
|
|||
<template>
|
||||
<div :style="!$route.meta.pageHeader ? 'margin: -24px -24px 0px;' : null">
|
||||
<!-- pageHeader , route meta hideHeader:true on hide -->
|
||||
<page-header v-if="!$route.meta.pageHeader" :title="title" :logo="logo" :avatar="avatar">
|
||||
<slot slot="action" name="action"></slot>
|
||||
<slot slot="content" name="headerContent"></slot>
|
||||
<div slot="content" v-if="!this.$slots.headerContent && desc">
|
||||
<p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ desc }}</p>
|
||||
<div class="link">
|
||||
<template v-for="(link, index) in linkList">
|
||||
<a :key="index" :href="link.href">
|
||||
<a-icon :type="link.icon"/>
|
||||
<span>{{ link.title }}</span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<slot slot="extra" name="extra"></slot>
|
||||
<div slot="pageMenu">
|
||||
<div class="page-menu-search" v-if="search">
|
||||
<a-input-search style="width: 80%; max-width: 522px;" placeholder="请输入..." size="large" enterButton="搜索" />
|
||||
</div>
|
||||
<div class="page-menu-tabs" v-if="tabs && tabs.items">
|
||||
<!-- @change="callback" :activeKey="activeKey" -->
|
||||
<a-tabs :tabBarStyle="{margin: 0}" @change="tabs.callback" :activeKey="tabs.active()">
|
||||
<a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</page-header>
|
||||
<div class="content">
|
||||
<div :class="['page-header-index-wide']">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from './PageHeader'
|
||||
|
||||
export default {
|
||||
name: 'LayoutContent',
|
||||
components: {
|
||||
PageHeader
|
||||
},
|
||||
// ['desc', 'logo', 'title', 'avatar', 'linkList', 'extraImage']
|
||||
props: {
|
||||
desc: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
linkList: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
extraImage: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
search: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tabs: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.content {
|
||||
margin: 24px 24px 0;
|
||||
|
||||
.link {
|
||||
margin-top: 16px;
|
||||
|
||||
&:not(:empty) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
a {
|
||||
margin-right: 32px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
span {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.page-menu-search {
|
||||
text-align: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.page-menu-tabs {
|
||||
margin-top: 48px;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
// eslint-disable-next-line
|
||||
import { BasicLayout, RouteView, BlankLayout, PageView } from '@/components/layouts'
|
||||
import { BasicLayout, RouteView, BlankLayout, PageView } from '@/layouts'
|
||||
|
||||
export const asyncRouterMap = [
|
||||
|
||||
|
@ -16,7 +16,7 @@ export const asyncRouterMap = [
|
|||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: () => import('@/views/dashboard/Dashboard'),
|
||||
meta: { title: '仪表盘', icon: 'dashboard', hiddenHeaderContent: true }
|
||||
meta: { title: '仪表盘', icon: 'dashboard', hiddenHeaderContent: false }
|
||||
},
|
||||
|
||||
// posts
|
||||
|
@ -31,25 +31,25 @@ export const asyncRouterMap = [
|
|||
path: '/posts/list',
|
||||
name: 'PostList',
|
||||
component: () => import('@/views/post/PostList'),
|
||||
meta: { title: '所有文章', hiddenHeaderContent: true }
|
||||
meta: { title: '所有文章', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/posts/write',
|
||||
name: 'PostEdit',
|
||||
component: () => import('@/views/post/PostEdit'),
|
||||
meta: { title: '写文章', hiddenHeaderContent: true }
|
||||
meta: { title: '写文章', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/categories',
|
||||
name: 'CategoryList',
|
||||
component: () => import('@/views/post/CategoryList'),
|
||||
meta: { title: '分类目录', hiddenHeaderContent: true }
|
||||
meta: { title: '分类目录', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/tags',
|
||||
name: 'TagList',
|
||||
component: () => import('@/views/post/TagList'),
|
||||
meta: { title: '标签', hiddenHeaderContent: true }
|
||||
meta: { title: '标签', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -66,13 +66,13 @@ export const asyncRouterMap = [
|
|||
path: '/pages/list',
|
||||
name: 'PageList',
|
||||
component: () => import('@/views/page/PageList'),
|
||||
meta: { title: '所有页面', hiddenHeaderContent: true }
|
||||
meta: { title: '所有页面', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/pages/write',
|
||||
name: 'PageEdit',
|
||||
component: () => import('@/views/page/PageEdit'),
|
||||
meta: { title: '新建页面', hiddenHeaderContent: true }
|
||||
meta: { title: '新建页面', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -82,7 +82,7 @@ export const asyncRouterMap = [
|
|||
path: '/attachments',
|
||||
name: 'Attachments',
|
||||
component: () => import('@/views/attachment/AttachmentList'),
|
||||
meta: { title: '附件', icon: 'picture', hiddenHeaderContent: true }
|
||||
meta: { title: '附件', icon: 'picture', hiddenHeaderContent: false }
|
||||
},
|
||||
|
||||
// comments
|
||||
|
@ -90,7 +90,7 @@ export const asyncRouterMap = [
|
|||
path: '/comments',
|
||||
name: 'Comments',
|
||||
component: () => import('@/views/comment/CommentList'),
|
||||
meta: { title: '评论', icon: 'message', hiddenHeaderContent: true }
|
||||
meta: { title: '评论', icon: 'message', hiddenHeaderContent: false }
|
||||
},
|
||||
|
||||
// interface
|
||||
|
@ -105,19 +105,19 @@ export const asyncRouterMap = [
|
|||
path: '/interface/themes',
|
||||
name: 'ThemeList',
|
||||
component: () => import('@/views/interface/ThemeList'),
|
||||
meta: { title: '主题', hiddenHeaderContent: true }
|
||||
meta: { title: '主题', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/interface/menus',
|
||||
name: 'MenuList',
|
||||
component: () => import('@/views/interface/MenuList'),
|
||||
meta: { title: '菜单', hiddenHeaderContent: true }
|
||||
meta: { title: '菜单', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/interface/themes/edit',
|
||||
name: 'ThemeEdit',
|
||||
component: () => import('@/views/interface/ThemeEdit'),
|
||||
meta: { title: '主题编辑', hiddenHeaderContent: true }
|
||||
meta: { title: '主题编辑', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -134,7 +134,7 @@ export const asyncRouterMap = [
|
|||
path: '/user/profile',
|
||||
name: 'Profile',
|
||||
component: () => import('@/views/user/Profile'),
|
||||
meta: { title: '个人资料', hiddenHeaderContent: true }
|
||||
meta: { title: '个人资料', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -151,19 +151,19 @@ export const asyncRouterMap = [
|
|||
path: '/system/options',
|
||||
name: 'OptionForm',
|
||||
component: () => import('@/views/system/OptionForm'),
|
||||
meta: { title: '博客设置', hiddenHeaderContent: true }
|
||||
meta: { title: '博客设置', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/system/backup',
|
||||
name: 'BackupList',
|
||||
component: () => import('@/views/system/BackupList'),
|
||||
meta: { title: '博客备份', hiddenHeaderContent: true }
|
||||
meta: { title: '博客备份', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/system/tools',
|
||||
name: 'ToolList',
|
||||
component: () => import('@/views/system/ToolList'),
|
||||
meta: { title: '小工具', hiddenHeaderContent: true }
|
||||
meta: { title: '小工具', hiddenHeaderContent: false }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
<template>
|
||||
<a-layout :class="['layout', device]">
|
||||
<!-- SideMenu -->
|
||||
<a-drawer
|
||||
v-if="isMobile()"
|
||||
placement="left"
|
||||
:wrapClassName="`drawer-sider ${navTheme}`"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
@close="drawerClose"
|
||||
>
|
||||
<side-menu
|
||||
mode="inline"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="false"
|
||||
:collapsible="true"
|
||||
@menuSelect="menuSelect"
|
||||
></side-menu>
|
||||
</a-drawer>
|
||||
|
||||
<side-menu
|
||||
v-else-if="isSideMenu()"
|
||||
mode="inline"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:collapsible="true"
|
||||
></side-menu>
|
||||
|
||||
<a-layout
|
||||
:class="[layoutMode, `content-width-${contentWidth}`]"
|
||||
:style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }"
|
||||
>
|
||||
<!-- layout header -->
|
||||
<global-header
|
||||
:mode="layoutMode"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:device="device"
|
||||
@toggle="toggle"
|
||||
/>
|
||||
|
||||
<!-- layout content -->
|
||||
<a-layout-content
|
||||
:style="{ height: '100%', margin: multiTab ? '24px 24px 0' : '24px 24px 0', paddingTop: fixedHeader ? '64px' : '0' }"
|
||||
>
|
||||
<multi-tab v-if="multiTab"></multi-tab>
|
||||
<transition name="page-transition">
|
||||
<route-view/>
|
||||
</transition>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- layout footer -->
|
||||
<a-layout-footer>
|
||||
<global-footer/>
|
||||
</a-layout-footer>
|
||||
|
||||
<!-- Setting Drawer (show in development mode) -->
|
||||
<setting-drawer v-if="!production"></setting-drawer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
import config from '@/config/defaultSettings'
|
||||
import { asyncRouterMap } from '@/config/router.config.js'
|
||||
|
||||
import RouteView from './RouteView'
|
||||
import MultiTab from '@/components/MultiTab'
|
||||
import SideMenu from '@/components/Menu/SideMenu'
|
||||
import GlobalHeader from '@/components/GlobalHeader'
|
||||
import GlobalFooter from '@/components/GlobalFooter'
|
||||
import SettingDrawer from '@/components/SettingDrawer'
|
||||
|
||||
export default {
|
||||
name: 'BasicLayout',
|
||||
mixins: [mixin, mixinDevice],
|
||||
components: {
|
||||
RouteView,
|
||||
MultiTab,
|
||||
SideMenu,
|
||||
GlobalHeader,
|
||||
GlobalFooter,
|
||||
SettingDrawer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
production: config.production,
|
||||
collapsed: false,
|
||||
menus: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
// 动态主路由
|
||||
mainMenu: state => state.permission.addRouters
|
||||
}),
|
||||
contentPaddingLeft() {
|
||||
if (!this.fixSidebar || this.isMobile()) {
|
||||
return '0'
|
||||
}
|
||||
if (this.sidebarOpened) {
|
||||
return '256px'
|
||||
}
|
||||
return '80px'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sidebarOpened(val) {
|
||||
this.collapsed = !val
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.menus = asyncRouterMap.find(item => item.path === '/').children
|
||||
// this.menus = this.mainMenu.find((item) => item.path === '/').children
|
||||
this.collapsed = !this.sidebarOpened
|
||||
},
|
||||
mounted() {
|
||||
const userAgent = navigator.userAgent
|
||||
if (userAgent.indexOf('Edge') > -1) {
|
||||
this.$nextTick(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
setTimeout(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
}, 16)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSidebar']),
|
||||
toggle() {
|
||||
this.collapsed = !this.collapsed
|
||||
this.setSidebar(!this.collapsed)
|
||||
triggerWindowResizeEvent()
|
||||
},
|
||||
paddingCalc() {
|
||||
let left = ''
|
||||
if (this.sidebarOpened) {
|
||||
left = this.isDesktop() ? '256px' : '80px'
|
||||
} else {
|
||||
left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0')
|
||||
}
|
||||
return left
|
||||
},
|
||||
menuSelect() {
|
||||
if (!this.isDesktop()) {
|
||||
this.collapsed = false
|
||||
}
|
||||
},
|
||||
drawerClose() {
|
||||
this.collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import url('../components/global.less');
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="page-transition" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the page transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.page-transition-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-enter .page-transition-container,
|
||||
.page-transition-leave-active .page-transition-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,176 @@
|
|||
<template>
|
||||
<div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null">
|
||||
<!-- pageHeader , route meta :true on hide -->
|
||||
<page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar">
|
||||
<slot slot="action" name="action"></slot>
|
||||
<slot slot="content" name="headerContent"></slot>
|
||||
<div slot="content" v-if="!this.$slots.headerContent && description">
|
||||
<p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p>
|
||||
<div class="link">
|
||||
<template v-for="(link, index) in linkList">
|
||||
<a :key="index" :href="link.href">
|
||||
<a-icon :type="link.icon" />
|
||||
<span>{{ link.title }}</span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<slot slot="extra" name="extra">
|
||||
<div class="extra-img">
|
||||
<img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
|
||||
</div>
|
||||
</slot>
|
||||
<div slot="pageMenu">
|
||||
<div class="page-menu-search" v-if="search">
|
||||
<a-input-search
|
||||
style="width: 80%; max-width: 522px;"
|
||||
placeholder="请输入..."
|
||||
size="large"
|
||||
enterButton="搜索"
|
||||
/>
|
||||
</div>
|
||||
<div class="page-menu-tabs" v-if="tabs && tabs.items">
|
||||
<!-- @change="callback" :activeKey="activeKey" -->
|
||||
<a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback">
|
||||
<a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</page-header>
|
||||
<div class="content">
|
||||
<div class="page-header-index-wide">
|
||||
<slot>
|
||||
<!-- keep-alive -->
|
||||
<keep-alive v-if="multiTab">
|
||||
<router-view ref="content" />
|
||||
</keep-alive>
|
||||
<router-view v-else ref="content" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import PageHeader from '@/components/PageHeader'
|
||||
|
||||
export default {
|
||||
name: 'PageView',
|
||||
components: {
|
||||
PageHeader
|
||||
},
|
||||
props: {
|
||||
avatar: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
title: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageTitle: null,
|
||||
description: null,
|
||||
linkList: [],
|
||||
extraImage: '',
|
||||
search: false,
|
||||
tabs: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
multiTab: state => state.app.multiTab
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.getPageMeta()
|
||||
},
|
||||
updated() {
|
||||
this.getPageMeta()
|
||||
},
|
||||
methods: {
|
||||
getPageMeta() {
|
||||
// eslint-disable-next-line
|
||||
this.pageTitle = (typeof(this.title) === 'string' || !this.title) ? this.title : this.$route.meta.title
|
||||
|
||||
const content = this.$refs.content
|
||||
if (content) {
|
||||
if (content.pageMeta) {
|
||||
Object.assign(this, content.pageMeta)
|
||||
} else {
|
||||
this.description = content.description
|
||||
this.linkList = content.linkList
|
||||
this.extraImage = content.extraImage
|
||||
this.search = content.search === true
|
||||
this.tabs = content.tabs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.content {
|
||||
margin: 24px 24px 0;
|
||||
.link {
|
||||
margin-top: 16px;
|
||||
&:not(:empty) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
a {
|
||||
margin-right: 32px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
i {
|
||||
font-size: 24px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
span {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.page-menu-search {
|
||||
text-align: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.page-menu-tabs {
|
||||
margin-top: 48px;
|
||||
}
|
||||
|
||||
.extra-img {
|
||||
margin-top: -60px;
|
||||
text-align: center;
|
||||
width: 195px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.extra-img{
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
width: 96px;
|
||||
|
||||
img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,12 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'RouteView',
|
||||
props: {
|
||||
keepAlive: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
@ -17,10 +23,10 @@ export default {
|
|||
// 这里增加了 multiTab 的判断,当开启了 multiTab 时
|
||||
// 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
|
||||
// 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
|
||||
if (meta.keepAlive === false) {
|
||||
if (!getters.multiTab && meta.keepAlive === false) {
|
||||
return notKeep
|
||||
}
|
||||
return getters.multiTab || meta.keepAlive ? inKeep : notKeep
|
||||
return this.keepAlive || getters.multiTab || meta.keepAlive ? inKeep : notKeep
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
import BlankLayout from './BlankLayout'
|
||||
import BasicLayout from './BasicLayout'
|
||||
import RouteView from './RouteView'
|
||||
import PageView from './PageView'
|
||||
|
||||
export { BasicLayout, BlankLayout, RouteView, PageView }
|
|
@ -1,108 +1,56 @@
|
|||
<template>
|
||||
<div class="page-header-index-wide">
|
||||
<page-view>
|
||||
<a-row :gutter="12" type="flex" align="middle">
|
||||
<a-col
|
||||
class="attachment-item"
|
||||
:xl="4"
|
||||
:lg="4"
|
||||
:md="12"
|
||||
:sm="12"
|
||||
:xs="24"
|
||||
>
|
||||
<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" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<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-col class="attachment-item" :xl="4" :lg="4" :md="12" :sm="12" :xs="24">
|
||||
<a-card :bodyStyle="{ padding: '14px' }">
|
||||
<img :src="src" slot="cover" />
|
||||
<img :src="src" slot="cover">
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PageView } from '@/layouts'
|
||||
export default {
|
||||
components: {
|
||||
PageView
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
src: 'https://cdn.ryanc.cc/img/blog/thumbnails/c86b59623c4a9bfeac403824dec3a789.jpg'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="page-header-index-wide">
|
||||
<page-view>
|
||||
<a-card :bordered="false">
|
||||
<div class="table-page-search-wrapper">
|
||||
<a-form layout="inline">
|
||||
|
@ -50,10 +50,11 @@
|
|||
</a-table>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PageView } from '@/layouts'
|
||||
const columns = [
|
||||
{
|
||||
title: '#',
|
||||
|
@ -84,7 +85,9 @@ const columns = [
|
|||
]
|
||||
export default {
|
||||
name: 'CommentList',
|
||||
components: {},
|
||||
components: {
|
||||
PageView
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 查询参数
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<template>
|
||||
<div class="page-header-index-wide">
|
||||
<page-view>
|
||||
<a-row :gutter="12">
|
||||
<a-col
|
||||
:xl="12"
|
||||
:lg="24"
|
||||
:md="24"
|
||||
:sm="24"
|
||||
:xs="24"
|
||||
>
|
||||
<a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
|
||||
<a-card
|
||||
:loading="postLoading"
|
||||
:bordered="false"
|
||||
|
@ -20,29 +14,16 @@
|
|||
:dataSource="formattedPostData"
|
||||
:pagination="false"
|
||||
>
|
||||
<span
|
||||
slot="status"
|
||||
slot-scope="status"
|
||||
>
|
||||
<a-badge :status="status.status" />{{ status.text }}
|
||||
<span slot="status" slot-scope="status">
|
||||
<a-badge :status="status.status"/>
|
||||
{{ status.text }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
slot="editTime"
|
||||
slot-scope="editTime"
|
||||
>
|
||||
{{ editTime | timeAgo }}
|
||||
</span>
|
||||
<span slot="editTime" slot-scope="editTime">{{ editTime | timeAgo }}</span>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
:xl="12"
|
||||
:lg="24"
|
||||
:md="24"
|
||||
:sm="24"
|
||||
:xs="24"
|
||||
>
|
||||
<a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
|
||||
<a-card
|
||||
:loading="commentLoading"
|
||||
:bordered="false"
|
||||
|
@ -54,15 +35,16 @@
|
|||
:columns="commentColumns"
|
||||
:dataSource="commentData"
|
||||
:pagination="false"
|
||||
>
|
||||
</a-table>
|
||||
></a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PageView } from '@/layouts'
|
||||
|
||||
import postApi from '@/api/post'
|
||||
import commentApi from '@/api/comment'
|
||||
import logApi from '@/api/log'
|
||||
|
@ -111,7 +93,9 @@ const commentColumns = [
|
|||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: {},
|
||||
components: {
|
||||
PageView
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
postLoading: true,
|
||||
|
|
Loading…
Reference in New Issue