cli3改版基本完成
Former-commit-id: 637e58a7d3aef389a4ea51c179aaee17f421f34c [formerly 637e58a7d3aef389a4ea51c179aaee17f421f34c [formerly 637e58a7d3aef389a4ea51c179aaee17f421f34c [formerly 637e58a7d3aef389a4ea51c179aaee17f421f34c [formerly fc66dcb2e437ff46b2c36ec1e3bcce71a6461250 [formerly b6451dc60d4c1e6006a9fcd380656d2023436e64]]]]] Former-commit-id: c791410cda91e2df1b9808bfb032f0d3d68106ef Former-commit-id: 0c5197800cfae6f27f7ab792c887fb25a73a23e0 Former-commit-id: 208a8e77c0fada6e9d191a7d495615ec2ef9704d [formerly af8c1367ed65b626196ac156c8521257cc804d60] Former-commit-id: 1fdb571cea6ed9dba9ea02f4ba6ebfb5d89e1b2f Former-commit-id: 774a145ae0694612edf988d9992ef797ddf4f21d Former-commit-id: 03fc24d70365836d60360aa24e24dc7cb4520b91 Former-commit-id: bba4fd5552fa42da029fd6b310f5ee48558d5508 Former-commit-id: 2de81d34fb07248965677e8a3866c13089fa95e7
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="d2-container-full-bs">
|
||||
<div v-if="$slots.header" class="d2-container-full-bs__header" ref="header">
|
||||
<slot name="header"/>
|
||||
</div>
|
||||
<div class="d2-container-full-bs__body" ref="wrapper">
|
||||
<div style="padding: 20px 0px;">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$slots.footer" class="d2-container-full-bs__footer" ref="footer">
|
||||
<slot name="footer"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 插件
|
||||
import BScroll from 'better-scroll'
|
||||
|
||||
export default {
|
||||
name: 'd2-container-full-bs',
|
||||
data () {
|
||||
return {
|
||||
BS: null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.scrollInit()
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.scrollDestroy()
|
||||
},
|
||||
methods: {
|
||||
scrollInit () {
|
||||
this.BS = new BScroll(this.$refs.wrapper, {
|
||||
mouseWheel: true,
|
||||
scrollbar: {
|
||||
fade: true,
|
||||
interactive: false
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollDestroy () {
|
||||
if (this.BS) {
|
||||
this.BS.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="d2-container-full">
|
||||
<div v-if="$slots.header" class="d2-container-full__header" ref="header">
|
||||
<slot name="header"/>
|
||||
</div>
|
||||
<div class="d2-container-full__body">
|
||||
<slot/>
|
||||
</div>
|
||||
<div v-if="$slots.footer" class="d2-container-full__footer" ref="footer">
|
||||
<slot name="footer"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'd2-container-full'
|
||||
}
|
||||
</script>
|
||||
115
src/components/core/d2-container/index.vue
Normal file
115
src/components/core/d2-container/index.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="container-component" :class="{responsive}" ref="container">
|
||||
<!-- [card] 卡片容器 -->
|
||||
<el-card v-if="type === 'card'" shadow="never" class="d2-container-card d2-mr">
|
||||
<slot v-if="$slots.header" name="header" slot="header"/>
|
||||
<slot/>
|
||||
</el-card>
|
||||
<!-- [ghost] 隐形的容器 -->
|
||||
<div v-if="type === 'ghost'" class="d2-container-ghost">
|
||||
<el-card v-if="$slots.header" shadow="never" class="d2-container-ghost-header">
|
||||
<slot name="header"/>
|
||||
</el-card>
|
||||
<slot/>
|
||||
</div>
|
||||
<!-- [container-full] 填充 -->
|
||||
<d2-container-full v-if="type === 'full' && !scroll">
|
||||
<slot v-if="$slots.header" name="header" slot="header"/>
|
||||
<slot/>
|
||||
<slot v-if="$slots.footer" name="footer" slot="footer"/>
|
||||
</d2-container-full>
|
||||
<!-- [container-full-bs] 填充 滚动优化 -->
|
||||
<d2-container-full-bs v-if="type === 'full' && scroll">
|
||||
<slot v-if="$slots.header" name="header" slot="header"/>
|
||||
<slot/>
|
||||
<slot v-if="$slots.footer" name="footer" slot="footer"/>
|
||||
</d2-container-full-bs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 插件
|
||||
import BScroll from 'better-scroll'
|
||||
// 组件
|
||||
import d2ContainerFull from './components/d2-container-full.vue'
|
||||
import d2ContainerFullBs from './components/d2-container-full-bs.vue'
|
||||
export default {
|
||||
name: 'd2-container',
|
||||
props: {
|
||||
// 容器样式
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'card'
|
||||
},
|
||||
// 滚动优化
|
||||
scroll: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
// 是否开启响应式尺寸变化
|
||||
responsive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'd2-container-full': d2ContainerFull,
|
||||
'd2-container-full-bs': d2ContainerFullBs
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
BS: null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.type === 'card' || this.type === 'ghost') {
|
||||
this.scrollInit()
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
if (this.type === 'card' || this.type === 'ghost') {
|
||||
this.scrollDestroy()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scrollInit () {
|
||||
this.BS = new BScroll(this.$refs.container, {
|
||||
mouseWheel: true,
|
||||
scrollbar: {
|
||||
fade: true,
|
||||
interactive: false
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollDestroy () {
|
||||
if (this.BS) {
|
||||
this.BS.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/style/public.scss';
|
||||
@media (min-width: 576px) {
|
||||
// 根据你的需要在这里添加样式
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
// 根据你的需要在这里添加样式
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
// 根据你的需要在这里添加样式
|
||||
}
|
||||
// 在大于1920分辨率的时候
|
||||
@media (min-width: 1921px) {
|
||||
.container-component.responsive {
|
||||
margin: 0px auto;
|
||||
margin-bottom: 20px;
|
||||
max-width: 1920px - 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
103
src/components/core/d2-count-up/index.vue
Normal file
103
src/components/core/d2-count-up/index.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<span></span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CountUp from 'countup.js'
|
||||
export default {
|
||||
name: 'd2-count-up',
|
||||
props: {
|
||||
start: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
end: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
decimals: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 2
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
c: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
end (value) {
|
||||
if (this.c && this.c.update) {
|
||||
this.c.update(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
if (!this.c) {
|
||||
this.c = new CountUp(
|
||||
this.$el,
|
||||
this.start,
|
||||
this.end,
|
||||
this.decimals,
|
||||
this.duration,
|
||||
this.options
|
||||
)
|
||||
this.c.start(() => {
|
||||
this.callback(this.c)
|
||||
})
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
this.c = null
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.destroy()
|
||||
},
|
||||
start (callback) {
|
||||
if (this.c && this.c.start) {
|
||||
this.c.start(() => {
|
||||
callback && callback(this.c)
|
||||
})
|
||||
}
|
||||
},
|
||||
pauseResume () {
|
||||
if (this.c && this.c.pauseResume) {
|
||||
this.c.pauseResume()
|
||||
}
|
||||
},
|
||||
reset () {
|
||||
if (this.c && this.c.reset) {
|
||||
this.c.reset()
|
||||
}
|
||||
},
|
||||
update (newEndVal) {
|
||||
if (this.c && this.c.update) {
|
||||
this.c.update(newEndVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
44
src/components/core/d2-highlight/index.vue
Normal file
44
src/components/core/d2-highlight/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<pre class="d2-highlight" v-html="highlightHTML"></pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// https://highlightjs.org/usage/
|
||||
// http://highlightjs.readthedocs.io/en/latest/api.html#configure-options
|
||||
import highlight from 'highlight.js'
|
||||
export default {
|
||||
name: 'd2-highlight',
|
||||
props: {
|
||||
code: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: `console.log('you lost code prop')`
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
highlightHTML: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.highlight()
|
||||
},
|
||||
watch: {
|
||||
code () {
|
||||
this.highlight()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
highlight () {
|
||||
this.highlightHTML = highlight.highlightAuto(this.code).value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.d2-highlight {
|
||||
margin: 0px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
193
src/components/core/d2-icon-select/index.vue
Normal file
193
src/components/core/d2-icon-select/index.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-popover
|
||||
ref="pop"
|
||||
v-model="pop"
|
||||
:placement="placement"
|
||||
width="300"
|
||||
trigger="click">
|
||||
<div class="header d2-clearfix d2-mb-10" v-if="clearable">
|
||||
<el-button type="danger" icon="el-icon-delete" size="mini" class="d2-fr" @click="selectIcon()">清空</el-button>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
:clearable="true"
|
||||
placeholder="搜索 比如 'plus'"
|
||||
prefix-icon="el-icon-search">
|
||||
</el-input>
|
||||
<el-collapse v-if="!searchMode" class="group" v-model="collapseActive">
|
||||
<el-collapse-item v-for="(item, index) in icon" :key="index" :title="item.title" :name="index" class="class">
|
||||
<el-row class="class-row">
|
||||
<el-col class="class-col" v-for="(iconName, iconIndex) in item.icon" :key="iconIndex" :span="4" @click.native="selectIcon(iconName)">
|
||||
<i :class="'fa fa-' + iconName"></i>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<div v-if="searchMode" class="group">
|
||||
<div class="class" v-for="(item, index) in iconFilted" :key="index">
|
||||
<div class="class-title">{{item.title}}</div>
|
||||
<el-row class="class-row">
|
||||
<el-col class="class-col" v-for="(iconName, iconIndex) in item.icon" :key="iconIndex" :span="4" @click.native="selectIcon(iconName)">
|
||||
<i :class="'fa fa-' + iconName"></i>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<!-- 允许用户输入 -->
|
||||
<el-input
|
||||
v-if="userInput"
|
||||
v-model="currentValue"
|
||||
v-bind="bind"
|
||||
style="max-width: 240px;">
|
||||
<template v-if="value" slot="prepend">
|
||||
<i :class="'fa fa-' + value"></i>
|
||||
</template>
|
||||
<el-button v-popover:pop slot="append">
|
||||
<i class="fa fa-list"></i>
|
||||
</el-button>
|
||||
</el-input>
|
||||
<!-- 不允许用户输入 -->
|
||||
<el-button v-popover:pop v-if="!userInput">
|
||||
<template v-if="value">
|
||||
<i :class="'fa fa-' + value"></i>
|
||||
</template>
|
||||
{{value ? value : placeholder}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icon from '@/assets/library/font-awesome-4.7.0-icon/icon.js'
|
||||
export default {
|
||||
name: 'd2-icon-select',
|
||||
props: {
|
||||
// 值
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
// 占位符
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '请选择'
|
||||
},
|
||||
// 弹出界面的方向
|
||||
placement: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'right'
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
// 是否允许用户输入
|
||||
userInput: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
// 是否在选择后自动关闭
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 绑定弹出框
|
||||
pop: false,
|
||||
// 所有图标
|
||||
icon,
|
||||
// 组件内输入框的值
|
||||
currentValue: '',
|
||||
// 搜索的文字
|
||||
searchText: '',
|
||||
// 不是搜索的时候显示的折叠面板绑定的展开数据
|
||||
collapseActive: []
|
||||
// collapseActive: [...Array(icon.length)].map((e, i) => i)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 输入框上绑定的设置
|
||||
bind () {
|
||||
return {
|
||||
placeholder: this.placeholder,
|
||||
clearable: this.clearable,
|
||||
...this.$attrs
|
||||
}
|
||||
},
|
||||
// 是否在搜索
|
||||
searchMode () {
|
||||
return !!this.searchText
|
||||
},
|
||||
// 过滤后的图标
|
||||
iconFilted () {
|
||||
return this.icon.map(iconClass => ({
|
||||
title: iconClass.title,
|
||||
icon: iconClass.icon.filter(icon => icon.indexOf(this.searchText) >= 0)
|
||||
})).filter(iconClass => iconClass.icon.length > 0)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (value) {
|
||||
this.currentValue = value
|
||||
},
|
||||
currentValue (value) {
|
||||
this.selectIcon(value)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.currentValue = this.value
|
||||
},
|
||||
methods: {
|
||||
selectIcon (iconName = '') {
|
||||
this.$emit('input', iconName)
|
||||
if (iconName && this.autoClose) {
|
||||
this.pop = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/style/public.scss';
|
||||
.group {
|
||||
max-height: 400px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
.class {
|
||||
.class-title {
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
background-color: $color-bg;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
.class-row {
|
||||
.class-col {
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
color: $color-text-sub;
|
||||
&:hover {
|
||||
color: $color-text-main;
|
||||
background-color: $color-bg;
|
||||
border-radius: 4px;
|
||||
font-size: 26px;
|
||||
box-shadow: inset 0px 0px 0px 1px $color-border-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
22
src/components/core/d2-icon-svg/index.vue
Normal file
22
src/components/core/d2-icon-svg/index.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<svg aria-hidden="true">
|
||||
<use :xlink:href="icon"></use>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'd2-icon-svg',
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
icon () {
|
||||
return `#d2-${this.name}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
16
src/components/core/d2-icon/index.vue
Normal file
16
src/components/core/d2-icon/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<i class="fa" :class="`fa-${name}`" aria-hidden="true"></i>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'd2-icon',
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'font-awesome'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="isFullScreen ? '退出全屏' : '全屏'"
|
||||
placement="bottom">
|
||||
<el-button class="d2-mr btn-text can-hover" type="text" @click="d2adminFullScreenToggle">
|
||||
<d2-icon v-if="isFullScreen" name="compress"/>
|
||||
<d2-icon v-else name="arrows-alt" style="font-size: 16px"/>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
isFullScreen: state => state.d2admin.isFullScreen
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
'd2adminFullScreenToggle'
|
||||
])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<div slot="content">
|
||||
<a class="link" :href="url" target="_blank">转到 Github</a>
|
||||
</div>
|
||||
<el-button class="d2-ml-0 d2-mr btn-text can-hover" type="text" @click="handleClick">
|
||||
<d2-icon name="github" style="font-size: 20px"/>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
url: 'https://github.com/d2-projects/d2-admin'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
window.open(this.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.link {
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip effect="dark" content="疑问" placement="bottom">
|
||||
<el-button class="d2-ml-0 d2-mr btn-text can-hover" type="text" @click="dialogVisible = true">
|
||||
<d2-icon name="question-circle" style="font-size: 20px"/>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-dialog title="帮助" width="600px" :visible.sync="dialogVisible">
|
||||
<div style="margin-top: -25px; margin-bottom: -25px;">
|
||||
<h1 class="d2-mt-0">如果你有问题可以加入交流群或者联系作者</h1>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-alert :closable="false" type="info" title="扫码进 QQ 群" class="d2-mb"/>
|
||||
<img class="qr" src="@/assets/image/contact/qq.jpg">
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-alert :closable="false" type="info" title="作者微信 加好友拉进微信群" class="d2-mb"/>
|
||||
<img class="qr" src="@/assets/image/contact/we.jpg">
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
dialogVisible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
img {
|
||||
&.qr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-menu mode="horizontal" @select="handleMenuSelect">
|
||||
<template v-for="(menu, menuIndex) in menus">
|
||||
<d2-layout-main-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
|
||||
<d2-layout-main-menu-sub v-else :menu="menu" :key="menuIndex"/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import menus from '@/menu/index.js'
|
||||
import menuMixin from '../mixin/menu'
|
||||
// 组件
|
||||
import d2LayoutMainMenuItem from '../-menu-item/index.vue'
|
||||
import d2LayoutMainMenuSub from '../-menu-sub/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'd2-layout-main-menu-header',
|
||||
mixins: [
|
||||
menuMixin
|
||||
],
|
||||
components: {
|
||||
'd2-layout-main-menu-item': d2LayoutMainMenuItem,
|
||||
'd2-layout-main-menu-sub': d2LayoutMainMenuSub
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
menus
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<el-menu-item :index="menu.path || uniqueid">
|
||||
<i :class="`fa fa-${menu.icon || 'file-o'}`"></i>
|
||||
<span slot="title">{{menu.title}}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _uniqueid from 'lodash.uniqueid'
|
||||
export default {
|
||||
name: 'd2-layout-main-menu-item',
|
||||
props: {
|
||||
menu: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
uniqueid: _uniqueid('d2-menu-empty-')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="d2-layout-main-menu-side">
|
||||
<el-menu
|
||||
:collapse="collapse"
|
||||
:unique-opened="true"
|
||||
:default-active="active"
|
||||
ref="menu"
|
||||
@select="handleMenuSelect">
|
||||
<template v-for="(menu, menuIndex) in menus">
|
||||
<d2-layout-main-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
|
||||
<d2-layout-main-menu-sub v-else :menu="menu" :key="menuIndex"/>
|
||||
</template>
|
||||
</el-menu>
|
||||
<div v-if="menus.length === 0 && !collapse" class="d2-layout-main-menu-empty">
|
||||
<d2-icon name="hdd-o"/>
|
||||
<span>当前目录没有菜单</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { side } from '@/menu/index.js'
|
||||
import menuMixin from '../mixin/menu'
|
||||
// 组件
|
||||
import d2LayoutMainMenuItem from '../-menu-item/index.vue'
|
||||
import d2LayoutMainMenuSub from '../-menu-sub/index.vue'
|
||||
// 插件
|
||||
import BScroll from 'better-scroll'
|
||||
|
||||
export default {
|
||||
name: 'd2-layout-main-menu-side',
|
||||
mixins: [
|
||||
menuMixin
|
||||
],
|
||||
components: {
|
||||
'd2-layout-main-menu-item': d2LayoutMainMenuItem,
|
||||
'd2-layout-main-menu-sub': d2LayoutMainMenuSub
|
||||
},
|
||||
props: {
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
menus: [],
|
||||
active: '',
|
||||
asideHeight: 300,
|
||||
BS: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 折叠和展开菜单的时候销毁 better scroll
|
||||
collapse (val) {
|
||||
this.scrollDestroy()
|
||||
setTimeout(() => {
|
||||
this.scrollInit()
|
||||
}, 500)
|
||||
},
|
||||
'$route.matched': {
|
||||
handler (val) {
|
||||
const path = val[0].path
|
||||
const _side = side.filter(menu => menu.path === path)
|
||||
this.menus = _side.length > 0 ? _side[0].children : []
|
||||
this.active = val[val.length - 1].path
|
||||
this.$nextTick(() => {
|
||||
if (this.menus.length > 0) {
|
||||
this.$refs.menu.activeIndex = this.active
|
||||
}
|
||||
})
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.scrollInit()
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.scrollDestroy()
|
||||
},
|
||||
methods: {
|
||||
scrollInit () {
|
||||
this.BS = new BScroll(this.$el, {
|
||||
mouseWheel: true,
|
||||
scrollbar: {
|
||||
fade: true,
|
||||
interactive: false
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollDestroy () {
|
||||
if (this.BS) {
|
||||
this.BS.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<el-submenu :index="menu.path || uniqueid">
|
||||
<template slot="title">
|
||||
<i :class="`fa fa-${menu.icon || 'folder-o'}`"></i>
|
||||
<span slot="title">{{menu.title}}</span>
|
||||
</template>
|
||||
<template v-for="(child, childIndex) in menu.children">
|
||||
<d2-layout-main-menu-item v-if="child.children === undefined" :menu="child" :key="childIndex"/>
|
||||
<d2-layout-main-menu-sub v-else :menu="child" :key="childIndex"/>
|
||||
</template>
|
||||
</el-submenu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _uniqueid from 'lodash.uniqueid'
|
||||
// 组件
|
||||
import d2LayoutMainMenuItem from '../-menu-item/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'd2-layout-main-menu-sub',
|
||||
components: {
|
||||
'd2-layout-main-menu-item': d2LayoutMainMenuItem
|
||||
},
|
||||
props: {
|
||||
menu: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
uniqueid: _uniqueid('d2-menu-empty-')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip effect="dark" content="主题" placement="bottom">
|
||||
<el-button class="d2-ml-0 d2-mr btn-text can-hover" type="text" @click="dialogVisible = true">
|
||||
<d2-icon name="diamond" style="font-size: 16px"/>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-dialog title="主题" width="600px" :visible.sync="dialogVisible">
|
||||
<d2-theme-list style="margin-top: -25px;"/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
dialogVisible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-dropdown class="d2-mr">
|
||||
<span class="btn-text">你好 {{username}}</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="logOff"><d2-icon name="power-off"/> 注销</el-dropdown-item>
|
||||
<el-dropdown-item><d2-icon name="user-circle-o"/> 个人中心</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 插件
|
||||
import Cookies from 'js-cookie'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
username: state => state.d2admin.username
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
'd2adminDbRemoveByUuid'
|
||||
]),
|
||||
logOff () {
|
||||
this.$confirm('注销此账户吗?', '注销', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 删除用户名
|
||||
this.d2adminDbRemoveByUuid({
|
||||
key: 'username',
|
||||
emptyValue: ''
|
||||
})
|
||||
// 删除cookie
|
||||
Cookies.remove('token')
|
||||
Cookies.remove('uuid')
|
||||
// 跳转路由
|
||||
this.$router.push({
|
||||
name: 'login'
|
||||
})
|
||||
}).catch(() => {
|
||||
// 取消了注销
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
13
src/components/core/d2-layout-main/components/mixin/menu.js
Normal file
13
src/components/core/d2-layout-main/components/mixin/menu.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
methods: {
|
||||
handleMenuSelect (index, indexPath) {
|
||||
if (/^d2-menu-empty-\d+$/.test(index)) {
|
||||
this.$message('功能正在开发')
|
||||
} else {
|
||||
this.$router.push({
|
||||
path: index
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/components/core/d2-layout-main/index.vue
Normal file
106
src/components/core/d2-layout-main/index.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div
|
||||
class="d2-layout-main-group"
|
||||
:style="styleLayoutMainGroup"
|
||||
:class="{grayMode: isGrayMode}">
|
||||
<!-- 半透明遮罩 -->
|
||||
<div class="d2-layout-main-mask"></div>
|
||||
<!-- 主体内容 -->
|
||||
<div class="d2-layout-main-content">
|
||||
<!-- 顶栏 -->
|
||||
<div class="d2-theme-header">
|
||||
<div class="logo-group" :style="{width: collapse ? asideWidthCollapse : asideWidth}">
|
||||
<img v-if="collapse" :src="`${$baseUrl}/theme/${themeActiveSetting.name}/logo/icon-only.png`">
|
||||
<img v-else :src="`${$baseUrl}/theme/${themeActiveSetting.name}/logo/all.png`">
|
||||
</div>
|
||||
<div class="toggle-aside-btn" @click="collapse = !collapse">
|
||||
<d2-icon name="bars"/>
|
||||
</div>
|
||||
<d2-layout-main-menu-header/>
|
||||
<!-- 顶栏右侧 -->
|
||||
<div class="d2-header-right">
|
||||
<d2-layout-main-header-github/>
|
||||
<d2-layout-main-header-help/>
|
||||
<d2-layout-main-header-full-screen/>
|
||||
<d2-layout-main-header-theme/>
|
||||
<d2-layout-main-header-user/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下面 主体 -->
|
||||
<div class="d2-theme-container">
|
||||
<!-- 主体 侧边栏 -->
|
||||
<div ref="aside" class="d2-theme-container-aside" :style="{width: collapse ? asideWidthCollapse : asideWidth}">
|
||||
<d2-layout-main-menu-side :collapse="collapse"/>
|
||||
</div>
|
||||
<!-- 主体 -->
|
||||
<div class="d2-theme-container-main">
|
||||
<div class="d2-theme-container-main-header">
|
||||
<d2-multiple-page-control/>
|
||||
</div>
|
||||
<div class="d2-theme-container-main-body">
|
||||
<transition name="fade-transverse">
|
||||
<keep-alive :include="keepAliveList">
|
||||
<router-view/>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'd2-layout-main',
|
||||
components: {
|
||||
'd2-layout-main-menu-side': () => import('./components/-menu-side'),
|
||||
'd2-layout-main-menu-header': () => import('./components/-menu-header'),
|
||||
'd2-layout-main-header-full-screen': () => import('./components/-full-screen'),
|
||||
'd2-layout-main-header-theme': () => import('./components/-theme'),
|
||||
'd2-layout-main-header-user': () => import('./components/-user'),
|
||||
'd2-layout-main-header-help': () => import('./components/-help'),
|
||||
'd2-layout-main-header-github': () => import('./components/-github')
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
collapse: false,
|
||||
// [侧边栏宽度] 正常状态
|
||||
asideWidth: '200px',
|
||||
// [侧边栏宽度] 折叠状态
|
||||
asideWidthCollapse: '65px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
isGrayMode: state => state.d2admin.isGrayMode,
|
||||
pageOpenedList: state => state.d2admin.pageOpenedList
|
||||
}),
|
||||
...mapGetters([
|
||||
'themeActiveSetting'
|
||||
]),
|
||||
/**
|
||||
* @description 返回现在需要缓存的页面 name
|
||||
*/
|
||||
keepAliveList () {
|
||||
return this.pageOpenedList.filter(item => !(item.meta && item.meta.notCache)).map(e => e.name)
|
||||
},
|
||||
/**
|
||||
* @description 最外层容器的背景图片样式
|
||||
*/
|
||||
styleLayoutMainGroup () {
|
||||
return {
|
||||
...this.themeActiveSetting.backgroundImage ? {
|
||||
backgroundImage: `url('${this.$baseUrl}${this.themeActiveSetting.backgroundImage}')`
|
||||
} : {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 注册主题
|
||||
@import '~@/assets/style/theme/register.scss';
|
||||
</style>
|
||||
144
src/components/core/d2-markdown/index.vue
Normal file
144
src/components/core/d2-markdown/index.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div class="component-markdown">
|
||||
<div class="spin-group" v-if="!markedHTML">
|
||||
<div>正在加载</div>
|
||||
</div>
|
||||
<div class="markdown-body" v-html="markedHTML"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import marked from 'marked'
|
||||
import highlight from 'highlight.js'
|
||||
import bandupan from './plugin/baidupan'
|
||||
export default {
|
||||
name: 'd2-markdown',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
highlight: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
// 百度网盘分享链接特殊样式
|
||||
baidupan: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
getReadmePublicPath: '',
|
||||
markedHTML: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.url) {
|
||||
this.initWithUrl()
|
||||
} else if (this.source) {
|
||||
this.initWithMd()
|
||||
} else {
|
||||
console.log('not mounted init')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 使用 md 初始化
|
||||
initWithMd () {
|
||||
this.markedHTML = this.marked(this.source)
|
||||
},
|
||||
// 使用 url 初始化
|
||||
async initWithUrl () {
|
||||
this.markedHTML = await this.getReadme(this.url)
|
||||
},
|
||||
// 从 url 加载原始数据
|
||||
async getReadme (name) {
|
||||
const data = await this.$axios.get(name)
|
||||
return this.marked(data)
|
||||
},
|
||||
marked (data) {
|
||||
const renderer = new marked.Renderer()
|
||||
renderer.blockquote = (quote) => {
|
||||
// 百度网盘
|
||||
return (this.baidupan && bandupan(quote, this.$baseUrl)) || `<blockquote>${quote}</blockquote>`
|
||||
}
|
||||
return marked(data, {
|
||||
...this.highlight ? {highlight: (code) => highlight.highlightAuto(code).value} : {},
|
||||
renderer
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/style/public.scss';
|
||||
.component-markdown {
|
||||
.spin-group {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
color: $color-primary;
|
||||
}
|
||||
}
|
||||
// 百度云
|
||||
$baidupanHeight: 30px;
|
||||
$baidupanPadding: 10px;
|
||||
.baidupan {
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
.container {
|
||||
height: $baidupanHeight + 2 * $baidupanPadding;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dfe2e5;
|
||||
padding: $baidupanPadding;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid $color-primary;
|
||||
.line {
|
||||
background-color: $color-primary;
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
float: left;
|
||||
height: $baidupanHeight;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
margin-right: $baidupanPadding;
|
||||
img {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
.url {
|
||||
float: left;
|
||||
height: $baidupanHeight;
|
||||
line-height: $baidupanHeight;
|
||||
color: $color-text-main;
|
||||
}
|
||||
.line {
|
||||
float: left;
|
||||
height: $baidupanHeight + 2 * $baidupanPadding;
|
||||
width: 1px;
|
||||
margin: -$baidupanPadding $baidupanPadding;
|
||||
background-color: #dfe2e5;
|
||||
}
|
||||
.pwd {
|
||||
float: left;
|
||||
height: $baidupanHeight;
|
||||
line-height: $baidupanHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
35
src/components/core/d2-markdown/plugin/baidupan.js
Normal file
35
src/components/core/d2-markdown/plugin/baidupan.js
Normal file
@@ -0,0 +1,35 @@
|
||||
export default (quote, assetsPublicPath) => {
|
||||
const _quote = quote.replace(/<[^<>]+>/g, '').trim()
|
||||
const bdPanUrl = /^https:\/\/pan\.baidu\.com\/s\/[a-z0-9]+$/i
|
||||
const bdPanUrlPwd = /^链接: https:\/\/pan\.baidu\.com\/s\/[a-z0-9]+ 密码: [a-z0-9]{4}$/i
|
||||
if (bdPanUrl.test(_quote)) {
|
||||
return `<div class="baidupan">
|
||||
<a href="${_quote}" class="container">
|
||||
<div class="icon">
|
||||
<img src="${assetsPublicPath}image/baidu-pan-logo.png" style="background-color: transparent;">
|
||||
</div>
|
||||
<div class="url">${_quote}</div>
|
||||
</a>
|
||||
</div>`
|
||||
} else if (bdPanUrlPwd.test(_quote)) {
|
||||
const url = _quote.match(/https:\/\/pan\.baidu\.com\/s\/[a-z0-9]+/i)
|
||||
const pwd = _quote.match(/[a-z0-9]{4}$/i)
|
||||
return `<div class="baidupan">
|
||||
<div class="container">
|
||||
<a href="${url[0]}">
|
||||
<div class="icon">
|
||||
<img src="${assetsPublicPath}image/baidu-pan-logo.png" style="background-color: transparent;">
|
||||
</div>
|
||||
<div class="url">${url[0]}</div>
|
||||
</a>
|
||||
<div class="line"></div>
|
||||
<div class="pwd">
|
||||
密码
|
||||
<span>${pwd[0]}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
61
src/components/core/d2-mde/index.vue
Normal file
61
src/components/core/d2-mde/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<textarea ref="mde"></textarea>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SimpleMDE from 'simplemde'
|
||||
export default {
|
||||
name: 'd2-mde',
|
||||
props: {
|
||||
// 值
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
// 配置参数
|
||||
config: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 编辑器实例
|
||||
mde: null,
|
||||
// 编辑器默认参数
|
||||
// 详见 https://github.com/sparksuite/simplemde-markdown-editor#configuration
|
||||
defaultConfig: {
|
||||
autoDownloadFontAwesome: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化
|
||||
this.init()
|
||||
},
|
||||
destroyed () {
|
||||
// 在组件销毁后销毁实例
|
||||
this.mde = null
|
||||
},
|
||||
methods: {
|
||||
// 初始化
|
||||
init () {
|
||||
// 合并参数
|
||||
const config = Object.assign({}, this.defaultConfig, this.config)
|
||||
// 初始化
|
||||
this.mde = new SimpleMDE({
|
||||
...config,
|
||||
// 初始值
|
||||
initialValue: this.value,
|
||||
// 挂载元素
|
||||
element: this.$refs.mde
|
||||
})
|
||||
this.mde.codemirror.on('change', () => {
|
||||
this.$emit('input', this.mde.value())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
118
src/components/core/d2-multiple-page-control/index.vue
Normal file
118
src/components/core/d2-multiple-page-control/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="d2-multiple-page-control-group">
|
||||
<div class="d2-multiple-page-control-content">
|
||||
<div class="d2-multiple-page-control-content-inner">
|
||||
<el-tabs
|
||||
class="d2-multiple-page-control"
|
||||
:value="pageCurrent"
|
||||
type="card"
|
||||
:closable="true"
|
||||
@tab-click="handleClick"
|
||||
@edit="handleTabsEdit">
|
||||
<el-tab-pane
|
||||
v-for="(page, index) in pageOpenedList"
|
||||
:key="index"
|
||||
:label="page.meta.title || '未命名'"
|
||||
:name="page.name">
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d2-multiple-page-control-btn">
|
||||
<el-dropdown
|
||||
split-button
|
||||
@click="handleControlBtnClick"
|
||||
@command="handleControlItemClick">
|
||||
<d2-icon name="times-circle"/>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="left">
|
||||
<d2-icon name="arrow-left" class="d2-mr-10"/>
|
||||
关闭左侧
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="right">
|
||||
<d2-icon name="arrow-right" class="d2-mr-10"/>
|
||||
关闭右侧
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="other">
|
||||
<d2-icon name="times" class="d2-mr-10"/>
|
||||
关闭其它
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="all">
|
||||
<d2-icon name="times-circle" class="d2-mr-10"/>
|
||||
全部关闭
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
pageOpenedList: state => state.d2admin.pageOpenedList,
|
||||
pageCurrent: state => state.d2admin.pageCurrent
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
'd2adminTagCloseLeft',
|
||||
'd2adminTagCloseRight',
|
||||
'd2adminTagCloseOther',
|
||||
'd2adminTagCloseAll'
|
||||
]),
|
||||
/**
|
||||
* @description 接收点击关闭控制上选项的事件
|
||||
*/
|
||||
handleControlItemClick (command) {
|
||||
switch (command) {
|
||||
case 'left':
|
||||
this.d2adminTagCloseLeft()
|
||||
break
|
||||
case 'right':
|
||||
this.d2adminTagCloseRight()
|
||||
break
|
||||
case 'other':
|
||||
this.d2adminTagCloseOther()
|
||||
break
|
||||
case 'all':
|
||||
this.d2adminTagCloseAll(this)
|
||||
break
|
||||
default:
|
||||
this.$message.error('无效的操作')
|
||||
break
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 接收点击关闭控制上按钮的事件
|
||||
*/
|
||||
handleControlBtnClick () {
|
||||
this.d2adminTagCloseAll(this)
|
||||
},
|
||||
/**
|
||||
* @description 接收点击 tab 标签的事件
|
||||
*/
|
||||
handleClick (tab, event) {
|
||||
// 找到点击的页面在 tag 列表里是哪个
|
||||
const page = this.pageOpenedList.find(page => page.name === tab.name)
|
||||
const { name, params, query } = page
|
||||
if (page) {
|
||||
this.$router.push({ name, params, query })
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 点击 tab 上的删除按钮触发这里 首页的删除按钮已经隐藏 因此这里不用判断是 index
|
||||
*/
|
||||
handleTabsEdit (tagName, action) {
|
||||
if (action === 'remove') {
|
||||
this.$store.commit('d2adminTagClose', {
|
||||
tagName,
|
||||
vm: this
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
77
src/components/core/d2-quill/index.vue
Normal file
77
src/components/core/d2-quill/index.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div ref="editor"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Quill from 'quill'
|
||||
import 'quill/dist/quill.core.css'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import 'quill/dist/quill.bubble.css'
|
||||
export default {
|
||||
name: 'd2-quill',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
Quill: undefined,
|
||||
options: {
|
||||
theme: 'snow',
|
||||
bounds: document.body,
|
||||
debug: 'warn',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
// [{ 'script': 'sub' }, { 'script': 'super' }],
|
||||
// [{ 'indent': '-1' }, { 'indent': '+1' }],
|
||||
// [{ 'direction': 'rtl' }],
|
||||
[{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
// [{ 'font': [] }],
|
||||
[{ 'align': [] }],
|
||||
['clean'],
|
||||
['link', 'image']
|
||||
]
|
||||
},
|
||||
placeholder: '书写你的内容',
|
||||
readOnly: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
const editor = this.$refs.editor
|
||||
this.Quill = new Quill(editor, this.options)
|
||||
// 默认值
|
||||
this.Quill.pasteHTML(this.value)
|
||||
// 绑定事件
|
||||
this.Quill.on('text-change', (delta, oldDelta, source) => {
|
||||
const html = this.$refs.editor.children[0].innerHTML
|
||||
const text = this.Quill.getText()
|
||||
const quill = this.Quill
|
||||
this.$emit('input', html)
|
||||
this.$emit('change', {html, text, quill})
|
||||
})
|
||||
this.Quill.on('text-change', (delta, oldDelta, source) => {
|
||||
this.$emit('text-change', delta, oldDelta, source)
|
||||
})
|
||||
this.Quill.on('selection-change', (range, oldRange, source) => {
|
||||
this.$emit('selection-change', range, oldRange, source)
|
||||
})
|
||||
this.Quill.on('editor-change', (eventName, ...args) => {
|
||||
this.$emit('editor-change', eventName, ...args)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
58
src/components/core/d2-theme-list/index.vue
Normal file
58
src/components/core/d2-theme-list/index.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<el-table :data="themeList" v-bind="table">
|
||||
<el-table-column prop="title" align="center" width="160"/>
|
||||
<el-table-column label="预览" width="120">
|
||||
<div
|
||||
slot-scope="scope"
|
||||
class="theme-preview"
|
||||
:style="{'backgroundImage': `url(${$baseUrl}${scope.row.preview})`}">
|
||||
</div>
|
||||
</el-table-column>
|
||||
<el-table-column prop="address" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="themeActiveName === scope.row.name" type="success" icon="el-icon-check" round>已激活</el-button>
|
||||
<el-button v-else round @click="handleSelectTheme(scope.row.name)">使用</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
export default {
|
||||
name: 'd2-theme-list',
|
||||
data () {
|
||||
return {
|
||||
table: {
|
||||
showHeader: false,
|
||||
border: true
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
themeList: state => state.d2admin.themeList,
|
||||
themeActiveName: state => state.d2admin.themeActiveName
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
'd2adminThemeSet'
|
||||
]),
|
||||
handleSelectTheme (name) {
|
||||
this.d2adminThemeSet(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/style/public.scss';
|
||||
.theme-preview {
|
||||
height: 50px;
|
||||
width: 100px;
|
||||
border-radius: 4px;
|
||||
background-size: cover;
|
||||
border: 1px solid $color-border-1;
|
||||
}
|
||||
</style>
|
||||
23
src/components/core/register.js
Normal file
23
src/components/core/register.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
import { GridLayout, GridItem } from 'vue-grid-layout'
|
||||
import SplitPane from 'vue-splitpane'
|
||||
|
||||
import d2Container from '@/components/core/d2-container'
|
||||
import d2MultiplePageControl from '@/components/core/d2-multiple-page-control'
|
||||
|
||||
Vue.component('d2-grid-layout', GridLayout)
|
||||
Vue.component('d2-grid-item', GridItem)
|
||||
Vue.component('SplitPane', SplitPane)
|
||||
|
||||
Vue.component('d2-container', d2Container)
|
||||
Vue.component('d2-multiple-page-control', d2MultiplePageControl)
|
||||
Vue.component('d2-count-up', () => import('@/components/core/d2-count-up'))
|
||||
Vue.component('d2-highlight', () => import('@/components/core/d2-highlight'))
|
||||
Vue.component('d2-icon', () => import('@/components/core/d2-icon'))
|
||||
Vue.component('d2-icon-select', () => import('@/components/core/d2-icon-select/index.vue'))
|
||||
Vue.component('d2-icon-svg', () => import('@/components/core/d2-icon-svg/index.vue'))
|
||||
Vue.component('d2-markdown', () => import('@/components/core/d2-markdown'))
|
||||
Vue.component('d2-mde', () => import('@/components/core/d2-mde'))
|
||||
Vue.component('d2-quill', () => import('@/components/core/d2-quill'))
|
||||
Vue.component('d2-theme-list', () => import('@/components/core/d2-theme-list'))
|
||||
Reference in New Issue
Block a user