Merge branch 'feature/store-module' into develop

# Conflicts:
#	src/pages/demo/playground/store/menu/index.vue


Former-commit-id: 77cb9e9175b35a94c8c5cbefb6981f75718bf282 [formerly 77cb9e9175b35a94c8c5cbefb6981f75718bf282 [formerly 77cb9e9175b35a94c8c5cbefb6981f75718bf282 [formerly 77cb9e9175b35a94c8c5cbefb6981f75718bf282 [formerly 6ebe5372a382d911bdbe82f5a1867f72ab58f286 [formerly f7811326ccd36bd64b92f6c3094060be390a2b76]]]]]
Former-commit-id: 1a0ec7498901cca64a7d12c2b2c8f13ae701ca21
Former-commit-id: 3531de0dd9e330312c5073676f4c3aa82d28dfb9
Former-commit-id: cc70c17a885ff8b72df8b799921e38fbcbedd3e9 [formerly 73282cfd24758da343bea77d413773d569606110]
Former-commit-id: fa461f51727fdfc67b8ae790e1f0df161f85ca8b
Former-commit-id: 10e2f2cfce4a446c01957229fe254e4c5ec211a6
Former-commit-id: 602f7a473458e6f5f0d5ec96388e608971daca34
Former-commit-id: bb4a69677e9c9a76c8f653fb2b7c714eff13fa67
Former-commit-id: 277ce50885de084a418ba885418c22e8978e3c22
This commit is contained in:
liyang
2018-08-12 08:07:35 +08:00
39 changed files with 1372 additions and 371 deletions

View File

@@ -28,7 +28,9 @@
"js-cookie": "^2.2.0",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"lodash.sortby": "^4.7.0",
"lodash.tostring": "^4.1.4",
"lodash.uniqueid": "^4.0.1",
"lowdb": "^1.0.0",
"marked": "^0.3.9",

View File

@@ -136,15 +136,15 @@ export default {
ExpandItem
},
computed: {
...mapState({
log: state => state.d2admin.log
...mapState('d2admin', {
logList: state => state.log.list
}),
logReverse () {
// 直接 reverse 的话有点问题
const res = []
const loglength = this.log.length
this.log.forEach((log, index) => {
res.push(this.log[loglength - 1 - index])
const loglength = this.logList.length
this.logList.forEach((log, index) => {
res.push(this.logList[loglength - 1 - index])
})
return res
}

View File

@@ -9,12 +9,12 @@
type="text"
@click="handleClick">
<el-badge
v-if="d2adminLogLength > 0"
v-if="logLength > 0"
:max="99"
:value="d2adminLogErrorLength"
:is-dot="d2adminLogErrorLength === 0">
:value="logLengthError"
:is-dot="logLengthError === 0">
<d2-icon
:name="d2adminLogErrorLength === 0 ? 'dot-circle-o' : 'bug'"
:name="logLengthError === 0 ? 'dot-circle-o' : 'bug'"
style="font-size: 20px"/>
</el-badge>
<d2-icon
@@ -49,30 +49,30 @@ export default {
}
},
computed: {
...mapGetters([
'd2adminLogLength',
'd2adminLogErrorLength'
]),
...mapGetters('d2admin', {
logLength: 'log/length',
logLengthError: 'log/lengthError'
}),
tooltipContent () {
return this.d2adminLogLength === 0
return this.logLength === 0
? '没有日志或异常'
: `${this.d2adminLogLength} 条日志${this.d2adminLogErrorLength > 0
? ` | 包含 ${this.d2adminLogErrorLength} 个异常`
: `${this.logLength} 条日志${this.logLengthError > 0
? ` | 包含 ${this.logLengthError} 个异常`
: ''}`
}
},
methods: {
...mapMutations([
'd2adminLogClean'
...mapMutations('d2admin/log', [
'clean'
]),
handleClick () {
if (this.d2adminLogLength > 0) {
if (this.logLength > 0) {
this.dialogVisible = true
}
},
handleLogClean () {
this.dialogVisible = false
this.d2adminLogClean()
this.clean()
}
}
}

View File

@@ -1,10 +1,10 @@
<template>
<el-tooltip
effect="dark"
:content="isFullScreen ? '退出全屏' : '全屏'"
:content="active ? '退出全屏' : '全屏'"
placement="bottom">
<el-button class="d2-mr btn-text can-hover" type="text" @click="d2adminFullScreenToggle">
<d2-icon v-if="isFullScreen" name="compress"/>
<el-button class="d2-mr btn-text can-hover" type="text" @click="toggle">
<d2-icon v-if="active" name="compress"/>
<d2-icon v-else name="arrows-alt" style="font-size: 16px"/>
</el-button>
</el-tooltip>
@@ -14,13 +14,13 @@
import { mapState, mapMutations } from 'vuex'
export default {
computed: {
...mapState({
isFullScreen: state => state.d2admin.isFullScreen
})
...mapState('d2admin/fullscreen', [
'active'
])
},
methods: {
...mapMutations([
'd2adminFullScreenToggle'
...mapMutations('d2admin/fullscreen', [
'toggle'
])
}
}

View File

@@ -1,5 +1,5 @@
<template>
<el-table :data="themeList" v-bind="table">
<el-table :data="list" v-bind="table">
<el-table-column prop="title" align="center" width="160"/>
<el-table-column label="预览" width="120">
<div
@@ -10,7 +10,7 @@
</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-if="activeName === 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>
@@ -30,17 +30,17 @@ export default {
}
},
computed: {
...mapState({
themeList: state => state.d2admin.themeList,
themeActiveName: state => state.d2admin.themeActiveName
})
...mapState('d2admin/theme', [
'list',
'activeName'
])
},
methods: {
...mapMutations([
'd2adminThemeSet'
...mapMutations('d2admin/theme', [
'set'
]),
handleSelectTheme (name) {
this.d2adminThemeSet(name)
this.set(name)
}
}
}

View File

@@ -1,6 +1,6 @@
<template>
<el-dropdown class="d2-mr">
<span class="btn-text">你好 {{userInfo.name}}</span>
<span class="btn-text">你好 {{info.name}}</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="logOff">
<d2-icon name="power-off" class="d2-mr-5"/>
@@ -14,19 +14,19 @@
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState({
userInfo: state => state.d2admin.userInfo
})
...mapState('d2admin/user', [
'info'
])
},
methods: {
...mapActions([
'd2adminLogout'
...mapActions('d2admin/account', [
'logout'
]),
/**
* @description 登出
*/
logOff () {
this.d2adminLogout({
this.logout({
vm: this,
confirm: true
})

View File

@@ -1,6 +1,6 @@
<template>
<el-menu mode="horizontal" @select="handleMenuSelect">
<template v-for="(menu, menuIndex) in menuHeader">
<template v-for="(menu, menuIndex) in header">
<d2-layout-header-aside-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
<d2-layout-header-aside-menu-sub v-else :menu="menu" :key="menuIndex"/>
</template>
@@ -22,9 +22,9 @@ export default {
'd2-layout-header-aside-menu-sub': d2LayoutMainMenuSub
},
computed: {
...mapState({
menuHeader: state => state.d2admin.menuHeader
})
...mapState('d2admin/menu', [
'header'
])
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<div class="d2-layout-header-aside-menu-side">
<el-menu
:collapse="isMenuAsideCollapse"
:collapse="asideCollapse"
:unique-opened="true"
:default-active="active"
ref="menu"
@select="handleMenuSelect">
<template v-for="(menu, menuIndex) in menuAside">
<template v-for="(menu, menuIndex) in aside">
<d2-layout-header-aside-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
<d2-layout-header-aside-menu-sub v-else :menu="menu" :key="menuIndex"/>
</template>
</el-menu>
<div v-if="menuAside.length === 0 && !isMenuAsideCollapse" class="d2-layout-header-aside-menu-empty" flex="dir:top main:center cross:center">
<div v-if="aside.length === 0 && !asideCollapse" class="d2-layout-header-aside-menu-empty" flex="dir:top main:center cross:center">
<d2-icon name="inbox"/>
<span>没有侧栏菜单</span>
</div>
@@ -41,14 +41,14 @@ export default {
}
},
computed: {
...mapState({
menuAside: state => state.d2admin.menuAside,
isMenuAsideCollapse: state => state.d2admin.isMenuAsideCollapse
})
...mapState('d2admin/menu', [
'aside',
'asideCollapse'
])
},
watch: {
// 折叠和展开菜单的时候销毁 better scroll
isMenuAsideCollapse (val) {
asideCollapse (val) {
this.scrollDestroy()
setTimeout(() => {
this.scrollInit()
@@ -59,7 +59,7 @@ export default {
handler (val) {
this.active = val[val.length - 1].path
this.$nextTick(() => {
if (this.menuAside.length > 0) {
if (this.aside.length > 0) {
this.$refs.menu.activeIndex = this.active
}
})

View File

@@ -12,14 +12,14 @@
</d2-contextmenu>
<el-tabs
class="d2-multiple-page-control"
:value="pageCurrent"
:value="current"
type="card"
:closable="true"
@tab-click="handleClick"
@edit="handleTabsEdit"
@contextmenu.native="handleContextmenu">
<el-tab-pane
v-for="(page, index) in pageOpenedList"
v-for="(page, index) in opened"
:key="index"
:label="page.meta.title || '未命名'"
:name="page.name"/>
@@ -82,17 +82,18 @@ export default {
}
},
computed: {
...mapState({
pageOpenedList: state => state.d2admin.pageOpenedList,
pageCurrent: state => state.d2admin.pageCurrent
})
...mapState('d2admin/page', [
'opened',
'current'
])
},
methods: {
...mapMutations([
'd2adminTagCloseLeft',
'd2adminTagCloseRight',
'd2adminTagCloseOther',
'd2adminTagCloseAll'
...mapMutations('d2admin/page', [
'close',
'closeLeft',
'closeRight',
'closeOther',
'closeAll'
]),
/**
* @description 右键菜单功能点击
@@ -136,16 +137,16 @@ export default {
}
switch (command) {
case 'left':
this.d2adminTagCloseLeft(params)
this.closeLeft(params)
break
case 'right':
this.d2adminTagCloseRight(params)
this.closeRight(params)
break
case 'other':
this.d2adminTagCloseOther(params)
this.closeOther(params)
break
case 'all':
this.d2adminTagCloseAll(this)
this.closeAll(this)
break
default:
this.$message.error('无效的操作')
@@ -156,14 +157,14 @@ export default {
* @description 接收点击关闭控制上按钮的事件
*/
handleControlBtnClick () {
this.d2adminTagCloseAll(this)
this.closeAll(this)
},
/**
* @description 接收点击 tab 标签的事件
*/
handleClick (tab, event) {
// 找到点击的页面在 tag 列表里是哪个
const page = this.pageOpenedList.find(page => page.name === tab.name)
const page = this.opened.find(page => page.name === tab.name)
const { name, params, query } = page
if (page) {
this.$router.push({ name, params, query })
@@ -174,7 +175,7 @@ export default {
*/
handleTabsEdit (tagName, action) {
if (action === 'remove') {
this.$store.commit('d2adminTagClose', {
this.close({
tagName,
vm: this
})

View File

@@ -2,16 +2,16 @@
<div
class="d2-layout-header-aside-group"
:style="styleLayoutMainGroup"
:class="{grayMode: isGrayMode}">
:class="{grayMode: grayActive}">
<!-- 半透明遮罩 -->
<div class="d2-layout-header-aside-mask"></div>
<!-- 主体内容 -->
<div class="d2-layout-header-aside-content" flex="dir:top">
<!-- 顶栏 -->
<div class="d2-theme-header" flex-box="0">
<div class="logo-group" :style="{width: isMenuAsideCollapse ? asideWidthCollapse : asideWidth}">
<img v-if="isMenuAsideCollapse" :src="`${$baseUrl}image/theme/${d2adminThemeActiveSetting.name}/logo/icon-only.png`">
<img v-else :src="`${$baseUrl}image/theme/${d2adminThemeActiveSetting.name}/logo/all.png`">
<div class="logo-group" :style="{width: asideCollapse ? asideWidthCollapse : asideWidth}">
<img v-if="asideCollapse" :src="`${$baseUrl}image/theme/${themeActiveSetting.name}/logo/icon-only.png`">
<img v-else :src="`${$baseUrl}image/theme/${themeActiveSetting.name}/logo/all.png`">
</div>
<div class="toggle-aside-btn" @click="handleToggleAside">
<d2-icon name="bars"/>
@@ -34,7 +34,7 @@
flex-box="0"
ref="aside"
class="d2-theme-container-aside"
:style="{width: isMenuAsideCollapse ? asideWidthCollapse : asideWidth}">
:style="{width: asideCollapse ? asideWidthCollapse : asideWidth}">
<d2-menu-side/>
</div>
<!-- 主体 -->
@@ -44,7 +44,7 @@
</div>
<div class="d2-theme-container-main-body" flex-box="1">
<transition name="fade-transverse">
<keep-alive :include="d2adminKeepAliveInclude">
<keep-alive :include="keepAlive">
<router-view/>
</keep-alive>
</transition>
@@ -78,35 +78,34 @@ export default {
}
},
computed: {
...mapState({
isGrayMode: state => state.d2admin.isGrayMode,
pageOpenedList: state => state.d2admin.pageOpenedList,
isMenuAsideCollapse: state => state.d2admin.isMenuAsideCollapse
...mapState('d2admin', {
grayActive: state => state.gray.active,
asideCollapse: state => state.menu.asideCollapse
}),
...mapGetters('d2admin', {
keepAlive: 'page/keepAlive',
themeActiveSetting: 'theme/activeSetting'
}),
...mapGetters([
'd2adminThemeActiveSetting',
'd2adminKeepAliveInclude'
]),
/**
* @description 最外层容器的背景图片样式
*/
styleLayoutMainGroup () {
return {
...this.d2adminThemeActiveSetting.backgroundImage ? {
backgroundImage: `url('${this.$baseUrl}${this.d2adminThemeActiveSetting.backgroundImage}')`
...this.themeActiveSetting.backgroundImage ? {
backgroundImage: `url('${this.$baseUrl}${this.themeActiveSetting.backgroundImage}')`
} : {}
}
}
},
methods: {
...mapMutations([
'd2adminMenuAsideCollapseToggle'
...mapMutations('d2admin/menu', [
'asideCollapseToggle'
]),
/**
* 接收点击切换侧边栏的按钮
*/
handleToggleAside () {
this.d2adminMenuAsideCollapseToggle()
this.asideCollapseToggle()
}
}
}

View File

@@ -7,12 +7,12 @@ const db = low(adapter)
// 初始化数据库
db.defaults({
themeActiveName: [],
pageOpenedList: [],
userInfo: [],
isMenuAsideCollapse: [],
database: [],
databasePublic: {}
// 系统
sys: {},
// 存储
database: {},
// 暂时的
db: {}
}).write()
export default db

43
src/libs/util.cookies.js Normal file
View File

@@ -0,0 +1,43 @@
import Cookies from 'js-cookie'
import { version } from '../../package.json'
const cookies = {}
/**
* @description 存储 cookie 值
* @param {String} name cookie name
* @param {String} value cookie value
* @param {Object} setting cookie setting
*/
cookies.set = function (name = 'default', value = '', setting = {}) {
let cookieSetting = {
expires: 1
}
Object.assign(cookieSetting, setting)
Cookies.set(`d2admin-${version}-${name}`, value, cookieSetting)
}
/**
* @description 拿到 cookie 值
* @param {String} name cookie name
*/
cookies.get = function (name = 'default') {
return Cookies.get(`d2admin-${version}-${name}`)
}
/**
* @description 拿到 cookie 全部的值
*/
cookies.getAll = function () {
return Cookies.get()
}
/**
* @description 删除 cookie
* @param {String} name cookie name
*/
cookies.remove = function (name = 'default') {
return Cookies.remove(`d2admin-${version}-${name}`)
}
export default cookies

View File

@@ -1,49 +1,12 @@
import Cookies from 'js-cookie'
import axios from 'axios'
import semver from 'semver'
import UaParser from 'ua-parser-js'
import { version } from '../../package.json'
import log from './util.log.js'
import cookies from './util.cookies.js'
let util = {
cookies: {},
log: {}
}
/**
* @description 存储 cookie 值
* @param {String} name cookie name
* @param {String} value cookie value
* @param {Object} setting cookie setting
*/
util.cookies.set = function (name = 'default', value = '', setting = {}) {
let cookieSetting = {
expires: 1
}
Object.assign(cookieSetting, setting)
Cookies.set(`d2admin-${version}-${name}`, value, cookieSetting)
}
/**
* @description 拿到 cookie 值
* @param {String} name cookie name
*/
util.cookies.get = function (name = 'default') {
return Cookies.get(`d2admin-${version}-${name}`)
}
/**
* @description 拿到 cookie 全部的值
*/
util.cookies.getAll = function () {
return Cookies.get()
}
/**
* @description 删除 cookie
* @param {String} name cookie name
*/
util.cookies.remove = function (name = 'default') {
return Cookies.remove(`d2admin-${version}-${name}`)
cookies,
log
}
/**
@@ -54,13 +17,6 @@ util.title = function (titleText) {
window.document.title = `${process.env.VUE_APP_TITLE}${titleText ? ` | ${titleText}` : ''}`
}
/**
* @description 获取所有的 UA 信息
*/
util.ua = function () {
return new UaParser().getResult()
}
/**
* @description 判断是否在其内
* @param {*} ele element
@@ -74,76 +30,6 @@ util.isOneOf = function (ele, targetArr) {
}
}
/**
* @description 返回这个样式的颜色值
* @param {String} type 样式名称 [ primary | success | warning | danger | text ]
*/
util.typeColor = function (type = 'default') {
let color = ''
switch (type) {
case 'default': color = '35495E'; break
case 'primary': color = '#3488ff'; break
case 'success': color = '#43B883'; break
case 'warning': color = '#e6a23c'; break
case 'danger': color = '#f56c6c'; break
default:; break
}
return color
}
/**
* @description 打印一个 “胶囊” 样式的信息
* @param {String} title title text
* @param {String} info info text
* @param {String} type style
*/
util.log.capsule = function (title, info, type = 'primary') {
console.log(
`%c ${title} %c ${info} %c`,
'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;',
`background:${util.typeColor(type)}; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;`,
'background:transparent'
)
}
/**
* @description 打印彩色文字
*/
util.log.colorful = function (textArr) {
console.log(
`%c ${textArr.map(t => t.text).join(' %c ')}`,
...textArr.map(t => `color: ${util.typeColor(t.type)};`)
)
}
/**
* @description 打印 primary 样式的文字
*/
util.log.primary = function (text) {
util.log.colorful([{ text, type: 'primary' }])
}
/**
* @description 打印 success 样式的文字
*/
util.log.success = function (text) {
util.log.colorful([{ text, type: 'success' }])
}
/**
* @description 打印 warning 样式的文字
*/
util.log.warning = function (text) {
util.log.colorful([{ text, type: 'warning' }])
}
/**
* @description 打印 danger 样式的文字
*/
util.log.danger = function (text) {
util.log.colorful([{ text, type: 'danger' }])
}
/**
* @description 检查版本更新
* @param {Object} vm vue
@@ -157,11 +43,11 @@ util.checkUpdate = function (vm) {
let versionGet = res.tag_name
const update = semver.lt(version, versionGet)
if (update) {
util.log.capsule('D2Admin', `New version ${res.name}`)
log.capsule('D2Admin', `New version ${res.name}`)
console.log(`版本号: ${res.tag_name} | 详情${res.html_url}`)
vm.$store.commit('d2adminReleasesUpdateSet', true)
vm.$store.commit('d2admin/releases/updateSet', true)
}
vm.$store.commit('d2adminReleasesLatestSet', res)
vm.$store.commit('d2admin/releases/latestSet', res)
})
.catch(err => {
console.log('checkUpdate error', err)
@@ -172,7 +58,7 @@ util.checkUpdate = function (vm) {
* @description 显示版本信息
*/
util.showInfo = function showInfo () {
util.log.capsule('D2Admin', `v${version}`)
log.capsule('D2Admin', `v${version}`)
console.log('Github https://github.com/d2-projects/d2-admin')
console.log('Doc http://d2admin.fairyever.com/zh/')
}

80
src/libs/util.log.js Normal file
View File

@@ -0,0 +1,80 @@
const log = {}
/**
* @description 返回这个样式的颜色值
* @param {String} type 样式名称 [ primary | success | warning | danger | text ]
*/
function typeColor (type = 'default') {
let color = ''
switch (type) {
case 'default': color = '#35495E'; break
case 'primary': color = '#3488ff'; break
case 'success': color = '#43B883'; break
case 'warning': color = '#e6a23c'; break
case 'danger': color = '#f56c6c'; break
default:; break
}
return color
}
/**
* @description 打印一个 [ title | text ] 样式的信息
* @param {String} title title text
* @param {String} info info text
* @param {String} type style
*/
log.capsule = function (title, info, type = 'primary') {
console.log(
`%c ${title} %c ${info} %c`,
'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;',
`background:${typeColor(type)}; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;`,
'background:transparent'
)
}
/**
* @description 打印彩色文字
*/
log.colorful = function (textArr) {
console.log(
`%c ${textArr.map(t => t.text || '').join(' %c ')}`,
...textArr.map(t => `color: ${typeColor(t.type)};`)
)
}
/**
* @description 打印 default 样式的文字
*/
log.default = function (text) {
log.colorful([{ text }])
}
/**
* @description 打印 primary 样式的文字
*/
log.primary = function (text) {
log.colorful([{ text, type: 'primary' }])
}
/**
* @description 打印 success 样式的文字
*/
log.success = function (text) {
log.colorful([{ text, type: 'success' }])
}
/**
* @description 打印 warning 样式的文字
*/
log.warning = function (text) {
log.colorful([{ text, type: 'warning' }])
}
/**
* @description 打印 danger 样式的文字
*/
log.danger = function (text) {
log.colorful([{ text, type: 'danger' }])
}
export default log

View File

@@ -51,17 +51,17 @@ new Vue({
// 处理路由 得到每一级的路由设置
this.getAllPageFromRoutes()
// 设置顶栏菜单
this.$store.commit('d2adminMenuHeaderSet', menuHeader)
this.$store.commit('d2admin/menu/headerSet', menuHeader)
},
mounted () {
// D2Admin 开发环境检查更新
util.checkUpdate(this)
// 获取并记录用户 UA
this.$store.commit('d2adminUaGet')
this.$store.commit('d2admin/ua/get')
// 展示系统信息
util.showInfo()
// 用户登陆后从数据库加载一系列的设置
this.$store.commit('d2adminLoginSuccessLoad')
this.$store.commit('d2admin/account/load')
// 初始化全屏监听
this.fullscreenListenerInit()
},
@@ -69,7 +69,7 @@ new Vue({
// 监听路由 控制侧边栏显示
'$route.matched' (val) {
const _side = menuAside.filter(menu => menu.path === val[0].path)
this.$store.commit('d2adminMenuAsideSet', _side.length > 0 ? _side[0].children : [])
this.$store.commit('d2admin/menu/asideSet', _side.length > 0 ? _side[0].children : [])
}
},
methods: {
@@ -80,7 +80,7 @@ new Vue({
if (screenfull.enabled) {
screenfull.on('change', () => {
if (!screenfull.isFullscreen) {
this.$store.commit('d2adminFullScreenSet', false)
this.$store.commit('d2admin/fullscreen/set', false)
}
})
}
@@ -101,7 +101,7 @@ new Vue({
})
}
push(frameInRoutes)
this.$store.commit('d2adminPagePoolSet', pool)
this.$store.commit('d2admin/page/poolSet', pool)
}
}
}).$mount('#app')

View File

@@ -32,7 +32,7 @@
<script>
import day from 'dayjs'
import { mapMutations } from 'vuex'
import { mapActions } from 'vuex'
export default {
data () {
return {
@@ -54,66 +54,63 @@ export default {
this.load()
},
methods: {
...mapMutations([
'd2adminUtilDatabase',
'd2adminUtilDatabaseClear'
...mapActions('d2admin/db', [
'database',
'databaseClear'
]),
/**
* 加载本地数据
*/
load () {
this.d2adminUtilDatabase(database => {
this.dataDisplay = JSON.stringify(database.value(), null, 2)
this.keyNameList = Object.keys(database.value()).map(k => ({
value: k,
label: k
}))
})
async load () {
const db = await this.database()
this.dataDisplay = JSON.stringify(db.value(), null, 2)
this.keyNameList = Object.keys(db.value()).map(k => ({
value: k,
label: k
}))
},
/**
* 删除一个字段
*/
handleDelete (name) {
this.d2adminUtilDatabase(database => {
database
.unset(name)
.write()
})
async handleDelete (name) {
const db = await this.database()
db
.unset(name)
.write()
this.load()
this.keyNameToDelete = ''
},
/**
* 清空当前用户的数据
*/
handleClear () {
this.d2adminUtilDatabaseClear()
async handleClear () {
const db = await this.databaseClear()
// db 是已经清空了的数据库对象 没有后续操作的话可以不接收这个值
this.load()
},
/**
* 添加一个数据
*/
handleSet () {
async handleSet () {
if (this.keyNameToSet === '') {
this.$message.error('字段名不能为空')
return
}
this.d2adminUtilDatabase(database => {
database
.set(this.keyNameToSet, this.valueToSet)
.write()
})
const db = await this.database()
db
.set(this.keyNameToSet, this.valueToSet)
.write()
this.load()
},
/**
* 添加一个随机数据
*/
handleSetRandom () {
this.d2adminUtilDatabase(database => {
const id = day().valueOf()
database
.set(id, Math.round(id * Math.random()))
.write()
})
async handleSetRandom () {
const id = day().valueOf()
const db = await this.database()
db
.set(id, Math.round(id * Math.random()))
.write()
this.load()
}
}

View File

@@ -32,7 +32,7 @@
<script>
import day from 'dayjs'
import { mapMutations } from 'vuex'
import { mapActions } from 'vuex'
export default {
data () {
return {
@@ -54,66 +54,63 @@ export default {
this.load()
},
methods: {
...mapMutations([
'd2adminUtilDatabaseUser',
'd2adminUtilDatabaseUserClear'
...mapActions('d2admin/db', [
'databaseByUser',
'databaseByUserClear'
]),
/**
* 加载本地数据
*/
load () {
this.d2adminUtilDatabaseUser(database => {
this.dataDisplay = JSON.stringify(database.value(), null, 2)
this.keyNameList = Object.keys(database.value()).map(k => ({
value: k,
label: k
}))
})
async load () {
const db = await this.databaseByUser()
this.dataDisplay = JSON.stringify(db.value(), null, 2)
this.keyNameList = Object.keys(db.value()).map(k => ({
value: k,
label: k
}))
},
/**
* 删除一个字段
*/
handleDelete (name) {
this.d2adminUtilDatabaseUser(database => {
database
.unset(name)
.write()
})
async handleDelete (name) {
const db = await this.databaseByUser()
db
.unset(name)
.write()
this.load()
this.keyNameToDelete = ''
},
/**
* 清空当前用户的数据
*/
handleClear () {
this.d2adminUtilDatabaseUserClear()
async handleClear () {
const db = await this.databaseByUserClear()
// db 是已经清空了的数据库对象 没有后续操作的话可以不接收这个值
this.load()
},
/**
* 添加一个数据
*/
handleSet () {
async handleSet () {
if (this.keyNameToSet === '') {
this.$message.error('字段名不能为空')
return
}
this.d2adminUtilDatabaseUser(database => {
database
.set(this.keyNameToSet, this.valueToSet)
.write()
})
const db = await this.databaseByUser()
db
.set(this.keyNameToSet, this.valueToSet)
.write()
this.load()
},
/**
* 添加一个随机数据
*/
handleSetRandom () {
this.d2adminUtilDatabaseUser(database => {
const id = day().valueOf()
database
.set(id, Math.round(id * Math.random()))
.write()
})
async handleSetRandom () {
const id = day().valueOf()
const db = await this.databaseByUser()
db
.set(id, Math.round(id * Math.random()))
.write()
this.load()
}
}

View File

@@ -1,23 +1,18 @@
<template>
<d2-container type="card" class="page-demo-playground-fullscreen">
<template slot="header">全屏</template>
<el-button type="primary" @click="d2adminFullScreenToggle">
d2adminFullScreenToggle 切换全屏
<el-button type="primary" @click="toggle">
toggle 切换全屏
</el-button>
</d2-container>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { mapMutations } from 'vuex'
export default {
computed: {
...mapState({
isFullScreen: state => state.d2admin.isFullScreen
})
},
methods: {
...mapMutations([
'd2adminFullScreenToggle'
...mapMutations('d2admin/fullscreen', [
'toggle'
])
}
}

View File

@@ -1,12 +1,12 @@
<template>
<d2-container type="card" class="page-demo-playground-gray">
<template slot="header">
<div class="colorful">{{isGrayMode ? 'GRAY' : 'COLORFUL'}}</div>
<div class="colorful">{{grayActive ? 'GRAY' : 'COLORFUL'}}</div>
</template>
<el-button-group>
<el-button @click="d2adminGrayModeToggle">切换灰度模式</el-button>
<el-button @click="d2adminGrayModeSet(true)">打开灰度模式</el-button>
<el-button @click="d2adminGrayModeSet(false)">关闭灰度模式</el-button>
<el-button @click="grayToggle">切换灰度模式</el-button>
<el-button @click="graySet(true)">打开灰度模式</el-button>
<el-button @click="graySet(false)">关闭灰度模式</el-button>
<el-button @click="dialogVisible = true">模拟报错提示框</el-button>
</el-button-group>
<el-dialog
@@ -47,20 +47,20 @@ export default {
}
},
computed: {
...mapState({
isGrayMode: state => state.d2admin.isGrayMode
...mapState('d2admin/gray', {
grayActive: 'active'
})
},
methods: {
...mapMutations([
'd2adminGrayModeToggle',
'd2adminGrayModeSet'
]),
...mapMutations('d2admin/gray', {
grayToggle: 'toggle',
graySet: 'set'
}),
handleDialogOpen () {
this.d2adminGrayModeSet(true)
this.graySet(true)
},
handleDialogClosed () {
this.d2adminGrayModeSet(false)
this.graySet(false)
}
}
}

View File

@@ -1,13 +1,16 @@
<template>
<d2-container type="ghost">
<template slot="header">version {{version}} [ {{releasesUpdate ? '有新版本' : '已经是最新版本'}} ]</template>
<template slot="header">version {{releasesVersion}} [ {{releasesUpdate ? '有新版本' : '已经是最新版本'}} ]</template>
<div class="d2-mt d2-mr">
<el-row :gutter="20">
<el-col :span="12">
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">顶栏菜单数据</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="menuHeader" :options="{ rootObjectKey: 'menuHeader', maxDepth: 2 }"/>
<tree-view
class="tree-view-small"
:data="menuHeader"
:options="{ rootObjectKey: 'menuHeader', maxDepth: 2 }"/>
</div>
</el-card>
</el-col>
@@ -15,7 +18,10 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">侧边栏菜单数据</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="menuAside" :options="{ rootObjectKey: 'menuAside', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="menuAside"
:options="{ rootObjectKey: 'menuAside', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
@@ -24,19 +30,19 @@
<el-col :span="8">
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">全屏模式</template>
<el-switch v-model="isFullScreen" active-text="打开" inactive-text="关闭" disabled/>
<el-switch v-model="fullscreenActive" active-text="打开" inactive-text="关闭" disabled/>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">灰度模式</template>
<el-switch v-model="isGrayMode" active-text="打开" inactive-text="关闭" disabled/>
<el-switch v-model="grayActive" active-text="打开" inactive-text="关闭" disabled/>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">侧边栏折叠</template>
<el-switch v-model="isMenuAsideCollapse" active-text="收缩" inactive-text="展开" disabled/>
<el-switch v-model="menuAsideCollapse" active-text="收缩" inactive-text="展开" disabled/>
</el-card>
</el-col>
</el-row>
@@ -53,7 +59,10 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">已经注册的主题</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="themeList" :options="{ rootObjectKey: 'themeList', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="themeList"
:options="{ rootObjectKey: 'themeList', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
@@ -61,7 +70,10 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">当前主题信息</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="d2adminThemeActiveSetting" :options="{ rootObjectKey: 'd2adminThemeActiveSetting', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="themeActiveSetting"
:options="{ rootObjectKey: 'themeActiveSetting', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
@@ -75,7 +87,10 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">支持多页显示的页面列表</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="pagePool" :options="{ rootObjectKey: 'pagePool', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="pagePool"
:options="{ rootObjectKey: 'pagePool', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
@@ -83,7 +98,10 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">打开的标签页</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="pageOpenedList" :options="{ rootObjectKey: 'pageOpenedList', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="pageopened"
:options="{ rootObjectKey: 'pageopened', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
@@ -91,14 +109,20 @@
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">缓存页面</template>
<div style="height: 300px; overflow: auto;">
<tree-view class="tree-view-small" :data="d2adminKeepAliveInclude" :options="{ rootObjectKey: 'd2adminKeepAliveInclude', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="keepAlive"
:options="{ rootObjectKey: 'keepAlive', maxDepth: 1 }"/>
</div>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" class="d2-card d2-mb">
<template slot="header">最新版本数据 [ {{releasesUpdate ? '有新版本' : '已经是最新版本'}} ]</template>
<tree-view class="tree-view-small" :data="releasesLatest" :options="{ rootObjectKey: 'releasesLatest', maxDepth: 1 }"/>
<tree-view
class="tree-view-small"
:data="releasesLatest"
:options="{ rootObjectKey: 'releasesLatest', maxDepth: 1 }"/>
</el-card>
</div>
</d2-container>
@@ -108,27 +132,33 @@
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState({
userInfo: state => state.d2admin.userInfo,
version: state => state.d2admin.version,
releasesLatest: state => state.d2admin.releasesLatest,
releasesUpdate: state => state.d2admin.releasesUpdate,
menuHeader: state => state.d2admin.menuHeader,
menuAside: state => state.d2admin.menuAside,
isFullScreen: state => state.d2admin.isFullScreen,
isGrayMode: state => state.d2admin.isGrayMode,
isMenuAsideCollapse: state => state.d2admin.isMenuAsideCollapse,
themeList: state => state.d2admin.themeList,
themeActiveName: state => state.d2admin.themeActiveName,
pagePool: state => state.d2admin.pagePool,
pageOpenedList: state => state.d2admin.pageOpenedList,
pageCurrent: state => state.d2admin.pageCurrent,
ua: state => state.d2admin.ua
...mapState('d2admin', {
// 用户信息
userInfo: state => state.user.info,
// 版本
releasesVersion: state => state.releases.version,
releasesLatest: state => state.releases.latest,
releasesUpdate: state => state.releases.update,
// 菜单
menuHeader: state => state.menu.header,
menuAside: state => state.menu.aside,
menuAsideCollapse: state => state.menu.asideCollapse,
// 主题
themeList: state => state.theme.list,
themeActiveName: state => state.theme.activeName,
// 全屏
fullscreenActive: state => state.fullscreen.active,
// 灰度模式
grayActive: state => state.gray.active,
// tag 池
pagePool: state => state.page.pool,
pageCurrent: state => state.page.current,
pageopened: state => state.page.opened
}),
...mapGetters([
'd2adminThemeActiveSetting',
'd2adminKeepAliveInclude'
])
...mapGetters('d2admin', {
keepAlive: 'page/keepAlive',
themeActiveSetting: 'theme/activeSetting'
})
}
}
</script>

View File

@@ -1,7 +1,7 @@
<template>
<d2-container type="card" class="page">
<template slot="header">主题</template>
<el-table :data="themeList" v-bind="table">
<el-table :data="list" v-bind="table">
<el-table-column prop="name" align="center" width="260"/>
<el-table-column label="预览" width="120">
<div
@@ -12,7 +12,7 @@
</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-if="activeName === 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>
@@ -25,7 +25,7 @@
</el-button>
</div>
<template slot="footer">
<el-button type="primary" size="small">当前激活主题 {{themeActiveName}}</el-button>
<el-button type="primary" size="small">当前激活主题 {{activeName}}</el-button>
</template>
</d2-container>
</template>
@@ -42,17 +42,17 @@ export default {
}
},
computed: {
...mapState({
themeList: state => state.d2admin.themeList,
themeActiveName: state => state.d2admin.themeActiveName
})
...mapState('d2admin/theme', [
'list',
'activeName'
])
},
methods: {
...mapMutations([
'd2adminThemeSet'
...mapMutations('d2admin/theme', [
'set'
]),
handleSelectTheme (name) {
this.d2adminThemeSet(name)
this.set(name)
}
}
}

View File

@@ -1,8 +1,8 @@
<template>
<d2-container>
<p class="d2-mt-0">useragent</p>
<el-input :value="ua.ua"></el-input>
<p>格式化数据 in vuex: state.d2admin.ua</p>
<el-input :value="uaData.ua"></el-input>
<p>格式化数据 in vuex: state.d2admin.ua.data</p>
<d2-highlight :code="uaStr"/>
</d2-container>
</template>
@@ -11,11 +11,11 @@
import { mapState } from 'vuex'
export default {
computed: {
...mapState({
ua: state => state.d2admin.ua
...mapState('d2admin/ua', {
uaData: 'data'
}),
uaStr () {
const { browser, engine, os, device, cpu } = this.ua
const { browser, engine, os, device, cpu } = this.uaData
return JSON.stringify({ browser, engine, os, device, cpu }, null, 2)
}
}

View File

@@ -114,8 +114,8 @@ export default {
}
},
methods: {
...mapActions([
'd2adminLogin'
...mapActions('d2admin/account', [
'login'
]),
/**
* @description 接收选择一个用户快速登陆的事件
@@ -136,7 +136,7 @@ export default {
// 登陆
// 注意 这里的演示没有传验证码
// 具体需要传递的数据请自行修改代码
this.d2adminLogin({
this.login({
vm: this,
username: this.formLogin.username,
password: this.formLogin.password

View File

@@ -5,7 +5,8 @@ export default {
install (Vue, options) {
Vue.config.errorHandler = function (err, vm, info) {
Vue.nextTick(() => {
store.commit('d2adminLogAdd', {
// 添加 log
store.dispatch('d2admin/log/add', {
type: 'error',
err,
vm,

View File

@@ -6,11 +6,16 @@ export default {
// 快速打印 log
Vue.prototype.$log = util.log
// 快速记录日志
Vue.prototype.$logAdd = function (info) {
store.commit('d2adminLogAdd', {
Vue.prototype.$logAdd = function (info, show = true) {
// store 赋值
store.dispatch('d2admin/log/add', {
type: 'log',
info
})
// 显示在控制台
if (show && process.env.NODE_ENV === 'development') {
util.log.default(info)
}
}
}
}

View File

@@ -42,7 +42,7 @@ router.afterEach(to => {
const app = router.app
const { name, params, query } = to
// 多页控制 打开新的页面
app.$store.commit('d2adminPageOpenNew', { name, params, query })
app.$store.commit('d2admin/page/open', { name, params, query })
// 更改标题
util.title(to.meta.title)
})

View File

@@ -1 +0,0 @@
efda2b963ee9b9d9fe1b30d4fdfeac80d127bb31

View File

@@ -0,0 +1,28 @@
import db from './modules/db'
import releases from './modules/releases'
import user from './modules/user'
import menu from './modules/menu'
import theme from './modules/theme'
import log from './modules/log'
import account from './modules/account'
import fullscreen from './modules/fullscreen'
import ua from './modules/ua'
import gray from './modules/gray'
import page from './modules/page'
export default {
namespaced: true,
modules: {
db,
releases,
user,
menu,
theme,
log,
account,
fullscreen,
ua,
gray,
page
}
}

View File

@@ -0,0 +1,105 @@
import util from '@/libs/util.js'
export default {
namespaced: true,
actions: {
/**
* 登陆
* @param {Object} param context
* @param {Object} param vm {Object} vue 实例
* @param {Object} param username {String} 用户账号
* @param {Object} param password {String} 密码
*/
login ({ commit }, { vm, username, password }) {
// 开始请求登录接口
vm.$axios({
method: 'post',
url: '/login',
data: {
username,
password
}
})
.then(res => {
// 设置 cookie 一定要存 uuid 和 token 两个 cookie
// 整个系统依赖这两个数据进行校验和存储
// uuid 是用户身份唯一标识 用户注册的时候确定 并且不可改变 不可重复
// token 代表用户当前登录状态 建议在网络请求中携带 token
// 如有必要 token 需要定时更新,默认保存一天
util.cookies.set('uuid', res.data.uuid)
util.cookies.set('token', res.data.token)
// 设置 vuex 用户信息
commit('d2admin/user/set', {
name: res.data.name
}, { root: true })
// 用户登陆后从数据库加载一系列的设置
commit('d2admin/account/load', null, { root: true })
// 跳转路由
vm.$router.push({
name: 'index'
})
})
.catch(err => {
console.group('登陆结果')
console.log('err: ', err)
console.groupEnd()
})
},
/**
* 注销用户并返回登陆页面
* @param {Object} param context
* @param {Object} param vm {Object} vue 实例
* @param {Object} param confirm {Boolean} 是否需要确认
*/
logout ({ commit }, { vm, confirm }) {
/**
* @description 注销
*/
function logout () {
// 删除cookie
util.cookies.remove('token')
util.cookies.remove('uuid')
// 跳转路由
vm.$router.push({
name: 'login'
})
}
// 判断是否需要确认
if (confirm) {
commit('d2admin/gray/set', true, { root: true })
vm.$confirm('注销当前账户吗? 打开的标签页和用户设置将会被保存。', '确认操作', {
confirmButtonText: '确定注销',
cancelButtonText: '放弃',
type: 'warning'
})
.then(() => {
commit('d2admin/gray/set', false, { root: true })
logout()
})
.catch(() => {
commit('d2admin/gray/set', false, { root: true })
vm.$message('放弃注销用户')
})
} else {
logout()
}
}
},
mutations: {
/**
* @class ...
* @description 用户登陆后从数据库加载一系列的设置
* @param {Object} state vuex state
*/
load (state) {
// DB -> store 加载用户名
this.commit('d2admin/user/load')
// DB -> store 加载主题
this.commit('d2admin/theme/load')
// DB -> store 数据库加载上次退出时的多页列表
this.commit('d2admin/page/openedLoad')
// DB -> store 数据库加载这个用户之前设置的侧边栏折叠状态
this.commit('d2admin/menu/asideCollapseLoad')
}
}
}

View File

@@ -0,0 +1,172 @@
import db from '@/libs/db.js'
import util from '@/libs/util.js'
/**
* @description 检查路径是否存在 不存在的话初始化
* @param {Object} param dbName {String} 数据库名称
* @param {Object} param path {String} 路径
* @param {Object} param user {Boolean} 区分用户
* @param {Object} param validator {Function} 数据校验钩子 返回 true 表示验证通过
* @param {Object} param defaultValue {*} 初始化默认值
* @returns {String} 可以直接使用的路径
*/
function pathInit ({
dbName = 'db',
path = '',
user = true,
validator = () => true,
defaultValue = ''
}) {
const uuid = util.cookies.get('uuid') || 'ghost-uuid'
const currentPath = `${dbName}.${user ? `user.${uuid}` : 'public'}${path ? `.${path}` : ''}`
const value = db.get(currentPath).value()
if (!(value && validator(value))) {
db
.set(currentPath, defaultValue)
.write()
}
return currentPath
}
export default {
namespaced: true,
mutations: {
/**
* @description 将数据存储到指定位置 | 路径不存在会自动初始化
* @description 效果类似于 dbName.path = value
* @param {Object} state vuex state
* @param {Object} param dbName {String} 数据库名称
* @param {Object} param path {String} 存储路径
* @param {Object} param value {*} 需要存储的值
*/
set (state, {
dbName = 'db',
path = '',
value = ''
}) {
db.set(pathInit({
dbName,
path,
user: false
}), value).write()
},
/**
* @description 将数据存储到指定位置 | 路径不存在会自动初始化 [区分用户]
* @description 效果类似于 dbName.path[user] = value
* @param {Object} state vuex state
* @param {Object} param dbName {String} 数据库名称
* @param {Object} param path {String} 存储路径
* @param {Object} param value {*} 需要存储的值
*/
setByUser (state, {
dbName = 'db',
path = '',
value = ''
}) {
db.set(pathInit({
dbName,
path
}), value).write()
}
},
actions: {
/**
* @description 获取存储数据库对象
*/
database () {
return new Promise(resolve => {
resolve(db.get(pathInit({
dbName: 'database',
path: '',
user: false,
defaultValue: {}
})))
})
},
/**
* @description 清空存储数据库对象
*/
databaseClear () {
return new Promise(resolve => {
resolve(db.get(pathInit({
dbName: 'database',
path: '',
user: false,
validator: () => false,
defaultValue: {}
})))
})
},
/**
* @description 获取存储数据库对象 [区分用户]
*/
databaseByUser () {
return new Promise(resolve => {
resolve(db.get(pathInit({
dbName: 'database',
path: '',
user: true,
defaultValue: {}
})))
})
},
/**
* @description 清空存储数据库对象 [区分用户]
*/
databaseByUserClear () {
return new Promise(resolve => {
resolve(db.get(pathInit({
dbName: 'database',
path: '',
user: true,
validator: () => false,
defaultValue: {}
})))
})
},
/**
* @description 获取数据
* @description 效果类似于 dbName.path || defaultValue
* @param {Object} state vuex state
* @param {Object} param dbName {String} 数据库名称
* @param {Object} param path {String} 存储路径
* @param {Object} param defaultValue {*} 取值失败的默认值
*/
get (context, {
dbName = 'db',
path = '',
defaultValue = ''
}) {
return new Promise(resolve => {
resolve(db.get(pathInit({
dbName,
path,
user: false,
defaultValue
})).value())
})
},
/**
* @description 获取数据 [区分用户]
* @description 效果类似于 dbName.path[user] || defaultValue
* @param {Object} state vuex state
* @param {Object} param dbName {String} 数据库名称
* @param {Object} param path {String} 存储路径
* @param {Object} param defaultValue {*} 取值失败的默认值
*/
getByUser (context, {
dbName = 'db',
path = '',
defaultValue = ''
}) {
return new Promise((resolve, reject) => {
resolve(db.get(pathInit({
dbName,
path,
user: true,
defaultValue
})).value())
})
}
}
}

View File

@@ -0,0 +1,31 @@
import screenfull from 'screenfull'
export default {
namespaced: true,
state: {
// 全屏激活
active: false
},
mutations: {
/**
* @description 切换全屏
*/
toggle () {
if (screenfull.isFullscreen) {
screenfull.exit()
this.commit('d2admin/fullscreen/set', false)
} else {
screenfull.request()
this.commit('d2admin/fullscreen/set', true)
}
},
/**
* @description 设置 store 里的全屏状态
* @param {Object} state vuex state
* @param {Boolean} active active
*/
set (state, active) {
state.active = active
}
}
}

View File

@@ -0,0 +1,24 @@
export default {
namespaced: true,
state: {
// 灰度
active: false
},
mutations: {
/**
* @description 切换灰度状态
* @param {Object} state vuex state
*/
toggle (state) {
state.active = !state.active
},
/**
* @description 设置灰度模式
* @param {Object} state vuex state
* @param {Boolean} value new value
*/
set (state, value) {
state.active = value
}
}
}

View File

@@ -0,0 +1,76 @@
import dayjs from 'dayjs'
import get from 'lodash.get'
import toString from 'lodash.tostring'
import util from '@/libs/util.js'
export default {
namespaced: true,
state: {
// 错误日志
list: []
},
getters: {
/**
* @description 返回现存 log (all) 的条数
* @param {*} state vuex state
*/
length (state) {
return state.list.length
},
/**
* @description 返回现存 log (error) 的条数
* @param {*} state vuex state
*/
lengthError (state) {
return state.list.filter(l => l.type === 'error').length
}
},
mutations: {
/**
* @description 清空日志
* @param {Object} state vuex state
*/
clean (state) {
// store 赋值
state.list = []
}
},
actions: {
/**
* @description 添加一个日志
* @param {Object} state vuex state
* @param {Object} param type {String} 类型
* @param {Object} param err {Error} 错误对象
* @param {Object} param vm {Object} vue 实例
* @param {Object} param info {String} 信息
*/
add ({ state, rootState }, { type, err, vm, info }) {
// store 赋值
state.list.push(Object.assign({
// 记录类型
type: 'log', // or error
// 信息
info: '',
// 错误对象
err: '',
// vue 实例
vm: '',
// 当前用户信息
user: rootState.d2admin.user.info,
// 当前用户的 uuid
uuid: util.cookies.get('uuid'),
// 当前的 token
token: util.cookies.get('token'),
// 当前地址
url: get(window, 'location.href', ''),
// 当前时间
time: dayjs().format('YYYY-M-D HH:mm:ss')
}, {
type,
err,
vm,
info: toString(info)
}))
}
}
}

View File

@@ -0,0 +1,70 @@
export default {
namespaced: true,
state: {
// 顶栏菜单
header: [],
// 侧栏菜单
aside: [],
// 侧边栏收缩
asideCollapse: false
},
mutations: {
/**
* @param {Object} state vuex state
* @param {Array} menu menu setting
*/
headerSet (state, menu) {
// store 赋值
state.header = menu
},
/**
* @param {Object} state vuex state
* @param {Array} menu menu setting
*/
asideSet (state, menu) {
// store 赋值
state.aside = menu
},
/**
* 设置侧边栏展开或者收缩
* @param {Object} state vuex state
* @param {Boolean} collapse is collapse
*/
asideCollapseSet (state, collapse) {
// store 赋值
state.asideCollapse = collapse
// 持久化
this.commit('d2admin/db/setByUser', {
dbName: 'sys',
path: 'menu.asideCollapse',
value: state.asideCollapse
})
},
/**
* 切换侧边栏展开和收缩
* @param {Object} state vuex state
*/
asideCollapseToggle (state) {
// store 赋值
state.asideCollapse = !state.asideCollapse
// 持久化
this.commit('d2admin/db/setByUser', {
dbName: 'sys',
path: 'menu.asideCollapse',
value: state.asideCollapse
})
},
/**
* 从数据库读取侧边栏展开或者收缩
* @param {Object} state vuex state
*/
async asideCollapseLoad (state) {
// store 赋值
state.asideCollapse = await this.dispatch('d2admin/db/getByUser', {
dbName: 'sys',
path: 'menu.asideCollapse',
defaultValue: false
})
}
}
}

View File

@@ -0,0 +1,309 @@
const openedDefult = {
name: 'index',
meta: {
title: '首页',
requiresAuth: false
}
}
export default {
namespaced: true,
state: {
// 可以在多页 tab 模式下显示的页面
pool: [],
// 当前显示的多页面列表
opened: [
openedDefult
],
// 当前页面
current: ''
},
getters: {
/**
* @description 从当前所有打开的多标签页里返回需要缓存的页面 name
* @param {*} state vuex state
*/
keepAlive (state) {
return state.opened.filter(item => {
if (item.meta) {
if (item.meta.notCache) {
return false
}
}
return true
}).map(e => e.name)
}
},
mutations: {
/**
* @class current
* @description 打开一个新的页面
* @param {Object} state vuex state
* @param {Object} param { name, params, query } 路由信息
*/
open (state, { name, params, query }) {
// 已经打开的页面
let opened = state.opened
// 判断此页面是否已经打开 并且记录位置
let pageOpendIndex = 0
const pageOpend = opened.find((page, index) => {
const same = page.name === name
pageOpendIndex = same ? index : pageOpendIndex
return same
})
if (pageOpend) {
// 页面以前打开过 但是新的页面可能 name 一样,参数不一样
this.commit('d2admin/page/openedUpdate', {
index: pageOpendIndex,
params,
query
})
} else {
// 页面以前没有打开过
let page = state.pool.find(t => t.name === name)
if (page) {
this.commit('d2admin/page/add', {
tag: page,
params,
query
})
}
}
this.commit('d2admin/page/currentSet', name)
},
/**
* @class current
* @description 设置当前激活的页面 name
* @param {Object} state vuex state
* @param {String} name new name
*/
currentSet (state, name) {
state.current = name
},
/**
* @class opened
* @description 更新页面列表上的某一项
* @param {Object} state vuex state
* @param {Object} param { index, params, query } 路由信息
*/
openedUpdate (state, { index, params, query }) {
// 更新页面列表某一项
let page = state.opened[index]
page.params = params || page.params
page.query = query || page.query
state.opened.splice(index, 1, page)
// 持久化
this.commit('d2admin/page/opend2db')
},
/**
* 将 opened 属性赋值并持久化 在这之前请先确保已经更新了 state.opened
* @param {Object} state vuex state
*/
opend2db (state) {
this.commit('d2admin/db/setByUser', {
dbName: 'sys',
path: 'page.opened',
value: state.opened
})
},
/**
* @class opened
* @description 从数据库载入分页列表
* @param {Object} state vuex state
*/
async openedLoad (state) {
// store 赋值
const value = await this.dispatch('d2admin/db/getByUser', {
dbName: 'sys',
path: 'page.opened',
defaultValue: [
openedDefult
]
})
// 在处理函数中进行数据优化 过滤掉现在已经失效的页签或者已经改变了信息的页签
// 以 name 字段为准
// 如果页面过多的话可能需要优化算法
// valid 有效列表 1, 1, 0, 1 => 有效, 有效, 失效, 有效
const valid = []
// 处理数据
state.opened = value.map(opened => {
// 忽略首页
if (opened.name === 'index') {
valid.push(1)
return opened
}
// 尝试在所有的支持多标签页的页面里找到 name 匹配的页面
const find = state.pool.find(item => item.name === opened.name)
// 记录有效或无效信息
valid.push(find ? 1 : 0)
// 返回合并后的数据 新的覆盖旧的
// 新的数据中一般不会携带 params 和 query, 所以旧的参数会留存
return Object.assign({}, opened, find)
}).filter((opened, index) => valid[index] === 1)
},
/**
* @class opened
* @description 新增一个 tag (打开一个页面)
* @param {Object} state vuex state
* @param {Object} param new tag info
*/
add (state, { tag, params, query }) {
// 设置新的 tag 在新打开一个以前没打开过的页面时使用
let newTag = tag
newTag.params = params || newTag.params
newTag.query = query || newTag.query
// 添加进当前显示的页面数组
state.opened.push(newTag)
// 持久化
this.commit('d2admin/page/opend2db')
},
/**
* @class opened
* @description 关闭一个 tag (关闭一个页面)
* @param {Object} state vuex state
* @param {Object} param { tagName: 要关闭的标签名字, vm: vue }
*/
close (state, { tagName, vm }) {
// 下个新的页面
let newPage = state.opened[0]
const isCurrent = state.current === tagName
// 如果关闭的页面就是当前显示的页面
if (isCurrent) {
// 去找一个新的页面
let len = state.opened.length
for (let i = 1; i < len; i++) {
if (state.opened[i].name === tagName) {
if (i < len - 1) {
newPage = state.opened[i + 1]
} else {
newPage = state.opened[i - 1]
}
break
}
}
}
// 找到这个页面在已经打开的数据里是第几个
const index = state.opened.findIndex(page => page.name === tagName)
if (index >= 0) {
state.opened.splice(index, 1)
}
// 持久化
this.commit('d2admin/page/opend2db')
// 最后需要判断是否需要跳到首页
if (isCurrent) {
const { name = '', params = {}, query = {} } = newPage
let routerObj = {
name,
params,
query
}
vm.$router.push(routerObj)
}
},
/**
* @class opened
* @description 关闭当前标签左边的标签
* @param {Object} state vuex state
* @param {Object} param { pageSelect: 当前选中的tagName, vm: vue }
*/
closeLeft (state, { pageSelect, vm } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.name === pageAim) {
currentIndex = index
}
})
if (currentIndex > 0) {
state.opened.splice(1, currentIndex - 1)
}
state.current = pageAim
if (vm && vm.$route.name !== pageAim) {
vm.$router.push({
name: pageAim
})
}
// 持久化
this.commit('d2admin/page/opend2db')
},
/**
* @class opened
* @description 关闭当前标签右边的标签
* @param {Object} state vuex state
* @param {Object} param { pageSelect: 当前选中的tagName, vm: vue }
*/
closeRight (state, { pageSelect, vm } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.name === pageAim) {
currentIndex = index
}
})
state.opened.splice(currentIndex + 1)
state.current = pageAim
if (vm && vm.$route.name !== pageAim) {
vm.$router.push({
name: pageAim
})
}
// 持久化
this.commit('d2admin/page/opend2db')
},
/**
* @class opened
* @description 关闭当前激活之外的 tag
* @param {Object} state vuex state
* @param {Object} param { pageSelect: 当前选中的tagName, vm: vue }
*/
closeOther (state, { pageSelect, vm } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.name === pageAim) {
currentIndex = index
}
})
if (currentIndex === 0) {
state.opened.splice(1)
} else {
state.opened.splice(currentIndex + 1)
state.opened.splice(1, currentIndex - 1)
}
state.current = pageAim
if (vm && vm.$route.name !== pageAim) {
vm.$router.push({
name: pageAim
})
}
// 持久化
this.commit('d2admin/page/opend2db')
},
/**
* @class opened
* @description 关闭所有 tag
* @param {Object} state vuex state
* @param {Object} vm vue
*/
closeAll (state, vm) {
state.opened.splice(1)
// 持久化
this.commit('d2admin/page/opend2db')
// 关闭所有的标签页后需要判断一次现在是不是在首页
if (vm.$route.name !== 'index') {
vm.$router.push({
name: 'index'
})
}
},
/**
* @class pool
* @description 保存 pool (候选池)
* @param {Object} state vuex state
* @param {Array} pool pages
*/
poolSet (state, pool) {
state.pool = pool
}
}
}

View File

@@ -0,0 +1,33 @@
import { version } from '../../../../../package'
export default {
namespaced: true,
state: {
// D2Admin 版本
version,
// 最新版本的信息
latest: {},
// 有新版本
update: false
},
mutations: {
/**
* @description 设置是否有新的 D2Admin 版本
* @param {Object} state vuex state
* @param {Boolean} update can update
*/
updateSet (state, update) {
// store 赋值
state.update = update
},
/**
* @description 设置最新版本的信息
* @param {Object} state vuex state
* @param {Object}} latest releases value
*/
latestSet (state, latest) {
// store 赋值
state.latest = latest
}
}
}

View File

@@ -0,0 +1,60 @@
import list from '@/assets/style/theme/list.js'
export default {
namespaced: true,
state: {
// 主题
list,
// 现在激活的主题 这应该是一个名字 不是对象
activeName: list[0].name
},
getters: {
/**
* @description 返回当前的主题信息 不是一个名字 而是当前激活主题的所有数据
* @param {Object} state vuex state
*/
activeSetting (state) {
return state.list.find(theme => theme.name === state.activeName)
}
},
mutations: {
/**
* @description 激活一个主题应用到dom上
* @param {Object} state vuex state
* @param {String} themeValue 需要激活的主题名称
*/
set (state, themeName) {
// 检查这个主题在主题列表里是否存在
state.activeName = state.list.find(e => e.name === themeName) ? themeName : state.list[0].name
// 将 vuex 中的主题应用到 dom
this.commit('d2admin/theme/dom')
// 持久化
this.commit('d2admin/db/setByUser', {
dbName: 'sys',
path: 'theme.activeName',
value: state.activeName
})
},
/**
* @description 从数据库加载主题设置
* @param {Object} state vuex state
*/
async load (state) {
// store 赋值
state.activeName = await this.dispatch('d2admin/db/getByUser', {
dbName: 'sys',
path: 'theme.activeName',
defaultValue: state.list[0].name
})
// 更新到页面
this.commit('d2admin/theme/dom')
},
/**
* @description 将 vuex 中的主题应用到 dom
* @param {Object} state vuex state
*/
dom (state) {
document.body.className = `theme-${state.activeName}`
}
}
}

View File

@@ -0,0 +1,18 @@
import UaParser from 'ua-parser-js'
export default {
namespaced: true,
state: {
// 用户 UA
data: {}
},
mutations: {
/**
* @description 记录 UA
* @param {Object} state vuex state
*/
get (state) {
state.data = new UaParser().getResult()
}
}
}

View File

@@ -0,0 +1,40 @@
export default {
namespaced: true,
state: {
// 用户信息
info: {
name: ''
}
},
mutations: {
/**
* @description 设置用户数据
* @param {Object} state vuex state
* @param {*} info info
*/
set (state, info) {
// store 赋值
state.info = info
// 持久化
this.commit('d2admin/db/setByUser', {
dbName: 'sys',
path: 'user.info',
value: info
})
},
/**
* @description 从数据库取用户数据
* @param {Object} state vuex state
*/
async load (state) {
// store 赋值
state.info = await this.dispatch('d2admin/db/getByUser', {
dbName: 'sys',
path: 'user.info',
defaultValue: {
name: 'Ghost'
}
})
}
}
}