Commit a007f580 authored by 张宗旭's avatar 张宗旭

提交

parents
> 1%
last 2 versions
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
# 所有环境默认
# 页面 title 前缀
VUE_APP_TITLE=电动车后台管理
# 网络请求公用地址
# VUE_APP_API=/api/
VUE_APP_API=
# 仓库地址
VUE_APP_REPO=https://github.com/d2-projects/d2-admin-start-kit
# 国际化配置
VUE_APP_I18N_LOCALE=zh-chs
VUE_APP_I18N_FALLBACK_LOCALE=en
# element 颜色
VUE_APP_ELEMENT_COLOR=#409EFF
# 开发环境
# 页面 title 前缀
VUE_APP_TITLE=电动车后台管理
# 构建预览页面
# 指定构建模式
NODE_ENV=production
# 标记当前构建方式
VUE_APP_BUILD_MODE=PREVIEW
# 显示源码按钮
VUE_APP_SCOURCE_LINK=TRUE
# 部署路径
VUE_APP_PUBLIC_PATH=/
# 忽略目录
build/
tests/
node_modules/
# D2CRUD 演示
src/views/demo/d2-crud/
# node 覆盖率文件
coverage/
# 忽略文件
**/*-min.js
**/*.min.js
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'@vue/standard'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
},
parserOptions: {
parser: 'babel-eslint'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
module.exports = {
plugins: {
autoprefixer: {}
}
}
MIT License
Copyright (c) 2018 李杨
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[D2Admin](https://github.com/d2-projects/d2-admin) is a fully open source and free enterprise back-end product front-end integration solution, using the latest front-end technology stack, javascript files loading of local first screen less than 60kb, has prepared most of the project preparations, and with a lot of sample code to help the management system agile development.
[中文](https://github.com/d2-projects/d2-admin-start-kit/blob/master/README.zh.md) | **English**
## Preview
![Deploy preview](https://github.com/d2-projects/d2-admin-start-kit/workflows/Deploy%20preview/badge.svg)
[![Netlify Status](https://api.netlify.com/api/v1/badges/08ff8c93-f0a8-497a-a081-440b31fb3aa4/deploy-status)](https://app.netlify.com/sites/d2-admin-start-kit/deploys)
The following access addresses are built and deployed by the latest master branch code at the same time. The access effect is completely consistent. Please select the appropriate access link according to your own network situation.
| server | link | server |
| --- | --- | --- |
| d2.pub | [d2.pub/d2-admin-start-kit/preview](https://d2.pub/d2-admin-start-kit/preview) | China server |
| cdn.d2.pub | [cdn.d2.pub/d2-admin-start-kit/preview](https://cdn.d2.pub/d2-admin-start-kit/preview) | qiniu CDN |
| github | [d2-projects.github.io/d2-admin-start-kit](https://d2-projects.github.io/d2-admin-start-kit) | GitHub pages |
| netlify | [d2-admin-start-kit.netlify.com](https://d2-admin-start-kit.netlify.com) | Netlify CDN |
## Other synchronous repositories
| type | link |
| --- | --- |
| gitee | [https://gitee.com/d2-projects/d2-admin](https://gitee.com/d2-projects/d2-admin) |
| coding | [https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git](https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git) |
\ No newline at end of file
[D2Admin](https://github.com/d2-projects/d2-admin) 是一个完全 **开源免费** 的企业中后台产品前端集成方案,使用最新的前端技术栈,小于 60kb 的本地首屏 js 加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助力管理系统敏捷开发。
**中文** | [English](https://github.com/d2-projects/d2-admin-start-kit)
## 预览
![Deploy preview](https://github.com/d2-projects/d2-admin-start-kit/workflows/Deploy%20preview/badge.svg)
[![Netlify Status](https://api.netlify.com/api/v1/badges/08ff8c93-f0a8-497a-a081-440b31fb3aa4/deploy-status)](https://app.netlify.com/sites/d2-admin-start-kit/deploys)
下列访问地址均由最新的 master 分支代码同时构建部署,访问效果完全一致,请根据自身网络情况选择合适的访问链接。
| 位置 | 链接 | 部署位置 |
| --- | --- | --- |
| d2.pub | [preview](https://d2.pub/d2-admin-start-kit/preview) | 中国服务器 |
| cdn.d2.pub | [preview](https://cdn.d2.pub/d2-admin-start-kit/preview) | 七牛云 CDN |
| github | [preview](https://d2-projects.github.io/d2-admin-start-kit) | GitHub pages |
| netlify | [preview](https://d2-admin-start-kit.netlify.com) | Netlify CDN |
## 其它同步仓库
| 位置 | 链接 |
| --- | --- |
| 码云 | [https://gitee.com/d2-projects/d2-admin-start-kit](https://gitee.com/d2-projects/d2-admin-start-kit) |
| coding | [https://d2-projects.coding.net/p/d2-projects/d/d2-admin-start-kit/git](https://d2-projects.coding.net/p/d2-projects/d/d2-admin-start-kit/git) |
> 如果您在 github 仓库下载很慢,可以尝试使用我们的码云仓库克隆代码
\ No newline at end of file
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
// 允许两种编码引入方式共存,也就是 common 规范与 es6 规范的共存处理
sourceType: 'unambiguous'
}
<babeledit_project version="1.2">
<!--
BabelEdit project file
https://www.codeandweb.com/babeledit
This file contains meta data for all translations, but not the translation texts itself.
They are stored in framework-specific message files (.json / .vue / .yaml / .properties)
-->
<preset_collections/>
<framework>vue-json</framework>
<filename>d2-admin.babel</filename>
<source_root_dir></source_root_dir>
<folder_node>
<name></name>
<children>
<concept_node>
<name>_element</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>ja-JP</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHS</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHT</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>_name</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>ja-JP</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHS</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHT</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node>
<name>page</name>
<children>
<folder_node>
<name>demo</name>
<children>
<folder_node>
<name>playground</name>
<children>
<folder_node>
<name>locales</name>
<children>
<concept_node>
<name>text</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>ja-JP</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHS</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CHT</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children>
</folder_node>
</children>
</folder_node>
</children>
</folder_node>
</children>
</folder_node>
<isTemplateProject>false</isTemplateProject>
<languages>
<language>
<code>en-US</code>
<source_id></source_id>
<source_file>src/locales/en.json</source_file>
</language>
<language>
<code>ja-JP</code>
<source_id></source_id>
<source_file>src/locales/ja.json</source_file>
</language>
<language>
<code>zh-CHS</code>
<source_id></source_id>
<source_file>src/locales/zh-chs.json</source_file>
</language>
<language>
<code>zh-CHT</code>
<source_id></source_id>
<source_file>src/locales/zh-cht.json</source_file>
</language>
</languages>
<translation_files>
<translation_file>
<file>src/locales/en.json</file>
</translation_file>
<translation_file>
<file>src/locales/ja.json</file>
</translation_file>
<translation_file>
<file>src/locales/zh-chs.json</file>
</translation_file>
<translation_file>
<file>src/locales/zh-cht.json</file>
</translation_file>
</translation_files>
<editor_configuration>
<save_empty_translations>true</save_empty_translations>
<copy_templates>
<copy_template>$t('%1')</copy_template>
<copy_template>{{ $t('%1') }}</copy_template>
<copy_template>this.$t('%1')</copy_template>
</copy_templates>
</editor_configuration>
<primary_language>zh-CHS</primary_language>
<configuration>
<indent>tab</indent>
<format>namespaced-json</format>
</configuration>
</babeledit_project>
// If you want to re enable these configurations, please make sure that the version number of each package is the latest
module.exports = [
// { name: 'vue', library: 'Vue', js: 'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js', css: '' },
// { name: 'vue-i18n', library: 'VueI18n', js: 'https://cdn.jsdelivr.net/npm/vue-i18n@8.15.1/dist/vue-i18n.min.js', css: '' },
// { name: 'vue-router', library: 'VueRouter', js: 'https://cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js', css: '' },
// { name: 'vuex', library: 'Vuex', js: 'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js', css: '' },
// { name: 'axios', library: 'axios', js: 'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js', css: '' },
// { name: 'better-scroll', library: 'BScroll', js: 'https://cdn.jsdelivr.net/npm/better-scroll@1.15.2/dist/bscroll.min.js', css: '' },
// { name: 'axios-mock-adapter', library: 'AxiosMockAdapter', js: 'https://cdn.jsdelivr.net/npm/axios-mock-adapter@1.18.1/dist/axios-mock-adapter.min.js', css: '' },
// { name: 'element-ui', library: 'ELEMENT', js: 'https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/index.js', css: 'https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/theme-chalk/index.css' },
// { name: 'lodash', library: '_', js: 'https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js', css: '' },
// { name: 'ua-parser-js', library: 'UAParser', js: 'https://cdn.jsdelivr.net/npm/ua-parser-js@0.7.20/dist/ua-parser.min.js', css: '' },
// { name: 'js-cookie', library: 'Cookies', js: 'https://cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js', css: '' },
// { name: 'nprogress', library: 'NProgress', js: 'https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js', css: 'https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.css' },
// { name: 'dayjs', library: 'dayjs', js: 'https://cdn.jsdelivr.net/npm/dayjs@1.8.17/dayjs.min.js', css: '' },
// { name: 'fuse.js', library: 'Fuse', js: 'https://cdn.jsdelivr.net/npm/fuse.js@5.2.3/dist/fuse.min.js', css: '' },
// { name: 'hotkeys-js', library: 'hotkeys', js: 'https://cdn.jsdelivr.net/npm/hotkeys-js@3.7.3/dist/hotkeys.min.js', css: '' },
// { name: 'qs', library: 'Qs', js: 'https://cdn.jsdelivr.net/npm/qs@6.9.1/dist/qs.js', css: '' },
// { name: 'lowdb', library: 'low', js: 'https://cdn.jsdelivr.net/npm/lowdb@1.0.0/dist/low.min.js', css: '' },
// { name: 'lowdb/adapters/LocalStorage', library: 'LocalStorage', js: 'https://cdn.jsdelivr.net/npm/lowdb@1.0.0/dist/LocalStorage.min.js', css: '' },
// { name: 'screenfull', library: 'screenfull', js: 'https://cdn.jsdelivr.net/npm/screenfull@5.0.2/dist/screenfull.min.js', css: '' },
// { name: 'sortablejs', library: 'Sortable', js: 'https://cdn.jsdelivr.net/npm/sortablejs@1.10.1/Sortable.min.js', css: '' }
]
module.exports = {
preset: '@vue/cli-plugin-unit-jest'
}
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": false,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "d2-admin",
"version": "1.20.1",
"scripts": {
"serve": "vue-cli-service serve --open",
"start": "npm run serve",
"dev": "npm run serve",
"build": "vue-cli-service build",
"build:preview": "NODE_OPTIONS=--max_old_space_size=4096 vue-cli-service build --mode preview",
"lint": "vue-cli-service lint --fix",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"axios": "^0.19.0",
"axios-mock-adapter": "^1.18.1",
"better-scroll": "^1.15.2",
"core-js": "^3.4.3",
"dayjs": "^1.8.17",
"element-ui": "^2.15.6",
"faker": "^4.1.0",
"flex.css": "^1.1.7",
"fuse.js": "^5.2.3",
"hotkeys-js": "^3.7.3",
"js-cookie": "^2.2.1",
"lodash": "^4.17.19",
"lowdb": "^1.0.0",
"nprogress": "^0.2.0",
"screenfull": "^5.0.2",
"sortablejs": "^1.10.1",
"ua-parser-js": "^0.7.20",
"vue": "^2.6.11",
"vue-i18n": "^8.15.1",
"vue-router": "^3.1.3",
"vuex": "^3.1.2"
},
"devDependencies": {
"@d2-projects/vue-filename-injector": "^1.1.0",
"@kazupon/vue-i18n-loader": "^0.5.0",
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-plugin-unit-jest": "^4.1.0",
"@vue/cli-plugin-vuex": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/test-utils": "^1.0.2",
"babel-eslint": "^10.0.3",
"compression-webpack-plugin": "^3.0.1",
"cz-conventional-changelog": "^3.2.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.23.7",
"sass-loader": "^8.0.0",
"svg-sprite-loader": "^4.1.6",
"text-loader": "^0.0.1",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-template-compiler": "^2.6.10",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-theme-color-replacer": "^1.3.3"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"repository": {
"type": "git",
"url": "https://github.com/d2-projects/d2-admin.git"
}
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white">
<path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/>
<path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z">
<animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" />
</path>
</svg>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>icon.ico.jpg">
<!-- 使用 CDN 加速的 CSS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
<!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<% } %>
<title><%= VUE_APP_TITLE %></title>
<style>
html, body, #app { height: 100%; margin: 0px; padding: 0px; width: 100%; }
.d2-home { background-color: #303133; height: 100%; display: flex; flex-direction: column; }
.d2-home__main { user-select: none; width: 100%; flex-grow: 1; display: flex; justify-content: center; align-items: center; flex-direction: column; }
.d2-home__footer { width: 100%; flex-grow: 0; text-align: center; padding: 1em 0; }
.d2-home__footer > a { font-size: 12px; color: #ABABAB; text-decoration: none; }
.d2-home__loading { height: 32px; width: 32px; margin-bottom: 20px; }
</style>
<script>
var _hmt = _hmt || [];
var hmid = "bc38887aa5588add05a38704342ad7e8";
(function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?" + hmid; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);})();
</script>
</head>
<body>
<noscript>
<strong>
Sorry, D2Admin will not work properly without JavaScript support. Enable JavaScript for browsers and continue.
</strong>
</noscript>
<div id="app">
<div class="d2-home">
<div class="d2-home__main">
<img
class="d2-home__loading"
src="./image/loading/loading-spin.svg"
alt="loading">
</div>
<div class="d2-home__footer">
<a
href="https://github.com/d2-projects/d2-admin"
target="_blank">
https://github.com/d2-projects/d2-admin
</a>
</div>
</div>
</div>
<!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
import util from '@/libs/util'
export default {
name: 'app',
watch: {
'$i18n.locale': 'i18nHandle'
},
created () {
this.i18nHandle(this.$i18n.locale)
},
methods: {
i18nHandle (val, oldVal) {
util.cookies.set('lang', val)
document.querySelector('html').setAttribute('lang', val)
}
}
}
</script>
<style lang="scss">
@import '~@/assets/style/public-class.scss';
</style>
import { assign, map } from 'lodash'
import faker from 'faker/locale/zh_CN'
import { service, request, serviceForMock, requestForMock, mock } from './service'
import * as tools from './tools'
const files = require.context('./modules', true, /\.api\.js$/)
const generators = files.keys().map(key => files(key).default)
export default assign({}, ...map(generators, generator => generator({
service,
request,
serviceForMock,
requestForMock,
mock,
faker,
tools
})))
export default ({
service,
request,
serviceForMock,
requestForMock,
mock,
faker,
tools
}) => ({
GET(url, params) {
return request({
url: url,
method: 'GET',
params
})
},
POST(url, data) {
return request({
url: url,
method: 'POST',
data
})
},
PUT(url, params) {
return request({
url: url,
method: 'PUT',
params
})
}
})
import { find, assign } from 'lodash'
const users = [
{ username: 'admin', password: 'admin', uuid: 'admin-uuid', name: 'Admin' },
{ username: 'editor', password: 'editor', uuid: 'editor-uuid', name: 'Editor' },
{ username: 'user1', password: 'user1', uuid: 'user1-uuid', name: 'User1' }
]
export default ({ service, request, serviceForMock, requestForMock, mock, faker, tools }) => ({
/**
* @description 登录
* @param {Object} data 登录携带的信息
*/
SYS_USER_LOGIN (data = {}) {
// 模拟数据
// mock
// .onAny('/login')
// .reply(config => {
// const user = find(users, tools.parse(config.data))
// return user
// ? tools.responseSuccess(assign({}, user, { token: faker.random.uuid() }))
// : tools.responseError({}, '账号或密码不正确')
// })
// 接口请求
return request({
url: '/api/login',
method: 'post',
data: data
})
}
})
import axios from 'axios'
import Adapter from 'axios-mock-adapter'
import { get } from 'lodash'
import util from '@/libs/util'
import store from '@/store/index'
import { errorLog, errorCreate } from './tools'
/**
* @description 创建请求实例
*/
function createService () {
// 创建一个 axios 实例
const service = axios.create()
// 请求拦截
service.interceptors.request.use(
config => config,
error => {
// 发送失败
console.log(error)
return Promise.reject(error)
}
)
// 响应拦截
service.interceptors.response.use(
response => {
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data
// 这个状态码是和后端约定的
const { code } = dataAxios
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
return dataAxios
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
case 0:
// [ 示例 ] code === 0 代表没有错误
// util.cookies.set('code1', code)
// return dataAxios.data
errorCreate(` ${dataAxios.msg}`)
break
case 1:
// [ 示例 ] code === 0 代表没有错误
util.cookies.set('code1', code)
return dataAxios.data
case 'xxx':
// [ 示例 ] 其它和后台约定的 code
errorCreate(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
break
default:
// 不是正确的 code
errorCreate(`${dataAxios.msg}: ${response.config.url}`)
break
}
}
},
error => {
const status = get(error, 'response.status')
switch (status) {
case 400: error.message = '请求错误'; break
// case 401: error.message = '未授权,请登录'; break
case 401:
store.dispatch('d2admin/account/logout', {
confirm: false
})
error.message = '未授权,请登录'; break
case 403: error.message = '拒绝访问'; break
case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
case 408: error.message = '请求超时'; break
case 500: error.message = '服务器内部错误'; break
case 501: error.message = '服务未实现'; break
case 502: error.message = '网关错误'; break
case 503: error.message = '服务不可用'; break
case 504: error.message = '网关超时'; break
case 505: error.message = 'HTTP版本不受支持'; break
default: break
}
errorLog(error)
return Promise.reject(error)
}
)
return service
}
/**
* @description 创建请求方法
* @param {Object} service axios 实例
*/
function createRequestFunction (service) {
return function (config) {
const token = util.cookies.get('token')
// console.log(token, '请求的token')
const configDefault = {
headers: {
token: token || '',
'Access-Control-Allow-Origin': '*',
'Content-Type': config.method === 'GET' ? 'application/x-www-form-urlencoded' : 'application/json;charset=utf-8'
},
timeout: 5000,
baseURL: process.env.VUE_APP_API,
data: {}
}
return service(Object.assign(configDefault, config))
}
}
// 用于真实网络请求的实例和请求方法
export const service = createService()
export const request = createRequestFunction(service)
// 用于模拟网络请求的实例和请求方法
export const serviceForMock = createService()
export const requestForMock = createRequestFunction(serviceForMock)
// 网络请求数据模拟工具
export const mock = new Adapter(serviceForMock)
import { Message } from 'element-ui'
import store from '@/store'
import util from '@/libs/util'
/**
* @description 安全地解析 json 字符串
* @param {String} jsonString 需要解析的 json 字符串
* @param {String} defaultValue 默认值
*/
export function parse (jsonString = '{}', defaultValue = {}) {
let result = defaultValue
try {
result = JSON.parse(jsonString)
} catch (error) {
console.log(error)
}
return result
}
/**
* @description 接口请求返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
* @param {Number} code 状态码
*/
export function response (data = {}, msg = '', code = 0) {
return [
200,
{ code, msg, data }
]
}
/**
* @description 接口请求返回 正确返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
*/
export function responseSuccess (data = {}, msg = '成功') {
return response(data, msg)
}
/**
* @description 接口请求返回 错误返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
* @param {Number} code 状态码
*/
export function responseError (data = {}, msg = '请求失败', code = 500) {
return response(data, msg, code)
}
/**
* @description 记录和显示错误
* @param {Error} error 错误对象
*/
export function errorLog (error) {
// 添加到日志
store.dispatch('d2admin/log/push', {
message: '数据请求异常',
type: 'danger',
meta: {
error
}
})
// 打印到控制台
if (process.env.NODE_ENV === 'development') {
util.log.danger('>>>>>> Error >>>>>>')
console.log(error)
}
// 显示提示
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
}
/**
* @description 创建一个错误
* @param {String} msg 错误信息
*/
export function errorCreate (msg) {
const error = new Error(msg)
errorLog(error)
throw error
}
/* 列表 */
.filter-box {
display: flex;
flex-wrap: wrap;
margin-bottom: 16px;
padding: 24px 0 16px 8px;
background: #f2f3f5;
.filter-item {
display: flex;
align-items: center;
margin-bottom: 20px;
width: 350px;
margin-right: 24px;
margin-bottom: 16px;
/* .el-input__inner{
width: 180px;
} */
.line {
height: 1px;
border: none;
width: 30px;
margin: 0 5px;
border-top: 1px solid #555555;
}
.spana {
width: 120px;
flex-shrink: 0;
font-size: 15px;
/* text-align: right; */
}
}
.filter-items {
display: flex;
align-items: center;
// margin-bottom: 20px;
// margin-right: 24px;
margin-bottom: 16px;
width: 500px;
}
.filter-picker {
min-width: 280px;
span {
text-align: left !important;
}
}
.filter-btn {
width: 100%;
}
.filter-picker {
display: flex;
width: 460px;
align-items: center;
margin-bottom: 20px;
span {
width: 100px;
text-align: right;
}
}
}
.el-range-editor.el-input__inner {
width: 350px;
}
.pagination {
margin-top: 20px;
@extend %flex-center-row;
}
// 底部保存取消 按钮
.dialog-footer {
margin-left: 10%;
}
::v-deep .limit-input .el-input__inner {
padding: 0 50px 0 15px;
}
// 添加 表单样式
.addHouser-container {
width: 90%;
margin: 1%;
padding-bottom: 20px;
.filter-item-same-width {
width: 350px;
}
.filter-item-title {
/* color: rgb(136, 134, 134); */
color: red;
margin-left: 15px;
}
}
.zhu {
margin-top: 5px;
color: red;
}
// 过渡动画 横向渐变
.fade-transverse-leave-active,
.fade-transverse-enter-active {
transition: all .5s;
}
.fade-transverse-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transverse-leave-to {
opacity: 0;
transform: translateX(30px);
}
// 过渡动画 缩放渐变
.fade-scale-leave-active,
.fade-scale-enter-active {
transition: all .3s;
}
.fade-scale-enter {
opacity: 0;
transform: scale(1.2);
}
.fade-scale-leave-to {
opacity: 0;
transform: scale(0.8);
}
// 优化显示
html, body {
margin: 0px;
height: 100%;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
#app {
@extend %full;
a {
text-decoration: none;
}
}
}
\ No newline at end of file
// element 样式补丁
.el-card {
&.is-always-shadow {
box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
}
&.is-hover-shadow {
&:hover {
box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
}
}
}
.el-menu--horizontal {
border-bottom: none !important;
}
.el-tabs__item:focus.is-active.is-focus:not(:active) {
box-shadow: none !important;
}
// 修复IE宽度不能撑满
.el-table__body,
.el-table__header {
width: 100% !important;
}
// Chrome下表格头部错位修复
.el-table th.gutter,
.el-table colgroup.gutter {
display: table-cell !important;
}
// markdown 样式补丁
.markdown-body {
ul {
list-style: disc;
}
h1, h2 {
border-bottom: none;
}
}
\ No newline at end of file
#nprogress {
.bar {
background: $color-primary !important;
}
.peg {
box-shadow: 0 0 10px $color-primary, 0 0 5px $color-primary !important;
}
}
\ No newline at end of file
.tree-view-wrapper.tree-view-small {
.tree-view-item {
font-size: 10px;
}
}
\ No newline at end of file
// vue-splitpane 样式补丁
.vue-grid-item {
&.vue-grid-placeholder {
border: 1px solid $color-border-1;
background-color: rgba(#FFF, .3);
opacity: 1;
border-radius: 4px;
}
}
\ No newline at end of file
// vue-splitpane 样式补丁
.splitter-pane-resizer {
background-color: $color-border-1 !important;
opacity: 1 !important;
}
\ No newline at end of file
@import 'public';
// 补丁 base
@import '~@/assets/style/fixed/base.scss';
// 补丁 element
@import '~@/assets/style/fixed/element.scss';
// 补丁 markdown
@import '~@/assets/style/fixed/markdown.scss';
// 补丁 n-progress
@import '~@/assets/style/fixed/n-progress.scss';
// 补丁 vue-splitpane
@import '~@/assets/style/fixed/vue-splitpane.scss';
// 补丁 vue-grid-layout
@import '~@/assets/style/fixed/vue-grid-layout.scss';
// 补丁 tree-view
@import '~@/assets/style/fixed/tree-view.scss';
// 动画
@import '~@/assets/style/animate/vue-transition.scss';
// 在这里写公用的class
// 注意 这个文件里只写class
// mixin等内容请在 public.scss 里书写
// 文字相关
.#{$prefix}-text-center {
text-align: center;
}
// 浮动相关
.#{$prefix}-fl {
float: left;
}
.#{$prefix}-fr {
float: right;
}
// 边距相关
$sizes: (0, 5, 10, 15, 20);
@for $index from 1 to 6 {
.#{$prefix}-m-#{nth($sizes, $index)} { margin: #{nth($sizes, $index)}px !important; }
.#{$prefix}-mt-#{nth($sizes, $index)} { margin-top: #{nth($sizes, $index)}px !important; }
.#{$prefix}-mr-#{nth($sizes, $index)} { margin-right: #{nth($sizes, $index)}px !important; }
.#{$prefix}-mb-#{nth($sizes, $index)} { margin-bottom: #{nth($sizes, $index)}px !important; }
.#{$prefix}-ml-#{nth($sizes, $index)} { margin-left: #{nth($sizes, $index)}px !important; }
.#{$prefix}-p-#{nth($sizes, $index)} { padding: #{nth($sizes, $index)}px !important; }
.#{$prefix}-pt-#{nth($sizes, $index)} { padding-top: #{nth($sizes, $index)}px !important; }
.#{$prefix}-pr-#{nth($sizes, $index)} { padding-right: #{nth($sizes, $index)}px !important; }
.#{$prefix}-pb-#{nth($sizes, $index)} { padding-bottom: #{nth($sizes, $index)}px !important; }
.#{$prefix}-pl-#{nth($sizes, $index)} { padding-left: #{nth($sizes, $index)}px !important; }
}
// 快速使用
.#{$prefix}-m { margin: 20px !important; }
.#{$prefix}-mt { margin-top: 20px !important; }
.#{$prefix}-mr { margin-right: 20px !important; }
.#{$prefix}-mb { margin-bottom: 20px !important; }
.#{$prefix}-ml { margin-left: 20px !important; }
.#{$prefix}-p { padding: 20px !important; }
.#{$prefix}-pt { padding-top: 20px !important; }
.#{$prefix}-pr { padding-right: 20px !important; }
.#{$prefix}-pb { padding-bottom: 20px !important; }
.#{$prefix}-pl { padding-left: 20px !important; }
@import '~@/assets/style/unit/color.scss';
// 工具类名统一前缀
$prefix: d2;
// 禁止用户选中 鼠标变为手形
%unable-select {
user-select: none;
cursor: pointer;
}
// 填满父元素
// 组要父元素 position: relative | absolute;
%full {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
// flex 垂直水平居中
%flex-center-row {
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
}
%flex-center-col {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
// 将元素模拟成卡片外观
%card {
border: 1px solid #dddee1;
border-color: #e9eaec;
background: #fff;
border-radius: 4px;
font-size: 14px;
position: relative;
}
\ No newline at end of file
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'chester';
// 主题背景颜色
$theme-bg-color: #2C3643;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: #FFFFFF;
$theme-message-info-text-color: #222A34;
$theme-message-info-border-color: #222A34;
// container组件
$theme-container-background-color: rgba(#FFF, 1);
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid #CFD7E5;
$theme-container-border-outer: 1px solid #2A2D2E;
$theme-multiple-page-control-color: #CCCCCC;
$theme-multiple-page-control-color-active: #242D38;
$theme-multiple-page-control-nav-prev-color: #CCCCCC;
$theme-multiple-page-control-nav-next-color: #CCCCCC;
$theme-multiple-page-control-border-color: #2A2D2E;
$theme-multiple-page-control-border-color-active: #FFFFFF;
$theme-multiple-page-control-background-color: #242D38;
$theme-multiple-page-control-background-color-active: #FFFFFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #CCCCCC;
$theme-menu-item-background-color-hover: #2A2D2E;
// 顶栏上的文字颜色
$theme-header-item-color: #CCCCCC;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #CCCCCC;
$theme-header-item-background-color-hover: #2A2D2E;
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #CCCCCC;
$theme-header-item-background-color-focus: #222A34;
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #FFFFFF;
$theme-header-item-background-color-active: #222A34;
// 侧边栏上的文字颜色
$theme-aside-item-color: #CCCCCC;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: #CCCCCC;
$theme-aside-item-background-color-hover: #2A2D2E;
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: #CCCCCC;
$theme-aside-item-background-color-focus: #222A34;
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: #FFFFFF;
$theme-aside-item-background-color-active: #222A34;
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: #CCCCCC;
$theme-aside-menu-empty-text-color: #CCCCCC;
$theme-aside-menu-empty-background-color: #242D38;
$theme-aside-menu-empty-icon-color-hover: #FFFFFF;
$theme-aside-menu-empty-text-color-hover: #FFFFFF;
$theme-aside-menu-empty-background-color-hover: #242D38;
\ No newline at end of file
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'd2';
// 主题背景颜色
$theme-bg-color: #ebf1f6;
// $theme-bg-color: #ffffff;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: $color-bg;
$theme-message-info-text-color: $color-text-normal;
$theme-message-info-border-color: $color-border-1;
// container组件
$theme-container-background-color: rgba(#FFF, 1);
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid #F2F2F5;
$theme-container-border-outer: 1px solid #F2F2F5;
$theme-multiple-page-control-color: #BFCBD9;
// $theme-multiple-page-control-color-active: #2f74ff;
$theme-multiple-page-control-color-active: #307dee;
$theme-multiple-page-control-nav-prev-color: #F2F2F5;
$theme-multiple-page-control-nav-next-color: #F2F2F5;
$theme-multiple-page-control-border-color: #F2F2F5;
$theme-multiple-page-control-border-color-active: #FFF;
// $theme-multiple-page-control-background-color: rgba(#000, .03);
$theme-multiple-page-control-background-color: #fff;
$theme-multiple-page-control-background-color-active: #FFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #293849;
$theme-menu-item-background-color-hover: #ecf5ff;
// 顶栏上的文字颜色
$theme-header-item-color: $color-text-normal;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #307dee;
$theme-header-item-background-color-hover: rgba(#FFF, .5);
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #307dee;
$theme-header-item-background-color-focus: rgba(#FFF, .5);
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #307dee;
$theme-header-item-background-color-active: rgba(#FFF, .5);
// 侧边栏上的文字颜色
$theme-aside-item-color: $color-text-normal;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: #FFF;
// $theme-aside-item-background-color-hover: rgba(#FFF, .5);
$theme-aside-item-background-color-hover: #307dee;
// 侧边栏上的项目在 focus 时
// $theme-aside-item-color-focus: #ff8c00;
// $theme-aside-item-background-color-focus: rgba(#FFF, .5);
$theme-aside-item-color-focus: #FFF;
$theme-aside-item-background-color-focus: #307dee;
// $theme-aside-item-color-active: #ff8c00;
// $theme-aside-item-background-color-active: rgba(#FFF, .5);
$theme-aside-item-color-active: #fff;
$theme-aside-item-background-color-active: #307dee;
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: $color-text-normal;
$theme-aside-menu-empty-text-color: $color-text-normal;
$theme-aside-menu-empty-background-color: rgba(#000, .03);
$theme-aside-menu-empty-icon-color-hover: $color-text-main;
$theme-aside-menu-empty-text-color-hover: $color-text-main;
$theme-aside-menu-empty-background-color-hover: rgba(#000, .05);
\ No newline at end of file
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'element';
// 主题背景颜色
$theme-bg-color: #f2f2f5;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: #ffffff;
$theme-message-info-text-color: #202d3d;
$theme-message-info-border-color: #202d3d;
// container组件
$theme-container-background-color: rgba(#fff, 1);
$theme-container-header-footer-background-color: #fff;
$theme-container-border-inner: 1px solid #cfd7e5;
$theme-container-border-outer: 1px solid #fff;
$theme-multiple-page-control-color: #bfcbd9;
$theme-multiple-page-control-color-active: #307dee;
$theme-multiple-page-control-nav-prev-color: #bfcbd9;
$theme-multiple-page-control-nav-next-color: #bfcbd9;
$theme-multiple-page-control-border-color: #fff;
$theme-multiple-page-control-border-color-active: #ffffff;
$theme-multiple-page-control-background-color: #fff;
$theme-multiple-page-control-background-color-active: #ffffff;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #bfcbd9;
$theme-menu-item-background-color-hover: #fff;
// 顶栏上的文字颜色
$theme-header-item-color: #bfcbd9;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #bfcbd9;
$theme-header-item-background-color-hover: #fff;
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #bfcbd9;
$theme-header-item-background-color-focus: #307dee;
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #fff;
$theme-header-item-background-color-active: #307dee;
// 侧边栏上的文字颜色
$theme-aside-item-color: black;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: black;
$theme-aside-item-background-color-hover: #fff;
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: #bfcbd9;
$theme-aside-item-background-color-focus: #307dee;
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: #fff;
$theme-aside-item-background-color-active: #307dee;
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: #bfcbd9;
$theme-aside-menu-empty-text-color: #bfcbd9;
$theme-aside-menu-empty-background-color: #307dee;
$theme-aside-menu-empty-icon-color-hover: #fff;
$theme-aside-menu-empty-text-color-hover: #fff;
$theme-aside-menu-empty-background-color-hover: #307dee;
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'line';
// 主题背景颜色
$theme-bg-color: #f8f8f9;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: $color-bg;
$theme-message-info-text-color: $color-text-normal;
$theme-message-info-border-color: $color-border-1;
// container组件
$theme-container-background-color: rgba(#FFF, .8);
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid $color-border-2;
$theme-container-border-outer: 1px solid #cfd7e5;
$theme-multiple-page-control-color: #FFF;
$theme-multiple-page-control-color-active: $color-text-normal;
$theme-multiple-page-control-nav-prev-color: #cfd7e5;
$theme-multiple-page-control-nav-next-color: #cfd7e5;
$theme-multiple-page-control-border-color: #cfd7e5;
$theme-multiple-page-control-border-color-active: #FFF;
$theme-multiple-page-control-background-color: #cfd7e5;
$theme-multiple-page-control-background-color-active: #FFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #293849;
$theme-menu-item-background-color-hover: #EFEFEF;
// 顶栏上的文字颜色
$theme-header-item-color: $color-text-normal;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: $color-text-main;
$theme-header-item-background-color-hover: rgba(#000, .02);
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: $color-text-main;
$theme-header-item-background-color-focus: rgba(#000, .02);
// 顶栏上的项目在 active 时
$theme-header-item-color-active: $color-text-main;
$theme-header-item-background-color-active: rgba(#000, .03);
// 侧边栏上的文字颜色
$theme-aside-item-color: $color-text-normal;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: $color-text-main;
$theme-aside-item-background-color-hover: rgba(#000, .02);
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: $color-text-main;
$theme-aside-item-background-color-focus: rgba(#000, .02);
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: $color-text-main;
$theme-aside-item-background-color-active: rgba(#000, .03);
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: $color-text-normal;
$theme-aside-menu-empty-text-color: $color-text-normal;
$theme-aside-menu-empty-background-color: rgba(#000, .03);
$theme-aside-menu-empty-icon-color-hover: $color-text-main;
$theme-aside-menu-empty-text-color-hover: $color-text-main;
$theme-aside-menu-empty-background-color-hover: rgba(#000, .05);
\ No newline at end of file
@import '~@/assets/style/theme/theme-base.scss';
@import '~@/assets/style/theme/d2/index.scss';
@import '~@/assets/style/theme/chester/index.scss';
@import '~@/assets/style/theme/element/index.scss';
@import '~@/assets/style/theme/line/index.scss';
@import '~@/assets/style/theme/star/index.scss';
@import '~@/assets/style/theme/tomorrow-night-blue/index.scss';
@import '~@/assets/style/theme/violet/index.scss';
\ No newline at end of file
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'star';
// 主题背景颜色
$theme-bg-color: #EFF4F8;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, .3);
// 消息提示
$theme-message-info-background-color: $color-bg;
$theme-message-info-text-color: $color-text-normal;
$theme-message-info-border-color: $color-border-1;
// container组件
$theme-container-background-color: rgba(#FFF, .9);
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid $color-border-1;
$theme-container-border-outer: 1px solid #114450;
$theme-multiple-page-control-color: #FFF;
$theme-multiple-page-control-color-active: $color-text-normal;
$theme-multiple-page-control-nav-prev-color: #FFF;
$theme-multiple-page-control-nav-next-color: #FFF;
$theme-multiple-page-control-border-color: #114450;
$theme-multiple-page-control-border-color-active: #FFF;
$theme-multiple-page-control-background-color: rgba(#FFF, .5);
$theme-multiple-page-control-background-color-active: #FFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #293849;
$theme-menu-item-background-color-hover: #ecf5ff;
// 顶栏上的文字颜色
$theme-header-item-color: #FFF;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #FFF;
$theme-header-item-background-color-hover: rgba(#000, .2);
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #FFF;
$theme-header-item-background-color-focus: rgba(#000, .2);
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #FFF;
$theme-header-item-background-color-active: rgba(#000, .3);
// 侧边栏上的文字颜色
$theme-aside-item-color: #FFF;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: #FFF;
$theme-aside-item-background-color-hover: rgba(#000, .2);
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: #FFF;
$theme-aside-item-background-color-focus: rgba(#000, .2);
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: #FFF;
$theme-aside-item-background-color-active: rgba(#000, .3);
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: #FFF;
$theme-aside-menu-empty-text-color: #FFF;
$theme-aside-menu-empty-background-color: rgba(#FFF, .2);
$theme-aside-menu-empty-icon-color-hover: #FFF;
$theme-aside-menu-empty-text-color-hover: #FFF;
$theme-aside-menu-empty-background-color-hover: rgba(#FFF, .3);
\ No newline at end of file
// 减小弹出菜单的项目高度
.el-menu--popup {
.el-menu-item {
height: 36px;
line-height: 36px;
}
.el-submenu__title {
height: 36px;
line-height: 36px;
}
}
// 整体框架结构
.d2-layout-header-aside-group {
height: 100%;
width: 100%;
min-width: 900px;
background-size: cover;
background-position: center;
overflow: hidden;
position: relative;
// 背景上面的半透明遮罩
.d2-layout-header-aside-mask {
@extend %full;
}
// 内容层
.d2-layout-header-aside-content {
@extend %full;
.d2-theme-header {
height: 60px;
margin-bottom: 4px;
background-color: #307dee;
.d2-theme-header-menu {
overflow: hidden;
&.is-scrollable {
position: relative;
padding: 0 20px;
.d2-theme-header-menu__prev,
.d2-theme-header-menu__next {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
}
.d2-theme-header-menu__content {
overflow: hidden;
.d2-theme-header-menu__scroll {
white-space: nowrap;
position: relative;
-webkit-transition: -webkit-transform 0.3s;
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
float: left;
}
}
.d2-theme-header-menu__prev,
.d2-theme-header-menu__next {
height: 60px;
position: absolute;
top: 0;
font-size: 20px;
cursor: pointer;
display: none;
}
.d2-theme-header-menu__prev {
left: 0;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.d2-theme-header-menu__next {
right: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
}
}
.d2-theme-container {
.d2-theme-container-aside {
position: relative;
background-color: #fff !important;
border: none;
.d2-layout-header-aside-menu-side {
@extend %full;
overflow: hidden;
}
}
.d2-theme-container-transition {
transition: width 0.3s;
}
.d2-theme-container-main {
padding: 0px;
position: relative;
overflow: hidden;
.d2-theme-container-main-layer {
position: absolute;
top: 0px;
bottom: 0px;
left: 5px;
right: 0px;
}
.d2-theme-container-main-body {
position: relative;
}
}
}
}
}
// 主题公用
.d2-layout-header-aside-group {
&.grayMode {
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
}
// 主体
.d2-layout-header-aside-content {
// [布局] 顶栏
.d2-theme-header {
// logo区域
.logo-group {
float: left;
text-align: center;
img {
height: 60px;
}
}
.logo-transition {
transition: width 0.3s;
@extend %flex-center-row;
}
// 折叠侧边栏切换按钮
.toggle-aside-btn {
float: left;
height: 60px;
width: 60px;
display: flex;
justify-content: center;
align-items: center;
@extend %unable-select;
i {
font-size: 20px;
margin-top: 4px;
}
}
.toggle-title {
color: #fff;
@extend %flex-center-row;
}
// [菜单] 顶栏
.el-menu {
float: left;
border-bottom: none;
background-color: transparent;
%header-menu-item {
@extend %unable-select;
i.fa {
font-size: 16px;
margin-right: 4px;
}
}
.el-menu-item {
@extend %header-menu-item;
border-bottom: none;
}
.el-submenu {
@extend %header-menu-item;
.el-submenu__title {
border-bottom: none;
}
}
}
// 顶栏右侧的按钮
.d2-header-right {
float: right;
height: 60px;
display: flex;
align-items: center;
.btn-text {
padding: 14px 12px;
border-radius: 4px;
margin: 0px !important;
&.el-color-picker.el-color-picker--mini {
padding: 9px 6px;
}
}
.el-dropdown {
@extend %unable-select;
}
}
}
// [布局] 顶栏下面
.d2-theme-container {
// 侧边栏
.d2-theme-container-aside {
%d2-theme-container-aside-menu-icon {
width: 20px;
text-align: center;
font-size: 16px;
}
// [菜单] 正常状态
.el-menu {
@extend %unable-select;
background-color: transparent;
border-right: none;
.el-menu-item {
i {
@extend %d2-theme-container-aside-menu-icon;
}
}
}
.el-submenu {
@extend %unable-select;
.el-submenu__title {
i {
@extend %d2-theme-container-aside-menu-icon;
}
.el-submenu__icon-arrow {
margin-top: -10px;
}
}
}
// 菜单为空的时候显示的信息
.d2-layout-header-aside-menu-empty {
height: 160px;
margin: 10px;
margin-top: 0px;
border-radius: 4px;
@extend %unable-select;
i {
font-size: 30px;
margin-bottom: 10px;
}
span {
font-size: 14px;
}
}
// [菜单] 折叠状态
.el-menu--collapse {
background-color: transparent;
.el-submenu__title {
text-align: center;
}
}
}
// 右下 主体
.d2-theme-container-main {
// 主体部分分为多页面控制器 和主体
.d2-theme-container-main-header {
height: 41px;
// 多页面控制器
.d2-multiple-page-control-group {
// padding-right: 20px;
.d2-multiple-page-control-content {
overflow: auto;
position: relative;
.d2-multiple-page-control-content-inner {
.d2-multiple-page-control {
.el-tabs__header.is-top {
margin: 0px;
}
.el-tabs__nav {
overflow: hidden;
}
}
}
}
.d2-multiple-page-control-btn {
position: relative;
bottom: -1px;
.el-dropdown {
.el-button-group {
.el-button:first-child {
border-bottom-left-radius: 0px;
}
.el-button:last-child {
border-bottom-right-radius: 0px;
}
}
}
}
}
}
// 主体
.d2-theme-container-main-body {
// 布局组件
.container-component {
@extend %full;
overflow: hidden;
// 填充式布局组件
.d2-container-full {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-full__header {
padding: 20px;
}
.d2-container-full__body {
flex-grow: 1;
height: 100%;
padding: 20px 20px;
overflow: auto;
position: relative;
}
.d2-container-full__footer {
padding: 20px;
}
}
// 填充式布局组件 - 滚动优化
.d2-container-full-bs {
position: absolute;
top: 0px;
right: 20px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-full-bs__header {
padding: 20px;
}
.d2-container-full-bs__body {
flex-grow: 1;
overflow: hidden;
position: relative;
.d2-container-full-bs__body-wrapper-inner {
padding: 20px;
position: relative;
}
}
.d2-container-full-bs__footer {
padding: 20px;
}
}
// 隐形布局组件
.d2-container-ghost {
position: absolute;
top: 0px;
right: 20px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-ghost__header {
padding: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.d2-container-ghost__body {
flex-grow: 1;
overflow: auto;
position: relative;
}
.d2-container-ghost__footer {
padding: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
// 隐形布局组件 - 滚动优化
.d2-container-ghost-bs {
position: absolute;
top: 0px;
right: 20px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-ghost-bs__header {
padding: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.d2-container-ghost-bs__body {
flex-grow: 1;
overflow: hidden;
position: relative;
}
.d2-container-ghost-bs__footer {
padding: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
// 卡片式布局组件
.d2-container-card {
position: absolute;
top: 0px;
right: 20px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-card__header {
padding: 20px;
}
.d2-container-card__body {
flex-grow: 1;
overflow: auto;
.d2-container-card__body-card {
position: relative;
margin-bottom: 20px;
padding: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
}
.d2-container-card__footer {
padding: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
// 卡片式布局组件 - 滚动优化
.d2-container-card-bs {
position: absolute;
top: 0px;
right: 20px;
bottom: 0px;
left: 0px;
display: flex;
flex-direction: column;
overflow: hidden;
.d2-container-card-bs__header {
padding: 20px;
}
.d2-container-card-bs__body {
position: relative;
flex-grow: 1;
overflow: hidden;
.d2-container-card-bs__body-wrapper-inner {
padding-bottom: 20px;
}
.d2-container-card-bs__body-card {
position: relative;
padding: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
}
.d2-container-card-bs__footer {
padding: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
}
}
}
}
}
}
// 每个主题特有的设置
.theme-#{$theme-name} {
.el-message {
&.el-message--info {
background-color: $theme-message-info-background-color;
color: $theme-message-info-text-color;
border-color: $theme-message-info-border-color;
}
}
.el-card {
&.d2-card {
border: $theme-container-border-outer;
.el-card__header {
border-bottom: $theme-container-border-outer;
}
}
}
// 背景图片和遮罩
.d2-layout-header-aside-group {
background-color: $theme-bg-color;
.d2-layout-header-aside-mask {
background: $theme-bg-mask;
}
}
// 菜单项目
@mixin theme-menu-hover-style {
color: $theme-menu-item-color-hover;
i.fa {
color: $theme-menu-item-color-hover;
}
// background: $theme-menu-item-background-color-hover;
background:#307dee;
}
%el-menu-icon {
i {
display: inline-block;
width: 14px;
text-align: center;
margin-right: 5px;
}
svg {
margin: 0px;
height: 14px;
width: 14px;
margin-right: 5px;
}
}
.el-submenu__title {
@extend %unable-select;
@extend %el-menu-icon;
}
.el-menu-item {
@extend %unable-select;
@extend %el-menu-icon;
}
.el-submenu__title:hover {
@include theme-menu-hover-style;
}
.el-menu-item:hover {
@include theme-menu-hover-style;
}
.el-menu--horizontal .el-menu-item:not(.is-disabled):hover {
@include theme-menu-hover-style;
}
.el-menu--horizontal .el-menu .el-submenu__title:hover {
@include theme-menu-hover-style;
}
// 顶栏
.d2-theme-header {
// 顶栏菜单空间不足时显示的滚动控件
.d2-theme-header-menu {
.d2-theme-header-menu__prev, .d2-theme-header-menu__next {
color: $theme-header-item-color;
background: $theme-header-item-background-color;
&:hover {
color: $theme-header-item-color-hover;
background: $theme-header-item-background-color-hover;
}
}
}
// 切换按钮
.toggle-aside-btn {
i {
color: $theme-header-item-color;
background: $theme-header-item-background-color;
&:hover {
color: $theme-header-item-color-hover;
}
}
}
// 顶栏菜单
.el-menu {
.el-menu-item {
transition: border-top-color 0s;
color: $theme-header-item-color;
background: $theme-header-item-background-color;
i.fa { color: inherit; }
&:hover {
color: $theme-header-item-color-hover;
background: $theme-header-item-background-color-hover;
i.fa { color: inherit; }
}
&:focus {
color: $theme-header-item-color-focus;
background: $theme-header-item-background-color-focus;
i.fa { color: inherit; }
}
&.is-active {
color: $theme-header-item-color-active;
background: $theme-header-item-background-color-active;
i.fa { color: inherit; }
}
}
.el-submenu {
&.is-active {
.el-submenu__title {
color: $theme-header-item-color-active;
background: $theme-header-item-background-color-active;
i.fa { color: inherit; }
}
}
.el-submenu__title {
transition: border-top-color 0s;
color: $theme-header-item-color;
background: $theme-header-item-background-color;
i.fa { color: inherit; }
.el-submenu__icon-arrow {
color: $theme-header-item-color;
}
&:hover {
color: $theme-header-item-color-hover;
background: $theme-header-item-background-color-hover;
i.fa { color: inherit; }
.el-submenu__icon-arrow {
color: $theme-header-item-color-hover;
}
}
&:focus {
color: $theme-header-item-color-focus;
background: $theme-header-item-background-color-focus;
i.fa { color: inherit; }
.el-submenu__icon-arrow {
color: $theme-header-item-color-focus;
}
}
}
}
}
// 顶栏右侧
.d2-header-right {
.btn-text {
color: $theme-header-item-color;
&.can-hover {
&:hover {
color: $theme-header-item-color-hover;
background: $theme-header-item-background-color-hover;
}
}
}
}
}
// [布局] 顶栏下面
.d2-theme-container {
// 侧边栏
.d2-theme-container-aside {
// 菜单为空的时候显示的信息
.d2-layout-header-aside-menu-empty {
background: $theme-aside-menu-empty-background-color;
i {
color: $theme-aside-menu-empty-icon-color;
}
span {
color: $theme-aside-menu-empty-text-color;
}
&:hover {
background: $theme-aside-menu-empty-background-color-hover;
i {
color: $theme-aside-menu-empty-icon-color-hover;
}
span {
color: $theme-aside-menu-empty-text-color-hover;
}
}
}
// [菜单] 正常状态
.el-menu {
.el-menu-item {
color: $theme-aside-item-color;
background: $theme-aside-item-background-color;
i {
color: $theme-aside-item-color;
}
&:hover {
color: $theme-aside-item-color-hover;
fill: $theme-aside-item-color-hover;
background: $theme-aside-item-background-color-hover;
i {
color: $theme-aside-item-color-hover;
}
}
&:focus {
color: $theme-aside-item-color-focus;
fill: $theme-aside-item-color-focus;
background: $theme-aside-item-background-color-focus;
i {
color: $theme-aside-item-color-focus;
}
}
&.is-active {
color: $theme-aside-item-color-active;
fill: $theme-aside-item-color-active;
background: $theme-aside-item-background-color-active;
i {
color: $theme-aside-item-color-active;
}
}
}
}
.el-submenu {
.el-submenu__title {
color: $theme-aside-item-color;
background: $theme-aside-item-background-color;
i {
color: $theme-aside-item-color;
}
.el-submenu__icon-arrow {
color: $theme-aside-item-color;
}
&:hover {
color: $theme-aside-item-color-hover;
background: $theme-aside-item-background-color-hover;
i {
color: $theme-aside-item-color-hover;
}
.el-submenu__icon-arrow {
color: $theme-aside-item-color-hover;
}
}
}
}
}
.d2-theme-container-main {
// 主体部分分为多页面控制器 和主体
.d2-theme-container-main-header {
// 多页面控制器
.d2-multiple-page-control {
.el-tabs__header.is-top {
border-bottom-color: $theme-multiple-page-control-border-color;
}
.el-tabs__nav {
border-color: $theme-multiple-page-control-border-color;
.el-tabs__item {
@extend %unable-select;
color: $theme-multiple-page-control-color;
background-color: $theme-multiple-page-control-background-color;
border-left-color: $theme-multiple-page-control-border-color;
&:first-child {
border-left: none;
&:hover {
padding: 0px 20px;
}
}
}
.el-tabs__item.is-active {
color: $theme-multiple-page-control-color-active;
background-color: $theme-multiple-page-control-background-color-active;
border-bottom-color: $theme-multiple-page-control-border-color-active;
}
}
%el-tabs__nav {
font-size: 20px;
}
.el-tabs__nav-prev {
@extend %el-tabs__nav;
color: $theme-multiple-page-control-nav-prev-color;
}
.el-tabs__nav-next {
@extend %el-tabs__nav;
color: $theme-multiple-page-control-nav-next-color;
}
}
// 多页控制器的关闭控制
.d2-multiple-page-control-btn {
.el-dropdown {
.el-button-group {
.el-button {
border-color: $theme-multiple-page-control-border-color;
}
}
}
}
}
// 主体
.d2-theme-container-main-body {
// 布局组件
.container-component {
// [组件]
// d2-container-full 填充型
.d2-container-full {
border: $theme-container-border-outer;
border-top: none;
border-bottom: none;
.d2-container-full__header {
border-bottom: $theme-container-border-inner;
background: $theme-container-header-footer-background-color;
}
.d2-container-full__body {
background: $theme-container-background-color;
}
.d2-container-full__footer {
border-top: $theme-container-border-inner;
background: $theme-container-header-footer-background-color;
}
}
// [组件]
// d2-container-full-bs 填充型 滚动优化
.d2-container-full-bs {
border: $theme-container-border-outer;
border-top: none;
border-bottom: none;
.d2-container-full-bs__header {
border-bottom: $theme-container-border-inner;
background: $theme-container-header-footer-background-color;
}
.d2-container-full-bs__body {
background: $theme-container-background-color;
}
.d2-container-full-bs__footer {
border-top: $theme-container-border-inner;
background: $theme-container-header-footer-background-color;
}
}
// [组件]
// d2-container-ghost 隐形布局组件
.d2-container-ghost {
.d2-container-ghost__header {
border-bottom: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
.d2-container-ghost__footer {
border-top: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
}
// [组件]
// d2-container-ghost-bs 隐形布局组件 滚动优化
.d2-container-ghost-bs {
.d2-container-ghost-bs__header {
border-bottom: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
.d2-container-ghost-bs__footer {
border-top: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
}
// [组件]
// d2-container-card 卡片型
.d2-container-card {
.d2-container-card__header {
border-bottom: $theme-container-border-inner;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
.d2-container-card__body {
.d2-container-card__body-card {
background: $theme-container-background-color;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
border-bottom: $theme-container-border-outer;
}
}
.d2-container-card__footer {
border-top: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
}
// [组件]
// d2-container-card-bs 卡片型 滚动优化
.d2-container-card-bs {
.d2-container-card-bs__header {
border-bottom: $theme-container-border-inner;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
.d2-container-card-bs__body {
.d2-container-card-bs__body-card {
background: $theme-container-background-color;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
border-bottom: $theme-container-border-outer;
}
}
.d2-container-card-bs__footer {
border-top: $theme-container-border-outer;
border-left: $theme-container-border-outer;
border-right: $theme-container-border-outer;
background: $theme-container-header-footer-background-color;
}
}
}
}
}
}
}
@import './setting.scss';
@import '../theme.scss';
// 主题名称
$theme-name: 'tomorrow-night-blue';
// 主题背景颜色
$theme-bg-color: #002253;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: $color-bg;
$theme-message-info-text-color: $color-text-normal;
$theme-message-info-border-color: $color-border-1;
// container组件
$theme-container-background-color: rgba(#FFF, 1);
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid $color-border-1;
$theme-container-border-outer: 1px solid #002253;
$theme-multiple-page-control-color: #FFF;
$theme-multiple-page-control-color-active: $color-text-normal;
$theme-multiple-page-control-nav-prev-color: #FFF;
$theme-multiple-page-control-nav-next-color: #FFF;
$theme-multiple-page-control-border-color: #002253;
$theme-multiple-page-control-border-color-active: #FFF;
$theme-multiple-page-control-background-color: rgba(#FFF, .2);
$theme-multiple-page-control-background-color-active: #FFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #293849;
$theme-menu-item-background-color-hover: #ecf5ff;
// 顶栏上的文字颜色
$theme-header-item-color: #FF929A;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #FFEBA4;
$theme-header-item-background-color-hover: rgba(#FFF, .05);
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #FFB870;
$theme-header-item-background-color-focus: rgba(#FFF, .05);
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #FFB870;
$theme-header-item-background-color-active: rgba(#FFF, .05);
// 侧边栏上的文字颜色
$theme-aside-item-color: #FF929A;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: #FFEBA4;
$theme-aside-item-background-color-hover: rgba(#FFF, .05);
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: #FFB870;
$theme-aside-item-background-color-focus: rgba(#FFF, .05);
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: #FFB870;
$theme-aside-item-background-color-active: rgba(#FFF, .05);
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: #FFB870;
$theme-aside-menu-empty-text-color: #FFB870;
$theme-aside-menu-empty-background-color: rgba(#FFF, .1);
$theme-aside-menu-empty-icon-color-hover: #FFEBA4;
$theme-aside-menu-empty-text-color-hover: #FFEBA4;
$theme-aside-menu-empty-background-color-hover: rgba(#FFF, .2);
\ No newline at end of file
@import './setting.scss';
@import '../theme.scss';
.theme-#{$theme-name} {
.d2-layout-header-aside-group {
background: #bc00e3;
background: linear-gradient(120deg, #bc00e3 0%, #4EFFFB 100%);
}
}
\ No newline at end of file
// 主题名称
$theme-name: 'violet';
// 主题背景颜色
$theme-bg-color: #000;
// 主题背景图片遮罩
$theme-bg-mask: rgba(#000, 0);
// 消息提示
$theme-message-info-background-color: $color-bg;
$theme-message-info-text-color: $color-text-normal;
$theme-message-info-border-color: $color-border-1;
// container组件
$theme-container-background-color: #FFF;
$theme-container-header-footer-background-color: #FFF;
$theme-container-border-inner: 1px solid $color-border-2;
$theme-container-border-outer: 1px solid #8C40E2;
$theme-multiple-page-control-color: #FFF;
$theme-multiple-page-control-color-active: $color-text-normal;
$theme-multiple-page-control-nav-prev-color: #FFF;
$theme-multiple-page-control-nav-next-color: #FFF;
$theme-multiple-page-control-border-color: #8C40E2;
$theme-multiple-page-control-border-color-active: #FFF;
$theme-multiple-page-control-background-color: rgba(#FFF, .3);
$theme-multiple-page-control-background-color-active: #FFF;
// 顶栏和侧边栏中展开的菜单 hover 状态下
$theme-menu-item-color-hover: #293849;
$theme-menu-item-background-color-hover: #ecf5ff;
// 顶栏上的文字颜色
$theme-header-item-color: #FFF;
$theme-header-item-background-color: transparent;
// 顶栏上的项目在 hover 时
$theme-header-item-color-hover: #FFF;
$theme-header-item-background-color-hover: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%);
// 顶栏上的项目在 focus 时
$theme-header-item-color-focus: #FFF;
$theme-header-item-background-color-focus: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%);
// 顶栏上的项目在 active 时
$theme-header-item-color-active: #FFF;
$theme-header-item-background-color-active: linear-gradient(-180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%);
// 侧边栏上的文字颜色
$theme-aside-item-color: #FFF;
$theme-aside-item-background-color: transparent;
// 侧边栏上的项目在 hover 时
$theme-aside-item-color-hover: #FFF;
$theme-aside-item-background-color-hover: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%);
// 侧边栏上的项目在 focus 时
$theme-aside-item-color-focus: #FFF;
$theme-aside-item-background-color-focus: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%);
// 侧边栏上的项目在 active 时
$theme-aside-item-color-active: #FFF;
$theme-aside-item-background-color-active: linear-gradient(90deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.00) 100%);
// 侧边栏菜单为空的时候显示的元素
$theme-aside-menu-empty-icon-color: #FFF;
$theme-aside-menu-empty-text-color: #FFF;
$theme-aside-menu-empty-background-color: rgba(#000, .1);
$theme-aside-menu-empty-icon-color-hover: #FFF;
$theme-aside-menu-empty-text-color-hover: #FFF;
$theme-aside-menu-empty-background-color-hover: rgba(#000, .15);
\ No newline at end of file
// 主色
$color-primary: #409EFF;
// 辅助色
$color-info: #909399;
$color-success: #67C23A;
$color-warning: #E6A23C;
$color-danger: #F56C6C;
// 文字
$color-text-main: #303133;
$color-text-normal: #606266;
$color-text-sub: #909399;
$color-text-placehoder: #C0C4CC;
// 边框
$color-border-1: #DCDFE6;
$color-border-2: #E4E7ED;
$color-border-3: #EBEEF5;
$color-border-4: #F2F6FC;
// 背景
$color-bg: #f8f8f9;
\ No newline at end of file
<svg viewBox="0 0 88 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.3 (51167) - http://www.bohemiancoding.com/sketch -->
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Tablet" transform="translate(-1077.000000, -135.000000)">
<g id="Group" transform="translate(1077.000000, 132.000000)">
<path d="M0.74,85.8 L0.74,71.44 L3.72,71.44 C5.7600102,71.44 7.2566619,71.5899985 8.21,71.89 C9.1633381,72.1900015 9.9866632,72.6633301 10.68,73.31 C11.3733368,73.9566699 11.9066648,74.746662 12.28,75.68 C12.6533352,76.613338 12.84,77.7033271 12.84,78.95 C12.84,80.1966729 12.5500029,81.3699945 11.97,82.47 C11.3899971,83.5700055 10.6033383,84.3999972 9.61,84.96 C8.6166617,85.5200028 7.186676,85.8 5.32,85.8 L0.74,85.8 Z M2.12,84.44 L3.78,84.44 C5.513342,84.44 6.7699961,84.3533342 7.55,84.18 C8.3300039,84.0066658 9.0133304,83.6633359 9.6,83.15 C10.1866696,82.6366641 10.6333318,82.013337 10.94,81.28 C11.2466682,80.546663 11.4,79.7066714 11.4,78.76 C11.4,77.8133286 11.2233351,76.940004 10.87,76.14 C10.5166649,75.339996 10.0133366,74.6800026 9.36,74.16 C8.7066634,73.6399974 7.9366711,73.2900009 7.05,73.11 C6.1633289,72.9299991 4.8600086,72.84 3.14,72.84 L2.12,72.84 L2.12,84.44 Z M16.2,75.94 L14.82,75.94 C14.886667,74.473326 15.3733288,73.2966711 16.28,72.41 C17.1866712,71.5233289 18.2833269,71.08 19.57,71.08 C20.8566731,71.08 21.9233291,71.4966625 22.77,72.33 C23.6166709,73.1633375 24.04,74.179994 24.04,75.38 C24.04,76.2733378 23.8033357,77.0999962 23.33,77.86 C22.8566643,78.6200038 22.1400048,79.5199948 21.18,80.56 L17.6,84.42 L24.24,84.42 L24.24,85.8 L14.48,85.8 L19.96,79.88 C20.9200048,78.8399948 21.6066646,78.020003 22.02,77.42 C22.4333354,76.819997 22.64,76.1333372 22.64,75.36 C22.64,74.5866628 22.3366697,73.9066696 21.73,73.32 C21.1233303,72.7333304 20.3700045,72.44 19.47,72.44 C18.5699955,72.44 17.8233363,72.7433303 17.23,73.35 C16.6366637,73.9566697 16.2933338,74.8199944 16.2,75.94 Z M32.58,70.86 L39.28,85.8 L37.74,85.8 L35.48,80.9 L29.28,80.9 L27.04,85.8 L25.44,85.8 L32.22,70.86 L32.58,70.86 Z M32.4,74.04 L29.94,79.44 L34.84,79.44 L32.4,74.04 Z M51.88,71.08 L51.88,85.8 L50.54,85.8 L50.54,83.98 C49.3266606,85.4333406 47.9366745,86.16 46.37,86.16 C44.8033255,86.16 43.486672,85.6066722 42.42,84.5 C41.353328,83.3933278 40.82,82.0666744 40.82,80.52 C40.82,78.9733256 41.3599946,77.6500055 42.44,76.55 C43.5200054,75.4499945 44.8133258,74.9 46.32,74.9 C48.053342,74.9 49.4599946,75.6399926 50.54,77.12 L50.54,71.08 L51.88,71.08 Z M50.62,80.56 C50.62,79.3466606 50.220004,78.3200042 49.42,77.48 C48.619996,76.6399958 47.620006,76.22 46.42,76.22 C45.219994,76.22 44.2166707,76.6499957 43.41,77.51 C42.6033293,78.3700043 42.2,79.3833275 42.2,80.55 C42.2,81.7166725 42.6133292,82.733329 43.44,83.6 C44.2666708,84.466671 45.2433277,84.9 46.37,84.9 C47.4966723,84.9 48.4866624,84.4900041 49.34,83.67 C50.1933376,82.8499959 50.62,81.8133396 50.62,80.56 Z M54.98,75.18 L56.34,75.18 L56.34,77.02 C57.2866714,75.6066596 58.4999926,74.9 59.98,74.9 C60.7666706,74.9 61.4699969,75.1233311 62.09,75.57 C62.7100031,76.0166689 63.1266656,76.6466626 63.34,77.46 C63.7800022,76.6333292 64.3399966,76.0000022 65.02,75.56 C65.7000034,75.1199978 66.4833289,74.9 67.37,74.9 C68.2566711,74.9 69.0599964,75.2433299 69.78,75.93 C70.5000036,76.6166701 70.86,77.9733232 70.86,80 L70.86,85.8 L69.46,85.8 L69.46,80 C69.46,79.0266618 69.4000006,78.3100023 69.28,77.85 C69.1599994,77.3899977 68.900002,77.0033349 68.5,76.69 C68.099998,76.3766651 67.5700033,76.22 66.91,76.22 C66.2499967,76.22 65.6333362,76.4399978 65.06,76.88 C64.4866638,77.3200022 64.0933344,77.8799966 63.88,78.56 C63.6666656,79.2400034 63.56,80.2666598 63.56,81.64 L63.56,85.8 L62.22,85.8 L62.22,80.36 C62.22,79.226661 62.1600006,78.4166691 62.04,77.93 C61.9199994,77.4433309 61.6533354,77.0366683 61.24,76.71 C60.8266646,76.3833317 60.320003,76.22 59.72,76.22 C59.119997,76.22 58.5566693,76.3966649 58.03,76.75 C57.5033307,77.1033351 57.0900015,77.5866636 56.79,78.2 C56.4899985,78.8133364 56.34,79.8466594 56.34,81.3 L56.34,85.8 L54.98,85.8 L54.98,75.18 Z M73.1,72.22 C73.1,71.9133318 73.2099989,71.6466678 73.43,71.42 C73.6500011,71.1933322 73.9166651,71.08 74.23,71.08 C74.5433349,71.08 74.8099989,71.1899989 75.03,71.41 C75.2500011,71.6300011 75.36,71.8966651 75.36,72.21 C75.36,72.5233349 75.2500011,72.7899989 75.03,73.01 C74.8099989,73.2300011 74.5433349,73.34 74.23,73.34 C73.9166651,73.34 73.6500011,73.2266678 73.43,73 C73.2099989,72.7733322 73.1,72.5133348 73.1,72.22 Z M73.54,75.18 L74.92,75.18 L74.92,85.8 L73.54,85.8 L73.54,75.18 Z M77.74,75.18 L79.12,75.18 L79.12,77.08 C80.2133388,75.6266594 81.5399922,74.9 83.1,74.9 C83.900004,74.9 84.6199968,75.1166645 85.26,75.55 C85.9000032,75.9833355 86.353332,76.5499965 86.62,77.25 C86.886668,77.9500035 87.02,78.9799932 87.02,80.34 L87.02,85.8 L85.66,85.8 L85.66,80.74 C85.66,79.4999938 85.6033339,78.6600022 85.49,78.22 C85.3766661,77.7799978 85.2033345,77.4066682 84.97,77.1 C84.7366655,76.7933318 84.4400018,76.5600008 84.08,76.4 C83.7199982,76.2399992 83.2833359,76.16 82.77,76.16 C82.2566641,76.16 81.7533358,76.2866654 81.26,76.54 C80.7666642,76.7933346 80.3466684,77.1466644 80,77.6 C79.6533316,78.0533356 79.4200006,78.5199976 79.3,79 C79.1799994,79.4800024 79.12,80.4466594 79.12,81.9 L79.12,85.8 L77.74,85.8 L77.74,75.18 Z" id="D2Admin" fill="#409EFF"></path>
<g id="logo-no-shadow" transform="translate(11.000000, 0.000000)">
<path d="M44.2833805,33.4299717 L6.05798302,56.3652102 C4.16366196,57.5018028 1.70662094,56.8875426 0.570028297,54.9932215 C0.197031333,54.3715599 8.87839274e-17,53.6602143 0,52.9352385 L-4.4408921e-16,7.06476152 C-7.1463071e-16,4.85562252 1.790861,3.06476152 4,3.06476152 C4.72497578,3.06476152 5.43632142,3.26179285 6.05798302,3.63478981 L44.2833805,26.5700283 C46.1777016,27.7066209 46.7919618,30.163662 45.6553692,32.057983 C45.3175701,32.6209814 44.8463789,33.0921727 44.2833805,33.4299717 Z" id="Triangle-Copy" fill="#35495E" transform="translate(25.000000, 30.000000) rotate(-180.000000) translate(-25.000000, -30.000000) "></path>
<path d="M60.2833805,33.4299717 L22.057983,56.3652102 C20.163662,57.5018028 17.7066209,56.8875426 16.5700283,54.9932215 C16.1970313,54.3715599 16,53.6602143 16,52.9352385 L16,7.06476152 C16,4.85562252 17.790861,3.06476152 20,3.06476152 C20.7249758,3.06476152 21.4363214,3.26179285 22.057983,3.63478981 L60.2833805,26.5700283 C62.1777016,27.7066209 62.7919618,30.163662 61.6553692,32.057983 C61.3175701,32.6209814 60.8463789,33.0921727 60.2833805,33.4299717 Z" id="Triangle" fill="#409EFF"></path>
<path d="M42.4688663,31.7973091 L24.0289915,42.8612339 C23.081831,43.4295303 21.8533105,43.1224001 21.2850141,42.1752396 C21.0985157,41.8644088 21,41.508736 21,41.1462481 L21,19.0183984 C21,17.9138289 21.8954305,17.0183984 23,17.0183984 C23.3624879,17.0183984 23.7181607,17.116914 24.0289915,17.3034125 L42.4688663,28.3673374 C43.4160268,28.9356337 43.7231569,30.1641542 43.1548606,31.1113147 C42.9859611,31.3928139 42.7503655,31.6284096 42.4688663,31.7973091 Z" id="Triangle-Copy" fill="#FFFFFF" transform="translate(31.000000, 30.082670) rotate(-180.000000) translate(-31.000000, -30.082670) "></path>
<path d="M37.5708451,30.8574929 L30.5144958,35.0913025 C30.0409155,35.3754507 29.4266552,35.2218856 29.1425071,34.7483054 C29.0492578,34.59289 29,34.4150536 29,34.2338096 L29,25.7661904 C29,25.2139056 29.4477153,24.7661904 30,24.7661904 C30.1812439,24.7661904 30.3590804,24.8154482 30.5144958,24.9086975 L37.5708451,29.1425071 C38.0444254,29.4266552 38.1979905,30.0409155 37.9138423,30.5144958 C37.8293925,30.6552454 37.7115947,30.7730432 37.5708451,30.8574929 Z" id="Triangle" fill="#409EFF"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<svg viewBox="0 0 60 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>D2Admin</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo-no-shadow" transform="translate(-3.000000, -3.000000)">
<path d="M44.2833805,33.4299717 L6.05798302,56.3652102 C4.16366196,57.5018028 1.70662094,56.8875426 0.570028297,54.9932215 C0.197031333,54.3715599 8.87839274e-17,53.6602143 0,52.9352385 L-4.4408921e-16,7.06476152 C-7.1463071e-16,4.85562252 1.790861,3.06476152 4,3.06476152 C4.72497578,3.06476152 5.43632142,3.26179285 6.05798302,3.63478981 L44.2833805,26.5700283 C46.1777016,27.7066209 46.7919618,30.163662 45.6553692,32.057983 C45.3175701,32.6209814 44.8463789,33.0921727 44.2833805,33.4299717 Z" id="Triangle-Copy" fill="#35495E" transform="translate(25.000000, 30.000000) rotate(-180.000000) translate(-25.000000, -30.000000) "></path>
<path d="M60.2833805,33.4299717 L22.057983,56.3652102 C20.163662,57.5018028 17.7066209,56.8875426 16.5700283,54.9932215 C16.1970313,54.3715599 16,53.6602143 16,52.9352385 L16,7.06476152 C16,4.85562252 17.790861,3.06476152 20,3.06476152 C20.7249758,3.06476152 21.4363214,3.26179285 22.057983,3.63478981 L60.2833805,26.5700283 C62.1777016,27.7066209 62.7919618,30.163662 61.6553692,32.057983 C61.3175701,32.6209814 60.8463789,33.0921727 60.2833805,33.4299717 Z" id="Triangle" fill="#409EFF"></path>
<path d="M42.4688663,31.7973091 L24.0289915,42.8612339 C23.081831,43.4295303 21.8533105,43.1224001 21.2850141,42.1752396 C21.0985157,41.8644088 21,41.508736 21,41.1462481 L21,19.0183984 C21,17.9138289 21.8954305,17.0183984 23,17.0183984 C23.3624879,17.0183984 23.7181607,17.116914 24.0289915,17.3034125 L42.4688663,28.3673374 C43.4160268,28.9356337 43.7231569,30.1641542 43.1548606,31.1113147 C42.9859611,31.3928139 42.7503655,31.6284096 42.4688663,31.7973091 Z" id="Triangle-Copy" fill="#FFFFFF" transform="translate(31.000000, 30.082670) rotate(-180.000000) translate(-31.000000, -30.082670) "></path>
<path d="M37.5708451,30.8574929 L30.5144958,35.0913025 C30.0409155,35.3754507 29.4266552,35.2218856 29.1425071,34.7483054 C29.0492578,34.59289 29,34.4150536 29,34.2338096 L29,25.7661904 C29,25.2139056 29.4477153,24.7661904 30,24.7661904 C30.1812439,24.7661904 30.3590804,24.8154482 30.5144958,24.9086975 L37.5708451,29.1425071 C38.0444254,29.4266552 38.1979905,30.0409155 37.9138423,30.5144958 C37.8293925,30.6552454 37.7115947,30.7730432 37.5708451,30.8574929 Z" id="Triangle" fill="#409EFF"></path>
</g>
</g>
</svg>
\ No newline at end of file
import Vue from 'vue'
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./icons', false, /\.svg$/)
const iconMap = requireAll(req)
Vue.prototype.$IconSvg = iconMap.map(e => e.default.id.slice(3))
<template>
<div>
<el-dialog title=""
:visible.sync="isShowImg"
width="600px"
:append-to-body='true'
:before-close="handleClose">
<div class="box">
<img :src="initContenta"
alt="">
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "",
props: {
isShowImg: {
type: Boolean,
default: false,
},
initContenta: {
type: String,
default: false,
},
},
data() {
return {};
},
methods: {
handleClose() {
console.log("执行了");
this.$emit("noShowImg");
},
},
};
</script>
<style lang="scss" scoped>
.box {
text-align: center;
img {
width: 500px;
height: 500px;
}
.name {
font-size: 22px;
text-align: center;
// font-weight: 500;
// color: #5454fb;
span {
margin-left: 10px;
}
}
.type {
margin-top: 15px;
font-size: 17px;
span {
margin-left: 10px;
}
}
.nameType {
margin-top: 15px;
font-size: 17px;
span {
margin-left: 10px;
}
}
.menuTitle {
margin-top: 15px;
font-size: 17px;
}
.money {
font-size: 15px;
}
.button-box {
display: flex;
margin-left: 350px;
}
.button {
width: 100px;
height: 40px;
// background-color: #03b615;
// color: #ffffff;
padding: 5px 10px;
// margin-left: 120px;
margin-top: 20px;
// margin-left: 450px;
}
}
</style>
<template>
<div>
<el-dialog title="重置密码"
:visible.sync="isModifyPassword"
width="500px"
:before-close="handleClose">
<div class="box">
<el-form :model="form"
label-width="100px"
ref="form">
<el-form-item label="新密码:">
<el-input v-model="form.password"
maxlength="16"
show-word-limit
auto-complete="off"
style="width:200px"></el-input>
</el-form-item>
<el-form-item label="确认密码:">
<el-input v-model="form.confirmPassword"
maxlength="16"
show-word-limit
auto-complete="off"
style="width:200px"></el-input>
</el-form-item>
<div class="zhu">注:密码应为数字+字母混排,字母区分大小写</div>
</el-form>
<div>
<el-button @click="submitAudit"
type="primary"
style="margin-top:30px">提交</el-button>
<el-button @click="handleClose"
class="quxiao">取消</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { getRechargType } from '@/libs/common/constant'
import { selectListALL } from '@/utils/json'
export default {
name: '',
props: {
isModifyPassword: {
type: Boolean,
default: false
}
},
data() {
return {
form: {
password: '',
confirmPassword: ''
}
}
},
watch: {
isModifyPassword(val) {
if (val) {
for (let key in this.form) {
this.form[key] = ''
}
}
}
},
methods: {
selectListALL(myJson) {
return selectListALL(myJson)
},
// 验证密码
validateString(string) {
var pattern = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]+$/
return pattern.test(string)
},
submitAudit() {
if (!this.validateString(this.form.password)) {
this.$message.error('密码格式错误!!!')
return
}
if (this.form.password != this.form.confirmPassword) {
this.$message.error('两次密码输入不正确!!!')
return
}
console.log('这里执行了')
this.$emit('modifyPsw', this.form)
},
handleClose() {
this.$emit('noModify')
},
go() {
this.$emit('goRecharge')
}
}
}
</script>
<style lang="scss" scoped>
.box {
padding-left: 70px;
.titleNmae {
text-align: center;
font-size: 18px;
margin-bottom: 5px;
}
.quxiao {
margin-left: 145px;
}
}
.zhu {
margin-top: 5px;
margin-left: 10px;
color: red;
}
</style>
<template>
<div :class="{ hidden: hidden }" class="pagination-container">
<!-- :background="background" -->
<el-pagination
:layout="layout"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:page-sizes="pageSizes"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:total="total"
/>
</div>
</template>
<script>
// import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',
props: {
formData: {
type: Object
},
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [5, 10, 20, 30, 50]
}
},
// 移动端页码按钮的数量端默认值5
// pagerCount: {
// type: Number,
// default: document.body.clientWidth < 992 ? 5 : 7
// },
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
// background: {
// type: Boolean,
// default: true
// },
// autoScroll: {
// type: Boolean,
// default: true
// },
hidden: {
type: Boolean,
default: false
}
},
// data () {
// return {
// }
// },
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
// if (this.autoScroll) {
// scrollTo(0, 800)
// }
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
// if (this.autoScroll) {
// scrollTo(0, 800)
// }
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>
<!--单图上传组件/按钮-->
<template>
<div :class="fitWidth ? 'upload-container-fit' : 'upload-container'">
<el-upload name="avatar"
action="#"
:http-request="myUpload"
:multiple="false"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:drag="drag"
with-credentials
:class="uploadClass">
<!-- <div v-if="!drag && !preview">
<el-button v-if="imageUrl"
size="small"
type="success">已上传,可点击修改</el-button>
<el-button size="small"
type="primary"
v-else><i class="el-icon-upload el-icon--left"></i>点击上传</el-button>
</div> -->
<div v-if="drag && !preview">
<i class="el-icon-upload"
v-if="!imageUrl" />
<div class="el-upload__text"
v-if="!imageUrl">
<em>点击上传</em>
</div>
<div v-if="drag && imageUrl"
class="el-upload-list el-upload-list--picture-card">
<div class="el-upload-list__item is-success">
<img :src="imageUrl"
alt=""
class="el-upload-list__item-thumbnail" />
<label class="el-upload-list__item-status-label">
<i class="el-icon-upload-success el-icon-check"></i>
</label>
<i class="el-icon-close"></i>
<i class="el-icon-close-tip">按 delete 键可删除</i>
<span class="el-upload-list__item-actions">
<!-- <span class="el-upload-list__item-preview">
<i class="el-icon-zoom-in"
@click.stop="handlePreview"></i>
</span> -->
<span class="el-upload-list__item-delete">
<i class="el-icon-delete"
@click.stop="removeImage"></i>
</span>
</span>
</div>
</div>
<!-- <img width="100%"
height="auto"
v-else
:src="imageUrl"
alt="" /> -->
</div>
</el-upload>
<!-- <el-dialog :visible.sync="dialogVisible"
:append-to-body="true"
:modal-append-to-body="false"
:width="dialogWidth"
class="preview-dialog">
<img width="100%"
:src="imageUrl"
alt="" />
</el-dialog> -->
<!-- <div v-if="!drag && imageUrl"
class="el-upload-list el-upload-list--text">
<div class="el-upload-list__item is-success">
<a class="el-upload-list__item-name"
@click="handlePreview">
<i class="el-icon-picture"></i>点此处预览</a>
<label class="el-upload-list__item-status-label">
<i class="el-icon-upload-success el-icon-circle-check"
v-if="!preview"></i>
</label>
<i class="el-icon-close"
v-if="!preview"
@click="removeImage"></i>
</div>
</div> -->
<div class="el-upload__tip"
slot="tip"
v-if="!preview && isShowTip">
<span v-if="limitWidth > 0 && limitHeight > 0">
注:图片规格{{limitWidth}}*{{limitHeight}},
</span>
<span>
格式限{{limitTypeString}},大小不超过{{limitKbString}}
</span>
</div>
</div>
</template>
<script>
import axios from "axios";
import util from "@/libs/util";
import { uploadImg } from "@/libs/common/constant";
export default {
name: "single-upload",
props: {
warnImageTypeTitle: {
type: String,
default: "",
}, // 上传图片格式提示文本
warnImageType: {
type: Boolean,
default: false,
}, // 是否使用默认上传图片格式提示
value: {
type: String,
default: "",
},
drag: {
type: Boolean,
default: false,
},
preview: {
type: Boolean,
default: false,
},
isShowTip: {
type: Boolean,
default: true,
},
fitWidth: {
type: Boolean,
default: false,
},
imgHidth: {
type: Number,
},
imgWidth: {
type: Number,
},
dialogWidth: {
type: String,
default: "35%",
},
preiview: {
type: Boolean,
default: false,
},
limitTypeList: {
type: Array,
default: () => ["image/jpg", "image/png", "image/jpeg"],
},
limitKb: {
type: Number,
default: 2048,
},
limitWidth: {
type: Number,
default: 0,
},
limitHeight: {
type: Number,
default: 0,
},
},
data() {
return {
dialogVisible: false,
currentSize: 0,
};
},
computed: {
uploadUrl() {
// const url = process.env.VUE_APP_API + 'mainManage/upload/img/one'
return uploadImg.url;
},
imageUrl() {
// return this.value ? process.env.VUE_APP_BASE_API + this.value : ''
return this.value;
},
uploadClass() {
return this.drag ? "image-uploader" : "";
},
// 限制图片
limitTypeString() {
let string = "";
this.limitTypeList.map((item) => {
string += item.replace("image/", "") + "/";
});
return string.substring(0, string.length - 1);
},
limitKbString() {
let string = "";
if (this.limitKb / 1024 >= 1) {
string = this.limitKb / 1024 + "M";
} else {
string = this.limitKb + "KB";
}
return string;
},
},
methods: {
myUpload(params) {
// 构建FormData对象
const data = new FormData();
data.append("file", params.file);
axios({
headers: {
"Content-Type": "multipart/form-data",
token: util.cookies.get("token"),
},
method: "POST",
// url: process.env.VUE_APP_API + 'provider/upload/img/one',
url: uploadImg.url,
data: data,
})
.then((res) => {
console.log(res, "上传了吗");
this.handleUploadSuccess(res);
})
.catch((err) => {
console.log(err);
});
},
removeImage(e) {
this.$confirm("确定移除已上传的图片?", "提示").then(() => {
this.emitInput("");
});
},
emitInput(val) {
console.log(val, "val");
this.$emit("input", val);
},
handleUploadSuccess(response) {
this.emitInput(response.data.data.urlPath);
this.$emit("getCurrentSize", this.currentSize);
},
handleUploadError(err) {
this.$message.error(err.message);
},
handleUploadRemove() {
this.emitInput("");
},
beforeUpload(file) {
const typeList = this.limitTypeList;
const isTypeValid = typeList.includes(file.type);
const isLtKb = file.size / 1024 < this.limitKb;
const limitWidth = this.limitWidth;
const limitHeight = this.limitHeight;
this.currentSize = file.size / 1024;
if (!isTypeValid) {
if (this.warnImageType) {
this.$message.error(this.warnImageTypeTitle);
} else {
const limitTypeList = this.limitTypeList;
const imageFormat = [];
// for (const i in limitTypeList) {
// if (limitTypeList[i] === 'image/jpeg') {
// imageFormat.push('jpeg')
// } else {
// imageFormat.push(limitTypeList[i].substring(6))
// }
// }
for (const i in limitTypeList) {
imageFormat.push(limitTypeList[i].substring(6));
}
const formatWarn = imageFormat.join("/");
this.$message.error(`图片格式只能是 ${formatWarn} `);
}
return false;
}
if (!isLtKb) {
this.$message.error(`图片大小不能超过 ${this.limitKbString}!`);
return false;
}
if (limitWidth === 0 && limitHeight === 0) {
return true;
}
const isSize = new Promise(function (resolve, reject) {
const _URL = window.URL || window.webkitURL;
const img = new Image();
img.onload = function () {
const imgWidth = img.width;
const imgHight = img.height;
const valid =
imgWidth === 0
? true
: imgWidth >= parseInt(limitWidth) &&
limitHeight === 0 &&
imgWidth === imgHight
? true
: imgHight >= parseInt(limitHeight) && imgWidth === imgHight;
valid ? resolve() : reject(new Error());
};
img.src = _URL.createObjectURL(file);
}).then(
() => {
return file;
},
() => {
if (limitWidth > 0 && limitHeight > 0) {
// this.$message.error(
// `图片尺寸不符合${limitWidth}*${limitHeight}标准`
// )
this.$message.error(`图片规格大小不符合要求`);
} else if (limitWidth > 0 && limitHeight === 0) {
// this.$message.error(`图片宽度不符合${limitWidth}标准`)
this.$message.error(`图片规格大小不符合要求`);
} else if (limitWidth === 0 && limitHeight > 0) {
// this.$message.error(`图片高度不符合${limitHeight}标准`)
this.$message.error(`图片规格大小不符合要求`);
}
return Promise.reject(new Error());
}
);
return isSize;
},
handlePreview() {
this.dialogVisible = true;
},
},
};
</script>
<style lang="scss">
.upload-container {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.image-uploader {
width: 150px;
margin-right: 20px;
.el-icon-upload {
margin: 20px 0 16px;
font-size: 60px;
}
.el-upload__text {
line-height: 20px;
font-size: 13px;
}
}
.el-upload-list--text {
margin: 6px 0 0 10px;
}
.image-uploader .el-upload-dragger {
width: 150px;
height: 150px;
}
.el-upload__tip {
margin: 0 0 0 15px;
}
.el-upload-list__item:first-child {
margin: 0;
}
}
.upload-container-fit {
width: 100%;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
img {
display: block;
}
.image-uploader {
width: 100%;
display: contents;
// margin-right: 20px;
.el-upload {
width: 100%;
}
.el-icon-upload {
margin: 0 0 16px;
font-size: 60px;
}
.el-upload__text {
line-height: 20px;
font-size: 13px;
}
}
.el-upload-list--text {
margin: 6px 0 0 10px;
}
.image-uploader .el-upload-dragger {
width: 100%;
height: auto;
}
.el-upload__tip {
margin: 0 0 0 15px;
}
.el-upload-list__item:first-child {
margin: 0;
}
}
.preview-dialog .el-dialog__header {
padding: 0;
}
.preview-dialog .el-dialog__body {
padding: 20px;
}
</style>
<template>
<div class="d2-container-card-bs">
<div v-if="$slots.header" class="d2-container-card-bs__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-card-bs__body" ref="wrapper">
<div class="d2-container-card-bs__body-wrapper-inner">
<div class="d2-container-card-bs__body-card">
<slot/>
</div>
</div>
</div>
<div v-if="$slots.footer" class="d2-container-card-bs__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import bs from './mixins/bs'
export default {
name: 'd2-container-card-bs',
mixins: [
bs
]
}
</script>
<template>
<div class="d2-container-card">
<div v-if="$slots.header" class="d2-container-card__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-card__body" ref="body">
<div class="d2-container-card__body-card">
<slot/>
</div>
</div>
<div v-if="$slots.footer" class="d2-container-card__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-card',
mixins: [
scroll
],
mounted () {
// 增加滚动事件监听
this.addScrollListener()
},
beforeDestroy () {
// 移除滚动事件监听
this.removeScrollListener()
}
}
</script>
<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 class="d2-container-full-bs__body-wrapper-inner">
<slot/>
</div>
</div>
<div v-if="$slots.footer" class="d2-container-full-bs__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import bs from './mixins/bs'
export default {
name: 'd2-container-card-bs',
mixins: [
bs
]
}
</script>
<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" ref="body">
<slot/>
</div>
<div v-if="$slots.footer" class="d2-container-full__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-full',
mixins: [
scroll
],
mounted () {
// 增加滚动事件监听
this.addScrollListener()
},
beforeDestroy () {
// 移除滚动事件监听
this.removeScrollListener()
}
}
</script>
<template>
<div class="d2-container-ghost-bs">
<div v-if="$slots.header" class="d2-container-ghost-bs__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-ghost-bs__body" ref="wrapper">
<!-- https://github.com/d2-projects/d2-admin/issues/181 -->
<div>
<slot/>
</div>
</div>
<div v-if="$slots.footer" class="d2-container-ghost-bs__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import bs from './mixins/bs'
export default {
name: 'd2-container-card-bs',
mixins: [
bs
]
}
</script>
<template>
<div class="d2-container-ghost">
<div v-if="$slots.header" class="d2-container-ghost__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-ghost__body" ref="body">
<slot/>
</div>
<div v-if="$slots.footer" class="d2-container-ghost__footer" ref="footer">
<slot name="footer"/>
</div>
</div>
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-ghost',
mixins: [
scroll
],
mounted () {
// 增加滚动事件监听
this.addScrollListener()
},
beforeDestroy () {
// 移除滚动事件监听
this.removeScrollListener()
}
}
</script>
<template>
<div
v-if="show"
class="d2-source"
:class="{ 'd2-source--active': isActive }"
@click="handleClick">
<d2-icon name="code"/> 本页源码
</div>
</template>
<script>
import { last, get } from 'lodash'
export default {
data () {
return {
isActive: false,
path: ''
}
},
computed: {
show () {
return process.env.VUE_APP_SCOURCE_LINK === 'TRUE'
}
},
watch: {
$route: {
handler (to) {
this.path = get(last(to.matched), 'components.default.__source')
},
immediate: true
}
},
mounted () {
// 一秒后显示按钮
setTimeout(() => {
this.isActive = true
}, 500)
},
methods: {
// 点击按钮的时候跳转到源代码
handleClick () {
this.$open(`${process.env.VUE_APP_REPO}/blob/master/${this.path}`)
}
}
}
</script>
<style lang="scss" scoped>
.d2-source {
$borderRadius: 4px;
$paddingLR: 15px;
$paddingTB: 7px;
$fontSize: 12px;
$rightOuter: $paddingLR / 2;
opacity: 0;
position: fixed;
z-index: 9999;
right: - $borderRadius - $rightOuter;
bottom: 20px;
font-size: $fontSize;
line-height: $fontSize;
font-weight: bold;
border-radius: $borderRadius;
padding: $paddingTB $paddingLR;
padding-right: $borderRadius + $paddingLR;
background-color: rgba(#000, .7);
border: 1px solid #000;
color: #FFF;
transition: all .3s;
@extend %unable-select;
&.d2-source--active {
opacity: 1;
}
&:hover {
right: - $borderRadius;
background-color: rgba(#000, .9);
}
}
</style>
import BScroll from 'better-scroll'
export default {
props: {
// 滚动优化的选项
betterScrollOptions: {
type: Object,
required: false,
default: () => ({})
}
},
data () {
return {
BS: null
}
},
mounted () {
this.scrollInit()
},
beforeDestroy () {
this.scrollDestroy()
},
methods: {
scrollInit () {
// 初始化 bs
this.BS = new BScroll(this.$refs.wrapper, Object.assign({
mouseWheel: true,
click: true,
scrollbar: {
fade: true,
interactive: false
}
}, this.betterScrollOptions))
// 滚动时发出事件 并且统一返回的数据格式
this.BS.on('scroll', ({ x, y }) => this.$emit('scroll', {
x: -x,
y: -y
}))
},
scrollDestroy () {
// https://github.com/d2-projects/d2-admin/issues/75
try {
this.BS.destroy()
} catch (e) {
delete this.BS
this.BS = null
}
},
// 外部调用的方法 返回顶部
scrollToTop () {
if (this.BS) this.BS.scrollTo(0, 0, 300)
},
// 手动发出滚动事件
scroll () {
if (this.BS) {
this.$emit('scroll', {
x: -this.BS.x,
y: -this.BS.y
})
}
}
}
}
// 提供滚动方面的功能
// 非滚动优化模式通用
import { throttle } from 'lodash'
// 生成滚动事件的 handler
function handleMaker (wait) {
return throttle(e => {
this.$emit('scroll', {
x: e.target.scrollLeft,
y: e.target.scrollTop
})
}, wait)
}
export default {
props: {
// 滚动事件节流间隔
scrollDelay: {
type: Number,
required: false,
default: 10
}
},
data () {
return {
handleScroll: null
}
},
watch: {
scrollDelay (val) {
// 移除旧的监听
this.removeScrollListener()
// 生成新的 handle 方法
this.handleScroll = handleMaker.call(this, val)
// 添加新的监听
this.addScrollListener()
}
},
methods: {
// 增加滚动事件监听
addScrollListener () {
if (typeof this.handleScroll !== 'function') {
// mounted 生命周期内调用这个方法的时候会进入这里的判断
this.handleScroll = handleMaker.call(this, this.scrollDelay)
}
// 添加监听
this.$refs.body.addEventListener('scroll', this.handleScroll)
},
// 移除滚动事件监听
removeScrollListener () {
this.$refs.body.removeEventListener('scroll', this.handleScroll)
},
// 外部调用的方法 返回顶部
scrollToTop () {
const smoothscroll = () => {
const body = this.$refs.body
const currentScroll = body.scrollTop
if (currentScroll > 0) {
window.requestAnimationFrame(smoothscroll)
body.scrollTo(0, currentScroll - (currentScroll / 5))
}
}
smoothscroll()
}
}
}
// 组件
import d2ContainerFull from './components/d2-container-full.vue'
import d2ContainerFullBs from './components/d2-container-full-bs.vue'
import d2ContainerGhost from './components/d2-container-ghost.vue'
import d2ContainerGhostBs from './components/d2-container-ghost-bs.vue'
import d2ContainerCard from './components/d2-container-card.vue'
import d2ContainerCardBs from './components/d2-container-card-bs.vue'
import d2Source from './components/d2-source.vue'
export default {
name: 'd2-container',
props: {
// 容器样式
type: {
type: String,
required: false,
default: 'full'
},
// 滚动优化
betterScroll: {
type: Boolean,
required: false,
default: false
}
},
computed: {
// 始终返回渲染组件
component () {
if (this.type === 'card' && !this.betterScroll) return d2ContainerCard
if (this.type === 'card' && this.betterScroll) return d2ContainerCardBs
if (this.type === 'ghost' && !this.betterScroll) return d2ContainerGhost
if (this.type === 'ghost' && this.betterScroll) return d2ContainerGhostBs
if (this.type === 'full' && !this.betterScroll) return d2ContainerFull
if (this.type === 'full' && this.betterScroll) return d2ContainerFullBs
else {
return 'div'
}
}
},
render (h) {
const slots = [
this.$slots.default,
this.$slots.header ? <template slot="header">{ this.$slots.header }</template> : null,
this.$slots.footer ? <template slot="footer">{ this.$slots.footer }</template> : null
]
return <div
ref="container"
class="container-component">
<this.component
ref="component"
{ ...{ attrs: this.$attrs } }
onScroll={ e => this.$emit('scroll', e) }>
{ slots }
</this.component>
<d2Source/>
</div>
},
methods: {
// 返回顶部
scrollToTop () {
this.$refs.component.scrollToTop()
// 如果开启了 better scroll 还需要手动触发一遍 scroll 事件
const bs = this.$refs.component.BS
if (bs) this.$refs.component.scroll()
},
// 用法同原生方法 scrollBy
scrollBy (x = 0, y = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollBy(-x, -y, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollBy(x, y)
}
},
// 用法同原生方法 scrollTo
scrollTo (x = 0, y = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollTo(-x, -y, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollTo(x, y)
}
},
// 用法同原生方法 scrollTop
scrollTop (top = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollTo(bs.x, -top, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollTop = top
}
}
}
}
<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>
/*!
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
This source diff could not be displayed because it is too large. You can view the blob instead.
<template>
<i class="fa" :class="`fa-${name}`" aria-hidden="true"></i>
</template>
<script>
import './font-awesome-4.7.0/css/font-awesome.min.css'
export default {
name: 'd2-icon',
props: {
name: {
type: String,
required: false,
default: 'font-awesome'
}
}
}
</script>
import Vue from 'vue'
import d2Container from './d2-container'
// 注意 有些组件使用异步加载会有影响
Vue.component('d2-container', d2Container)
Vue.component('d2-icon', () => import('./d2-icon'))
Vue.component('d2-icon-svg', () => import('./d2-icon-svg/index.vue'))
// 分页
Vue.component('pagination', () => import('./common/pagination/index.vue'))
// 上传图片
Vue.component('update-image', () => import('./common/single-update/index.vue'))
// 图片
Vue.component('lookImage', () => import('./common/lookImage/index.vue'))
// 修改 账号 密码
Vue.component('modifyPws', () => import('./common/modifyPsw/index.vue'))
\ No newline at end of file
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import util from '@/libs/util'
Vue.use(VueI18n)
function loadLocaleMessages () {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
for (const key of locales.keys()) {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
const localeElementUI = require(`element-ui/lib/locale/lang/${locales(key)._element}`)
messages[locale] = {
...locales(key),
...localeElementUI ? localeElementUI.default : {}
}
}
}
return messages
}
const messages = loadLocaleMessages()
Vue.prototype.$languages = Object.keys(messages).map(langlage => ({
label: messages[langlage]._name,
value: langlage
}))
const i18n = new VueI18n({
locale: util.cookies.get('lang') || process.env.VUE_APP_I18N_LOCALE,
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE,
messages
})
export default i18n
<template>
<div class="d2-contentmenu-list" @click="rowClick">
<div v-for="item in menulist" :key="item.value" :data-value="item.value" class="d2-contentmenu-item" flex="cross:center main:center">
<d2-icon v-if="item.icon" :name="item.icon"/>
<div class="d2-contentmenu-item-title" flex-box="1">
{{item.title}}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'd2-contextmenu-list',
props: {
menulist: {
type: Array,
default: () => []
}
},
methods: {
rowClick (event) {
let target = event.target
while (!target.dataset.value) {
target = target.parentNode
}
this.$emit('rowClick', target.dataset.value)
}
}
}
</script>
<style lang="scss">
.d2-contentmenu-list {
.d2-contentmenu-item {
padding: 8px 20px 8px 15px;
margin: 0;
font-size: 14px;
color: #606266;
cursor: pointer;
&:hover {
background: #ecf5ff;
color: #66b1ff;
}
.d2-contentmenu-item-title {
margin-left: 10px;
}
}
}
</style>
<template>
<div class="d2-contextmenu" v-show="flag" :style="style">
<slot/>
</div>
</template>
<script>
export default {
name: 'd2-contextmenu',
props: {
visible: {
type: Boolean,
default: false
},
x: {
type: Number,
default: 0
},
y: {
type: Number,
default: 0
}
},
computed: {
flag: {
get () {
if (this.visible) {
// 注册全局监听事件 [ 目前只考虑鼠标解除触发 ]
window.addEventListener('mousedown', this.watchContextmenu)
}
return this.visible
},
set (newVal) {
this.$emit('update:visible', newVal)
}
},
style () {
return {
left: this.x + 'px',
top: this.y + 'px',
display: this.visible ? 'block' : 'none '
}
}
},
methods: {
watchContextmenu (event) {
if (!this.$el.contains(event.target) || event.button !== 0) this.flag = false
window.removeEventListener('mousedown', this.watchContextmenu)
}
},
mounted () {
// 将菜单放置到body下
document.querySelector('body').appendChild(this.$el)
}
}
</script>
<style>
.d2-contextmenu {
position: absolute;
padding: 5px 0;
z-index: 2018;
background: #FFF;
border: 1px solid #cfd7e5;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
</style>
<template>
<el-color-picker
class="btn-text can-hover"
:value="value"
:predefine="predefine"
size="mini"
@change="set"/>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'd2-header-color',
data () {
return {
predefine: [
'#ff4500',
'#ff8c00',
'#ffd700',
'#90ee90',
'#00ced1',
'#1e90ff',
'#c71585'
]
}
},
computed: {
...mapState('d2admin/color', [
'value'
])
},
watch: {
value (value) {
this.set(value)
}
},
methods: {
...mapActions('d2admin/color', [
'set'
])
}
}
</script>
<template>
<el-tooltip effect="dark" :content="active ? '退出全屏' : '全屏'" placement="bottom">
<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>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState('d2admin/fullscreen', [
'active'
])
},
methods: {
...mapActions('d2admin/fullscreen', [
'toggle'
])
}
}
</script>
<template>
<el-dropdown placement="bottom" size="small" @command="onChangeLocale">
<el-button class="d2-mr btn-text can-hover" type="text">
<d2-icon name="language" style="font-size: 16px;"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="language in $languages"
:key="language.value"
:command="language.value">
<d2-icon :name="$i18n.locale === language.value ? 'dot-circle-o' : 'circle-o'" class="d2-mr-5"/>
{{ language.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import localeMixin from '@/locales/mixin.js'
export default {
mixins: [
localeMixin
]
}
</script>
<template>
<el-tooltip effect="dark" :content="tooltipContent" placement="bottom">
<el-button class="d2-ml-0 d2-mr btn-text can-hover" type="text" @click="handleClick">
<el-badge v-if="logLength > 0" :max="99" :value="logLengthError" :is-dot="logLengthError === 0">
<d2-icon :name="logLengthError === 0 ? 'dot-circle-o' : 'bug'" style="font-size: 20px"/>
</el-badge>
<d2-icon v-else name="dot-circle-o" style="font-size: 20px"/>
</el-button>
</el-tooltip>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters('d2admin', {
logLength: 'log/length',
logLengthError: 'log/lengthError'
}),
tooltipContent () {
return this.logLength === 0
? '没有日志或异常'
: `${this.logLength} 条日志${this.logLengthError > 0
? ` | 包含 ${this.logLengthError} 个异常`
: ''}`
}
},
methods: {
...mapMutations('d2admin/log', [
'clean'
]),
handleClick () {
this.$router.push({
name: 'log'
})
}
}
}
</script>
<template>
<el-button class="d2-mr btn-text can-hover" type="text" @click="handleClick">
<d2-icon name="search" style="font-size: 18px;"/>
</el-button>
</template>
<script>
export default {
methods: {
handleClick () {
this.$emit('click')
}
}
}
</script>
<template>
<el-dropdown placement="bottom" size="small" @command="handleChange">
<el-button class="d2-mr btn-text can-hover" type="text">
<d2-icon name="font" style="font-size: 16px;"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item in options" :key="item.value" :command="item.value">
<d2-icon :name="iconName(item.value)" class="d2-mr-5"/>{{item.label}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
name: 'd2-header-size',
data () {
return {
options: [
{ label: '默认', value: 'default' },
{ label: '中', value: 'medium' },
{ label: '小', value: 'small' },
{ label: '最小', value: 'mini' }
]
}
},
computed: {
...mapState('d2admin/size', [
'value'
])
},
methods: {
...mapMutations({
pageKeepAliveClean: 'd2admin/page/keepAliveClean'
}),
...mapActions({
sizeSet: 'd2admin/size/set'
}),
handleChange (value) {
this.sizeSet(value)
this.$notify({
title: '提示',
dangerouslyUseHTMLString: true,
message: '已更新页面内 <b>组件</b> 的 <b>默认尺寸</b><br/>例如按钮大小,<b>非字号</b>',
type: 'success'
})
},
iconName (name) {
return name === this.value ? 'dot-circle-o' : 'circle-o'
}
}
}
</script>
<template>
<el-table :data="list" 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})` }"/>
</el-table-column>
<el-table-column prop="address" align="center">
<template slot-scope="scope">
<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>
</el-table>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'd2-theme-list',
data () {
return {
table: {
showHeader: false,
border: true
}
}
},
computed: {
...mapState('d2admin/theme', [
'list',
'activeName'
])
},
methods: {
...mapActions('d2admin/theme', [
'set'
]),
handleSelectTheme (name) {
this.set(name)
}
}
}
</script>
<style lang="scss" scoped>
.theme-preview {
height: 50px;
width: 100px;
border-radius: 4px;
background-size: cover;
border: 1px solid $color-border-1;
}
</style>
<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"
:append-to-body="true">
<d2-theme-list style="margin-top: -25px;"/>
</el-dialog>
</div>
</template>
<script>
import themeList from './components/d2-theme-list'
export default {
components: {
'd2-theme-list': themeList
},
data () {
return {
dialogVisible: false
}
}
}
</script>
<template>
<el-dropdown size="small" class="d2-mr">
<span class="btn-text">{{info.name ? `你好 ${info.name}` : '未登录'}}</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="logOff">
<d2-icon name="power-off" class="d2-mr-5"/>
注销
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState('d2admin/user', [
'info'
])
},
methods: {
...mapActions('d2admin/account', [
'logout'
]),
/**
* @description 登出
*/
logOff () {
this.logout({
confirm: true
})
}
}
}
</script>
/**
* @description 创建菜单
* @param {Function} h createElement
* @param {Object} menu 菜单项
*/
export function elMenuItem (h, menu) {
let icon = null
if (menu.icon) icon = <i class={ `fa fa-${menu.icon}` }/>
else if (menu.iconSvg) icon = <d2-icon-svg name={ menu.iconSvg }/>
else icon = <i class="fa fa-file-o"/>
return <el-menu-item
key={ menu.path }
index={ menu.path }>
{ icon }
<span slot="title">{ menu.title || '未命名菜单' }</span>
</el-menu-item>
}
/**
* @description 创建子菜单
* @param {Function} h createElement
* @param {Object} menu 菜单项
*/
export function elSubmenu (h, menu) {
let icon = null
if (menu.icon) icon = <i slot="title" class={ `fa fa-${menu.icon}` }/>
else if (menu.iconSvg) icon = <d2-icon-svg slot="title" name={ menu.iconSvg }/>
else icon = <i slot="title" class="fa fa-folder-o"/>
return <el-submenu
key={ menu.path }
index={ menu.path }>
{ icon }
<span slot="title">{ menu.title || '未命名菜单' }</span>
{ menu.children.map(child => createMenu.call(this, h, child)) }
</el-submenu>
}
/**
* @description 在组件中调用此方法渲染菜单项目
* @param {Function} h createElement
* @param {Object} menu 菜单项
*/
export function createMenu (h, menu) {
if (menu.children === undefined) return elMenuItem.call(this, h, menu)
return elSubmenu.call(this, h, menu)
}
import { throttle } from 'lodash'
import { mapState } from 'vuex'
import menuMixin from '../mixin/menu'
import { createMenu } from '../libs/util.menu'
export default {
name: 'd2-layout-header-aside-menu-header',
mixins: [
menuMixin
],
render (h) {
return <div
flex="cross:center"
class={ { 'd2-theme-header-menu': true, 'is-scrollable': this.isScroll } }
ref="page">
<div
ref="content"
class="d2-theme-header-menu__content"
flex-box="1"
flex>
<div
class="d2-theme-header-menu__scroll"
flex-box="0"
style={ { transform: `translateX(${this.currentTranslateX}px)` } }
ref="scroll">
<el-menu
mode="horizontal"
defaultActive={ this.active }
onSelect={ this.handleMenuSelect }>
{ this.header.map(menu => createMenu.call(this, h, menu)) }
</el-menu>
</div>
</div>
{
this.isScroll
? [
<div
class="d2-theme-header-menu__prev"
flex="main:center cross:center"
flex-box="0"
onClick={ () => this.scroll('left') }>
<i class="el-icon-arrow-left"></i>
</div>,
<div
class="d2-theme-header-menu__next"
flex="main:center cross:center"
flex-box="0"
onClick={ () => this.scroll('right') }>
<i class="el-icon-arrow-right"></i>
</div>
]
: []
}
</div>
},
computed: {
...mapState('d2admin/menu', [
'header'
])
},
data () {
return {
active: '',
isScroll: false,
scrollWidth: 0,
contentWidth: 0,
currentTranslateX: 0,
throttledCheckScroll: null
}
},
watch: {
'$route.matched': {
handler (val) {
this.active = val[val.length - 1].path
},
immediate: true
}
},
methods: {
scroll (direction) {
if (direction === 'left') {
// 向右滚动
this.currentTranslateX = 0
} else {
// 向左滚动
if (this.contentWidth * 2 - this.currentTranslateX <= this.scrollWidth) {
this.currentTranslateX -= this.contentWidth
} else {
this.currentTranslateX = this.contentWidth - this.scrollWidth
}
}
},
checkScroll () {
let contentWidth = this.$refs.content.clientWidth
let scrollWidth = this.$refs.scroll.clientWidth
if (this.isScroll) {
// 页面依旧允许滚动的情况,需要更新width
if (this.contentWidth - this.scrollWidth === this.currentTranslateX) {
// currentTranslateX 也需要相应变化【在右端到头的情况时】
this.currentTranslateX = contentWidth - scrollWidth
// 快速的滑动依旧存在判断和计算时对应的contentWidth变成正数,所以需要限制一下
if (this.currentTranslateX > 0) {
this.currentTranslateX = 0
}
}
// 更新元素数据
this.contentWidth = contentWidth
this.scrollWidth = scrollWidth
// 判断何时滚动消失: 当scroll > content
if (contentWidth > scrollWidth) {
this.isScroll = false
}
}
// 判断何时滚动出现: 当scroll < content
if (!this.isScroll && contentWidth < scrollWidth) {
this.isScroll = true
// 注意,当isScroll变为true,对应的元素盒子大小会发生变化
this.$nextTick(() => {
contentWidth = this.$refs.content.clientWidth
scrollWidth = this.$refs.scroll.clientWidth
this.contentWidth = contentWidth
this.scrollWidth = scrollWidth
this.currentTranslateX = 0
})
}
}
},
mounted () {
// 初始化判断
// 默认判断父元素和子元素的大小,以确定初始情况是否显示滚动
this.checkScroll()
// 全局窗口变化监听,判断父元素和子元素的大小,从而控制isScroll的开关
this.throttledCheckScroll = throttle(this.checkScroll, 300)
window.addEventListener('resize', this.throttledCheckScroll)
},
beforeDestroy () {
// 取消监听
window.removeEventListener('resize', this.throttledCheckScroll)
}
}
import { mapState } from 'vuex'
import menuMixin from '../mixin/menu'
import { createMenu } from '../libs/util.menu'
import BScroll from 'better-scroll'
export default {
name: 'd2-layout-header-aside-menu-side',
mixins: [
menuMixin
],
render (h) {
return <div class="d2-layout-header-aside-menu-side">
<el-menu
collapse={ this.asideCollapse }
collapseTransition={ this.asideTransition }
uniqueOpened={ true }
defaultActive={ this.$route.fullPath }
ref="menu"
onSelect={ this.handleMenuSelect }>
{ this.aside.map(menu => createMenu.call(this, h, menu)) }
</el-menu>
{
this.aside.length === 0 && !this.asideCollapse
? <div class="d2-layout-header-aside-menu-empty" flex="dir:top main:center cross:center">
<d2-icon name="inbox"></d2-icon>
<span>没有侧栏菜单</span>
</div>
: null
}
</div>
},
data () {
return {
asideHeight: 300,
BS: null
}
},
computed: {
...mapState('d2admin/menu', [
'aside',
'asideCollapse',
'asideTransition'
])
},
watch: {
// 折叠和展开菜单的时候销毁 better scroll
asideCollapse (val) {
this.scrollDestroy()
setTimeout(() => {
this.scrollInit()
}, 500)
}
},
mounted () {
this.scrollInit()
},
beforeDestroy () {
this.scrollDestroy()
},
methods: {
scrollInit () {
this.BS = new BScroll(this.$el, {
mouseWheel: true,
click: true
// 如果你愿意可以打开显示滚动条
// scrollbar: {
// fade: true,
// interactive: false
// }
})
},
scrollDestroy () {
// https://github.com/d2-projects/d2-admin/issues/75
try {
this.BS.destroy()
} catch (e) {
delete this.BS
this.BS = null
}
}
}
}
import util from '@/libs/util.js'
export default {
methods: {
handleMenuSelect (index, indexPath) {
if (/^d2-menu-empty-\d+$/.test(index) || index === undefined) {
this.$message.warning('临时菜单')
} else if (/^https:\/\/|http:\/\//.test(index)) {
util.open(index)
} else {
this.$router.push({
path: index
})
}
}
}
}
<template>
<div class="d2-panel-search-item" :class="hoverMode ? 'can-hover' : ''" flex>
<div class="d2-panel-search-item__icon" flex-box="0">
<div class="d2-panel-search-item__icon-box" flex="main:center cross:center">
<d2-icon v-if="item.icon" :name="item.icon"/>
<d2-icon-svg v-else-if="item.iconSvg" :name="item.iconSvg"/>
<d2-icon v-else name="file-o"/>
</div>
</div>
<div class="d2-panel-search-item__info" flex-box="1" flex="dir:top">
<div class="d2-panel-search-item__info-title" flex-box="1" flex="cross:center">
<span>{{item.title}}</span>
</div>
<div class="d2-panel-search-item__info-fullTitle" flex-box="0">
<span>{{item.fullTitle}}</span>
</div>
<div class="d2-panel-search-item__info-path" flex-box="0">
<span>{{item.path}}</span>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
item: {
default: () => ({})
},
hoverMode: {
default: false
}
}
}
</script>
<style lang="scss" scoped>
.d2-panel-search-item {
height: 64px;
margin: 0px -20px;
&.can-hover {
@extend %unable-select;
margin: 0px;
&:hover {
background-color: #F5F7FA;
.d2-panel-search-item__icon {
.d2-panel-search-item__icon-box {
i {
font-size: 24px;
color: $color-primary;
}
}
}
.d2-panel-search-item__info {
.d2-panel-search-item__info-title {
color: $color-text-main;
}
.d2-panel-search-item__info-fullTitle {
color: $color-text-normal;
}
.d2-panel-search-item__info-path {
color: $color-text-normal;
}
}
}
}
.d2-panel-search-item__icon {
width: 64px;
.d2-panel-search-item__icon-box {
height: 64px;
width: 64px;
border-right: 1px solid $color-border-3;
i {
font-size: 20px;
color: $color-text-sub;
}
svg {
height: 20px;
width: 20px;
}
}
}
.d2-panel-search-item__info {
margin-left: 10px;
.d2-panel-search-item__info-title {
font-size: 16px;
line-height: 16px;
font-weight: bold;
color: $color-text-normal;
}
.d2-panel-search-item__info-fullTitle {
font-size: 10px;
line-height: 14px;
color: $color-text-placehoder;
}
.d2-panel-search-item__info-path {
margin-bottom: 4px;
font-size: 10px;
line-height: 14px;
color: $color-text-placehoder;
}
}
}
</style>
<template>
<div class="panel-search" flex="dir:top">
<div class="panel-search__input-group" flex-box="0" flex="dir:top main:center cross:center" @click.self="handlePanelClick">
<d2-icon-svg class="panel-search__logo" name="d2-admin-text"/>
<el-autocomplete
class="panel-search__input"
ref="input"
v-model="searchText"
suffix-icon="el-icon-search"
placeholder="搜索页面"
:fetch-suggestions="querySearch"
:trigger-on-focus="false"
:clearable="true"
@keydown.esc.native="handleEsc"
@select="handleSelect">
<d2-panel-search-item slot-scope="{ item }" :item="item"/>
</el-autocomplete>
<div class="panel-search__tip">
您可以使用快捷键
<span class="panel-search__key">{{hotkey.open}}</span>
唤醒搜索面板,按
<span class="panel-search__key">{{hotkey.close}}</span>
关闭
</div>
</div>
<div v-if="resultsList.length > 0" class="panel-search__results-group" flex-box="1">
<el-card shadow="never">
<div class="panel-search__results-group-inner">
<d2-panel-search-item
v-for="(item, index) in resultsList"
:key="index"
:item="item"
:hover-mode="true"
@click.native="handleResultsGroupItemClick(item.path)"/>
</div>
</el-card>
</div>
</div>
</template>
<script>
import Fuse from 'fuse.js'
import { mapState } from 'vuex'
import mixin from '../mixin/menu'
export default {
mixins: [
mixin
],
components: {
'd2-panel-search-item': () => import('./components/panel-search-item/index.vue')
},
data () {
return {
searchText: '',
results: []
}
},
computed: {
...mapState('d2admin/search', [
'hotkey',
'pool'
]),
// 这份数据是展示在搜索面板下面的
resultsList () {
return (this.results.length === 0 && this.searchText === '') ? this.pool.map(e => ({
value: e.fullTitle,
...e
})) : this.results
},
// 根据 pool 更新 fuse 实例
fuse () {
return new Fuse(this.pool, {
shouldSort: true,
tokenize: true,
threshold: 0.6,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
'fullTitle',
'path'
]
})
}
},
methods: {
/**
* @description 过滤选项 这个方法在每次输入框的值发生变化时会触发
*/
querySearch (queryString, callback) {
const results = this.fuse.search(queryString).map(e => e.item)
this.results = results
callback(results)
},
/**
* @description 聚焦输入框
*/
focus () {
this.input = ''
setTimeout(() => {
if (this.$refs.input) {
this.$refs.input.focus()
}
// 还原
this.searchText = ''
this.results = []
}, 500)
},
/**
* @description 接收用户在列表中选择项目的事件
*/
handleResultsGroupItemClick (path) {
// 如果用户选择的就是当前页面 就直接关闭搜索面板
if (path === this.$route.path) {
this.handleEsc()
return
}
// 用户选择的是其它页面
this.handleMenuSelect(path)
},
/**
* @description 接收用户在下拉菜单中选中事件
*/
async handleSelect ({ path }) {
// 如果用户选择的就是当前页面 就直接关闭搜索面板
if (path === this.$route.path) {
this.handleEsc()
return
}
// 用户选择的是其它页面
await this.$nextTick()
this.handleMenuSelect(path)
},
/**
* @augments 关闭输入框的下拉菜单
*/
closeSuggestion () {
if (this.$refs.input.activated) {
this.$refs.input.suggestions = []
this.$refs.input.activated = false
}
},
/**
* @augments 接收用户点击空白区域的关闭
*/
handlePanelClick () {
this.handleEsc()
},
/**
* @augments 接收用户触发的关闭
*/
async handleEsc () {
this.closeSuggestion()
await this.$nextTick()
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.panel-search {
margin: 20px;
width: 100%;
.panel-search__input-group {
height: 240px;
.panel-search__logo {
width: 80px;
height: 80px;
margin-bottom: 20px;
}
.panel-search__input {
width: 500px;
}
.panel-search__tip {
@extend %unable-select;
margin-top: 20px;
margin-bottom: 40px;
font-size: 12px;
color: $color-text-sub;
.panel-search__key {
padding: 1px 5px;
margin: 0px 2px;
border-radius: 2px;
background-color: $color-text-normal;
color: $color-bg;
}
}
}
.panel-search__results-group {
overflow: auto;
margin-bottom: -20px;
.panel-search__results-group-inner {
margin: -20px;
}
}
}
</style>
<template>
<div class="d2-multiple-page-control-group" flex>
<div class="d2-multiple-page-control-content" flex-box="1">
<div class="d2-multiple-page-control-content-inner">
<d2-contextmenu
:visible.sync="contextmenuFlag"
:x="contentmenuX"
:y="contentmenuY">
<d2-contextmenu-list
:menulist="tagName === '/index' ? contextmenuListIndex : contextmenuList"
@rowClick="contextmenuClick"/>
</d2-contextmenu>
<el-tabs
class="d2-multiple-page-control d2-multiple-page-sort"
:value="current"
type="card"
@tab-click="handleClick"
@tab-remove="handleTabRemove"
@contextmenu.native="handleContextmenu">
<el-tab-pane
v-for="page in opened"
:key="page.fullPath"
:label="page.meta.title"
:name="page.fullPath"
:closable="isTabClosable(page)"/>
</el-tabs>
</div>
</div>
<div class="d2-multiple-page-control-btn" flex-box="0">
<el-dropdown
size="default"
split-button
@click="closeAll"
@command="command => handleControlItemClick(command)">
<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, mapActions } from 'vuex'
import Sortable from 'sortablejs'
export default {
components: {
D2Contextmenu: () => import('../contextmenu'),
D2ContextmenuList: () => import('../contextmenu/components/contentmenuList')
},
data () {
return {
contextmenuFlag: false,
contentmenuX: 0,
contentmenuY: 0,
contextmenuListIndex: [
{ icon: 'times-circle', title: '关闭全部', value: 'all' }
],
contextmenuList: [
{ icon: 'refresh', title: '刷新', value: 'refresh' },
{ icon: 'arrow-left', title: '关闭左侧', value: 'left' },
{ icon: 'arrow-right', title: '关闭右侧', value: 'right' },
{ icon: 'times', title: '关闭其它', value: 'other' },
{ icon: 'times-circle', title: '关闭全部', value: 'all' }
],
tagName: '/index'
}
},
computed: {
...mapState('d2admin/page', [
'opened',
'current'
])
},
methods: {
...mapActions('d2admin/page', [
'close',
'closeLeft',
'closeRight',
'closeOther',
'closeAll',
'openedSort'
]),
/**
* @description 计算某个标签页是否可关闭
* @param {Object} page 其中一个标签页
*/
isTabClosable (page) {
return page.name !== 'index'
},
/**
* @description 右键菜单功能点击
* @param {Object} event 事件
*/
handleContextmenu (event) {
let target = event.target
// fix https://github.com/d2-projects/d2-admin/issues/54
let flag = false
if (target.className.indexOf('el-tabs__item') > -1) flag = true
else if (target.parentNode.className.indexOf('el-tabs__item') > -1) {
target = target.parentNode
flag = true
}
if (flag) {
event.preventDefault()
event.stopPropagation()
this.contentmenuX = event.clientX
this.contentmenuY = event.clientY
this.tagName = target.getAttribute('aria-controls').slice(5)
this.contextmenuFlag = true
}
},
/**
* @description 右键菜单的 row-click 事件
* @param {String} command 事件类型
*/
contextmenuClick (command) {
this.handleControlItemClick(command, this.tagName)
},
/**
* @description 接收点击关闭控制上选项的事件
* @param {String} command 事件类型
* @param {String} tagName tab 名称
*/
handleControlItemClick (command, tagName = null) {
if (tagName) this.contextmenuFlag = false
const params = { pageSelect: tagName }
switch (command) {
case 'refresh': this.$router.push({ name: 'refresh' }); break
case 'left': this.closeLeft(params); break
case 'right': this.closeRight(params); break
case 'other': this.closeOther(params); break
case 'all': this.closeAll(); break
default: this.$message.error('无效的操作'); break
}
},
/**
* @description 接收点击 tab 标签的事件
* @param {object} tab 标签
* @param {object} event 事件
*/
handleClick (tab, event) {
// 找到点击的页面在 tag 列表里是哪个
const page = this.opened.find(page => page.fullPath === tab.name)
if (page) {
const { name, params, query } = page
this.$router.push({ name, params, query })
}
},
/**
* @description 点击 tab 上的删除按钮触发这里
* @param {String} tagName tab 名称
*/
handleTabRemove (tagName) {
this.close({ tagName })
}
},
mounted () {
const el = document.querySelectorAll('.d2-multiple-page-sort .el-tabs__nav')[0]
Sortable.create(el, {
onEnd: (evt) => {
const { oldIndex, newIndex } = evt
this.openedSort({ oldIndex, newIndex })
}
})
}
}
</script>
import layout from './layout'
export default layout
<template>
<div class="d2-layout-header-aside-group"
:style="styleLayoutMainGroup"
: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"
:style="{ opacity: this.searchActive ? 0.5 : 1 }"
flex-box="0"
flex>
<router-link to="/index"
:class="{ 'logo-group': true, 'logo-transition': asideTransition }"
:style="{ width: asideCollapse ? asideWidthCollapse : asideWidth }"
flex-box="0">
<div class="toggle-title"
v-if="asideCollapse">电动车</div>
<div class="toggle-title"
v-else>电动车后台管理</div>
</router-link>
<div class="toggle-aside-btn"
@click="handleToggleAside"
flex-box="0">
<d2-icon name="bars" />
</div>
<div class="toggle-title">
{{info.companyName}}
</div>
<!-- <d2-menu-header flex-box="1"/> -->
<!-- 顶栏右侧 -->
<div class="d2-header-right"
flex-box="0"
style="margin-left: auto">
<d2-header-user />
</div>
</div>
<!-- 下面 主体 -->
<div class="d2-theme-container"
flex-box="1"
flex>
<!-- 主体 侧边栏 -->
<div flex-box="0"
ref="aside"
:class="{
'd2-theme-container-aside': true,
'd2-theme-container-transition': asideTransition
}"
:style="{
width: asideCollapse ? asideWidthCollapse : asideWidth,
opacity: this.searchActive ? 0.5 : 1
}">
<d2-menu-side />
</div>
<!-- 主体 -->
<div class="d2-theme-container-main"
flex-box="1"
flex>
<!-- 搜索 -->
<transition name="fade-scale">
<div v-if="searchActive"
class="d2-theme-container-main-layer"
flex>
<d2-panel-search ref="panelSearch"
@close="searchPanelClose" />
</div>
</transition>
<!-- 内容 -->
<transition name="fade-scale">
<div v-if="!searchActive"
class="d2-theme-container-main-layer"
flex="dir:top">
<!-- tab -->
<div class="d2-theme-container-main-header"
flex-box="0">
<d2-tabs />
</div>
<!-- 页面 -->
<div class="d2-theme-container-main-body"
flex-box="1">
<transition :name="transitionActive ? 'fade-transverse' : ''">
<keep-alive :include="keepAlive">
<router-view :key="routerViewKey" />
</keep-alive>
</transition>
</div>
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
<script>
import d2MenuSide from "./components/menu-side";
// import d2MenuHeader from './components/menu-header'
import d2Tabs from "./components/tabs";
// import d2HeaderFullscreen from './components/header-fullscreen'
// import d2HeaderLocales from './components/header-locales'
// import d2HeaderSearch from './components/header-search'
// import d2HeaderSize from './components/header-size'
// import d2HeaderTheme from './components/header-theme'
import d2HeaderUser from "./components/header-user";
// import d2HeaderLog from './components/header-log'
// import d2HeaderColor from './components/header-color'
import { mapState, mapGetters, mapActions } from "vuex";
import mixinSearch from "./mixins/search";
export default {
name: "d2-layout-header-aside",
mixins: [mixinSearch],
components: {
d2MenuSide,
// d2MenuHeader,
d2Tabs,
// d2HeaderFullscreen,
// d2HeaderLocales,
// d2HeaderSearch,
// d2HeaderSize,
// d2HeaderTheme,
d2HeaderUser,
// d2HeaderLog,
// d2HeaderColor
},
data() {
return {
// [侧边栏宽度] 正常状态
asideWidth: "200px",
// [侧边栏宽度] 折叠状态
asideWidthCollapse: "65px",
};
},
computed: {
...mapState("d2admin", {
keepAlive: (state) => state.page.keepAlive,
grayActive: (state) => state.gray.active,
transitionActive: (state) => state.transition.active,
asideCollapse: (state) => state.menu.asideCollapse,
asideTransition: (state) => state.menu.asideTransition,
}),
...mapState("d2admin/user", ["info"]),
...mapGetters("d2admin", {
themeActiveSetting: "theme/activeSetting",
}),
/**
* @description 用来实现带参路由的缓存
*/
routerViewKey() {
// 默认情况下 key 类似 __transition-n-/foo
// 这里的字符串操作是为了最终 key 的格式和原来相同 类似 __transition-n-__stamp-time-/foo
const stamp = this.$route.meta[`__stamp-${this.$route.path}`] || "";
return `${stamp ? `__stamp-${stamp}-` : ""}${this.$route.path}`;
},
/**
* @description 最外层容器的背景图片样式
*/
styleLayoutMainGroup() {
return this.themeActiveSetting.backgroundImage
? {
backgroundImage: `url('${this.$baseUrl}${this.themeActiveSetting.backgroundImage}')`,
}
: {};
},
},
methods: {
...mapActions("d2admin/menu", ["asideCollapseToggle"]),
/**
* 接收点击切换侧边栏的按钮
*/
handleToggleAside() {
this.asideCollapseToggle();
},
},
};
</script>
<style lang="scss">
// 注册主题
@import "~@/assets/style/theme/register.scss";
</style>
import { mapState, mapMutations } from 'vuex'
import hotkeys from 'hotkeys-js'
export default {
components: {
'd2-panel-search': () => import('../components/panel-search')
},
mounted () {
// 绑定搜索功能快捷键 [ 打开 ]
hotkeys(this.searchHotkey.open, event => {
event.preventDefault()
this.searchPanelOpen()
})
// 绑定搜索功能快捷键 [ 关闭 ]
hotkeys(this.searchHotkey.close, event => {
event.preventDefault()
this.searchPanelClose()
})
},
beforeDestroy () {
hotkeys.unbind(this.searchHotkey.open)
hotkeys.unbind(this.searchHotkey.close)
},
computed: {
...mapState('d2admin', {
searchActive: state => state.search.active,
searchHotkey: state => state.search.hotkey
})
},
methods: {
...mapMutations({
searchToggle: 'd2admin/search/toggle',
searchSet: 'd2admin/search/set'
}),
/**
* 接收点击搜索按钮
*/
handleSearchClick () {
this.searchToggle()
if (this.searchActive) {
setTimeout(() => {
if (this.$refs.panelSearch) {
this.$refs.panelSearch.focus()
}
}, 500)
}
},
searchPanelOpen () {
if (!this.searchActive) {
this.searchSet(true)
setTimeout(() => {
if (this.$refs.panelSearch) {
this.$refs.panelSearch.focus()
}
}, 500)
}
},
// 关闭搜索面板
searchPanelClose () {
if (this.searchActive) {
this.searchSet(false)
}
}
}
}
export const jdAddressCascader = {
url: '/jdAddress/api/get/one',
url2: '/jdAddress/api/get/two/',
url3: '/jdAddress/api/get/three/',
url4: '/jdAddress/api/get/four/',
title: '地址联动',
name: 'jdAddressCascader',
path: 'request_ui/address/jdAddressCascader',
router: 'jdAddressCascader'
}
// 上传图片
export const uploadImg = {
url: '/api/upload/one/path'
}
// 客户
// 客户列表
export const getCustomerList = {
url: ''
}
// 新增客户
export const addCustomer = {
url: '/api/customer/insert'
}
// 产品
// 产品 列表
export const getProduct = {
url: '/api/product/find/all/page'
}
// 新增 产品
export const addProduct = {
url: '/api/product/insert'
}
// 获取 一条 产品
export const getOneProduct = {
url:'/api/product/get/one/openId/'
}
// 编辑 产品
export const editProduct = {
url:'/api/product/update'
}
// 供应商
// 新增供应商
export const addBusiness = {
url: '/api/company/insert/supplier'
}
// 修改 供应商
export const modiyBusiness = {
url: '/api/company/update/supplier'
}
// 获取 供应商 列表
export const getBusinessList = {
url: '/api/company/get/supplier/all/page'
}
// 获取 供应商 详情
export const getBusiness = {
url: '/api/company/get/supplier/openId/'
}
// 获取 供应商 商家介绍
export const getBusinessInit = {
url: '/api/company/get/supplier/introduce/'
}
// 获取 供应商 宣传图片
export const getBusinessImg = {
url: '/api/company/get/supplier/brochure/image/'
}
// 获取 供应商 套餐订餐截止时间
export const getBusinessSupplierDeadline = {
url: '/api/company/get/supplier/deadline/'
}
// 修改 供应商 密码
export const modifyPws = {
url: '/api/company/update/supplier/password'
}
// 禁用 供应商
export const disableBusiness = {
url: '/api/company/disable/supplier'
}
// 启用 供应商
export const enableBusiness = {
url: '/api/company/enable/supplier'
}
// 常量
// 客户 性质
export const getCustomerNature = {
url: '/constant/get/customer/nature'
}
// 客户 类型
export const getCustomerType = {
url: '/constant/get/customer/type'
}
// 产品 类型
export const getProductType = {
url: '/constant/get/product/type'
}
import Cookies from 'js-cookie'
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 = '', cookieSetting = {}) {
const currentCookieSetting = {
expires: 1
}
Object.assign(currentCookieSetting, cookieSetting)
Cookies.set(`d2admin-${process.env.VUE_APP_VERSION}-${name}`, value, currentCookieSetting)
}
/**
* @description 拿到 cookie 值
* @param {String} name cookie name
*/
cookies.get = function (name = 'default') {
return Cookies.get(`d2admin-${process.env.VUE_APP_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-${process.env.VUE_APP_VERSION}-${name}`)
}
export default cookies
import low from 'lowdb'
import LocalStorage from 'lowdb/adapters/LocalStorage'
import util from '@/libs/util'
import { cloneDeep } from 'lodash'
const adapter = new LocalStorage(`d2admin-${process.env.VUE_APP_VERSION}`)
const db = low(adapter)
db
.defaults({
sys: {},
database: {}
})
.write()
export default db
/**
* @description 检查路径是否存在 不存在的话初始化
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 路径
* @param {Object} payload user {Boolean} 区分用户
* @param {Object} payload validator {Function} 数据校验钩子 返回 true 表示验证通过
* @param {Object} payload defaultValue {*} 初始化默认值
* @returns {String} 可以直接使用的路径
*/
export function pathInit ({
dbName = 'database',
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 !== undefined && validator(value))) {
db
.set(currentPath, defaultValue)
.write()
}
return currentPath
}
/**
* @description 将数据存储到指定位置 | 路径不存在会自动初始化
* @description 效果类似于取值 dbName.path = value
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload value {*} 需要存储的值
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function dbSet ({
dbName = 'database',
path = '',
value = '',
user = false
}) {
db.set(pathInit({
dbName,
path,
user
}), value).write()
}
/**
* @description 获取数据
* @description 效果类似于取值 dbName.path || defaultValue
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload defaultValue {*} 取值失败的默认值
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function dbGet ({
dbName = 'database',
path = '',
defaultValue = '',
user = false
}) {
return cloneDeep(db.get(pathInit({
dbName,
path,
user,
defaultValue
})).value())
}
/**
* @description 获取存储数据库对象
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function database ({
dbName = 'database',
path = '',
user = false,
validator = () => true,
defaultValue = ''
} = {}) {
return db.get(pathInit({
dbName, path, user, validator, defaultValue
}))
}
module.exports = file => require('@/views/' + file).default
module.exports = file => () => import('@/views/' + file)
import cookies from './util.cookies'
import db from './util.db'
import log from './util.log'
const util = {
cookies,
db,
log
}
/**
* @description 更新标题
* @param {String} title 标题
*/
util.title = function (titleText) {
const processTitle = process.env.VUE_APP_TITLE || 'D2Admin'
window.document.title = `${processTitle}${titleText ? ` | ${titleText}` : ''}`
// window.document.title = `${processTitle}`
}
/**
* @description 打开新页面
* @param {String} url 地址
*/
util.open = function (url) {
var a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('target', '_blank')
a.setAttribute('id', 'd2admin-link-temp')
document.body.appendChild(a)
a.click()
document.body.removeChild(document.getElementById('d2admin-link-temp'))
}
export default util
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
{
"_element": "en",
"_name": "English",
"page": {
"demo": {
"playground": {
"locales": {
"text": "D2Admin is a fully open source and free enterprise back-end product front-end integration solution, using the latest front-end technology stack, has prepared most of the project preparations, and with a lot of sample code to help the management system agile development."
}
}
}
}
}
{
"_element": "ja",
"_name": "日本語",
"page": {
"demo": {
"playground": {
"locales": {
"text": "D2Adminは、最新のフロントエンドテクノロジースタックを使用した、完全にオープンソースの無料エンタープライズバックエンド製品フロントエンド統合ソリューションであり、プロジェクトのほとんどの準備を整えており、システムのアジャイル開発の管理に役立つ多くのサンプルコードを備えています。"
}
}
}
}
}
export default {
methods: {
onChangeLocale (command) {
this.$i18n.locale = command
let message = `当前语言:${this.$t('_name')} [ ${this.$i18n.locale} ]`
if (process.env.VUE_APP_BUILD_MODE === 'PREVIEW') {
message = [
`当前语言:${this.$t('_name')} [ ${this.$i18n.locale} ]`,
'仅提供切换功能,没有配置具体的语言数据 ',
'文档参考:<a class="el-link el-link--primary is-underline" target="_blank" href="https://d2.pub/zh/doc/d2-admin/locales">《国际化 | D2Admin》</a>'
].join('<br/>')
}
this.$notify({
title: '语言变更',
dangerouslyUseHTMLString: true,
message
})
}
}
}
{
"_element": "zh-CN",
"_name": "简体中文",
"page": {
"demo": {
"playground": {
"locales": {
"text": "D2Admin 是一个完全 开源免费 的企业中后台产品前端集成方案,使用最新的前端技术栈,已经做好大部分项目前期准备工作,并且带有大量示例代码,助力管理系统敏捷开发。"
}
}
}
}
}
{
"_element": "zh-TW",
"_name": "繁體中文",
"page": {
"demo": {
"playground": {
"locales": {
"text": "D2Admin 是一個完全 開源免費 的企業中後台產品前端集成方案,使用最新的前端技術棧,已經做好大部分項目前期準備工作,並且帶有大量示例代碼,助力管理系統敏捷開發。"
}
}
}
}
}
// Vue
import Vue from 'vue'
import i18n from './i18n'
import App from './App'
// 核心插件
import d2Admin from '@/plugin/d2admin'
// store
import store from '@/store/index'
// 菜单和路由设置
import router from './router'
import { menuHeader, menuAside } from '@/menu'
import { frameInRoutes } from '@/router/routes'
// 引入 公共 css
import './assets/public/public.scss'
// 核心插件
Vue.use(d2Admin)
new Vue({
router,
store,
i18n,
render: h => h(App),
created () {
// 处理路由 得到每一级的路由设置
this.$store.commit('d2admin/page/init', frameInRoutes)
// 设置顶栏菜单
this.$store.commit('d2admin/menu/headerSet', menuHeader)
// 设置侧边栏菜单
this.$store.commit('d2admin/menu/asideSet', menuAside)
// 初始化菜单搜索功能
this.$store.commit('d2admin/search/init', menuHeader)
},
mounted () {
// 展示系统信息
this.$store.commit('d2admin/releases/versionShow')
// 用户登录后从数据库加载一系列的设置
this.$store.dispatch('d2admin/account/load')
// 获取并记录用户 UA
this.$store.commit('d2admin/ua/get')
// 初始化全屏监听
this.$store.dispatch('d2admin/fullscreen/listen')
}
}).$mount('#app')
import { uniqueId } from 'lodash'
/**
* @description 给菜单数据补充上 path 字段
* @description https://github.com/d2-projects/d2-admin/issues/209
* @param {Array} menu 原始的菜单数据
*/
function supplementPath (menu) {
return menu.map(e => ({
...e,
path: e.path || uniqueId('d2-menu-empty-'),
...e.children ? {
children: supplementPath(e.children)
} : {}
}))
}
// 顶部右侧导航
export const menuHeader = supplementPath([
{ path: '/index', title: '首页', icon: 'home' }
// {
// title: '页面',
// icon: 'folder-o',
// children: [
// { path: '/page1', title: '页面 1' },
// { path: '/page2', title: '页面 2' },
// { path: '/page3', title: '页面 3' }
// ]
// }
])
export const menuAside = supplementPath([
{ path: '/index', title: '首页', icon: 'home' },
{ path: '/customerList', title: '客户管理', icon: '' },
{ path: '/productList', title: '产品管理', icon: '' }
])
import api from '@/api'
export default {
install (Vue) {
Vue.prototype.$api = api
}
}
// Element
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// flex 布局库
import 'flex.css'
// 组件
import '@/components'
// svg 图标
import '@/assets/svg-icons'
// 国际化
import i18n from '@/i18n.js'
// 功能插件
import pluginApi from '@/plugin/api'
import pluginError from '@/plugin/error'
import pluginLog from '@/plugin/log'
import pluginOpen from '@/plugin/open'
export default {
async install (Vue, options) {
// 设置为 false 以阻止 vue 在启动时生成生产提示
// https://cn.vuejs.org/v2/api/#productionTip
Vue.config.productionTip = false
// 当前环境
Vue.prototype.$env = process.env.NODE_ENV
// 当前的 baseUrl
Vue.prototype.$baseUrl = process.env.BASE_URL
// 当前版本
Vue.prototype.$version = process.env.VUE_APP_VERSION
// 构建时间
Vue.prototype.$buildTime = process.env.VUE_APP_BUILD_TIME
// Element
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
})
// 插件
Vue.use(pluginApi)
Vue.use(pluginError)
Vue.use(pluginLog)
Vue.use(pluginOpen)
}
}
import { get, isObject } from 'lodash'
import store from '@/store'
import util from '@/libs/util'
export default {
install (Vue, options) {
function writeLog (logType) {
return (error, vm, info = '') => {
Vue.nextTick(() => {
store.dispatch('d2admin/log/push', {
message: `${info}: ${isObject(error) ? error.message : error}`,
type: logType,
meta: {
error,
vm
}
})
if (process.env.NODE_ENV !== 'development') return
util.log.capsule('D2Admin', 'ErrorHandler', logType)
util.log.danger('>>>>>> 错误信息 >>>>>>')
console.log(info)
util.log.danger('>>>>>> Vue 实例 >>>>>>')
console.log(vm)
util.log.danger('>>>>>> Error >>>>>>')
console.log(error)
})
}
}
if (process.env.NODE_ENV === 'development') {
Vue.config.warnHandler = writeLog('warning')
}
Vue.config.errorHandler = writeLog('danger')
window.onunhandledrejection = error => {
store.dispatch('d2admin/log/push', {
message: get(error, 'reason.message', 'Unknown error'),
type: 'danger',
meta: {
error: get(error, 'reason'),
trace: get(error, 'reason.stack')
}
})
}
window.onerror = (event, source, lineno, colno, error) => {
store.dispatch('d2admin/log/push', {
message: get(error, 'message', 'Unknown error'),
type: 'danger',
meta: {
error,
trace: get(error, 'stack'),
source: `${source}@${lineno}:${colno}`,
event: event
}
})
}
}
}
import store from '@/store'
import util from '@/libs/util'
export default {
install (Vue, options) {
// 快速打印 log
Vue.prototype.$log = {
...util.log,
push (data) {
if (typeof data === 'string') {
// 如果传递来的数据是字符串
// 赋值给 message 字段
// 为了方便使用
// eg: this.$log.push('foo text')
store.dispatch('d2admin/log/push', {
message: data
})
} else if (typeof data === 'object') {
// 如果传递来的数据是对象
store.dispatch('d2admin/log/push', data)
}
}
}
}
}
import util from '@/libs/util'
export default {
install (Vue, options) {
Vue.prototype.$open = util.open
}
}
import Vue from 'vue'
import VueRouter from 'vue-router'
// 进度条
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import store from '@/store/index'
import util from '@/libs/util.js'
// 路由数据
import routes from './routes'
// fix vue-router NavigationDuplicated
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
return VueRouterPush.call(this, location).catch(err => err)
}
const VueRouterReplace = VueRouter.prototype.replace
VueRouter.prototype.replace = function replace (location) {
return VueRouterReplace.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
// 导出路由 在 main.js 里使用
const router = new VueRouter({
routes
})
/**
* 路由拦截
* 权限验证
*/
router.beforeEach(async (to, from, next) => {
// 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
await store.dispatch('d2admin/page/isLoaded')
// 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
await store.dispatch('d2admin/size/isLoaded')
// 进度条
NProgress.start()
// 关闭搜索面板
store.commit('d2admin/search/set', false)
// 验证当前路由所有的匹配中是否需要有登录验证的
if (to.matched.some(r => r.meta.auth)) {
// 这里暂时将cookie里是否存有token作为验证是否登录的条件
// 请根据自身业务需要修改
const token = util.cookies.get('token')
if (token && token !== 'undefined') {
next()
} else {
// 没有登录的时候跳转到登录界面
// 携带上登陆成功之后需要跳转的页面完整路径
next({
name: 'login',
query: {
redirect: to.fullPath
}
})
// https://github.com/d2-projects/d2-admin/issues/138
NProgress.done()
}
} else {
// 不需要身份校验 直接通过
next()
}
})
router.afterEach(to => {
// 进度条
NProgress.done()
// 多页控制 打开新的页面
store.dispatch('d2admin/page/open', to)
// 更改标题
util.title(to.meta.title)
})
export default router
import layoutHeaderAside from '@/layout/header-aside'
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
/**
* 在主框架内显示
*/
const frameIn = [
{
path: '/',
redirect: { name: 'index' },
component: layoutHeaderAside,
children: [
// 首页
{
path: 'index',
name: 'index',
meta: {
auth: true
},
component: _import('homePage/index')
},
// 客户
// 客户 列表
{
path: 'customerList',
name: 'customerList',
meta: {
title: '客户列表',
auth: true
},
component: _import('customer/list/index')
},
// 添加 客户
{
path: 'addCustomer',
name: 'addCustomer',
meta: {
title: '添加客户',
auth: true
},
component: _import('customer/add/index')
},
// 产品
// 添加 产品
{
path: 'addProduct',
name: 'addProduct',
meta: {
title: '添加产品',
auth: true
},
component: _import('product/add/index')
},
// 编辑 产品
{
path: 'editProduct/:id',
name: 'editProduct/:id',
meta: {
title: '编辑产品',
auth: true
},
component: _import('product/add/index')
},
// 产品 列表
{
path: 'productList',
name: 'productList',
meta: {
title: '产品列表',
auth: true
},
component: _import('product/list/index')
},
// 商家 列表
{
path: 'businessList',
name: 'businessList',
meta: {
title: '商家列表',
auth: true
},
component: _import('business/list/index')
},
// 添加 商家
{
path: 'addBusiness',
name: 'addBusiness',
meta: {
title: '添加商家',
auth: true
},
component: _import('business/add/index')
},
// 编辑 商家
{
path: 'editBusiness/:id',
name: 'editBusiness/:id',
meta: {
title: '编辑商家',
auth: true
},
component: _import('business/add/index')
},
// 详情
{
path: 'detailsBusiness/:id',
name: 'detailsBusiness/:id',
meta: {
title: '商家信息',
auth: true
},
component: _import('business/details/index')
},
// 基本信息 设置
{
path: 'basicModiy/:id',
name: 'basicModiy/:id',
meta: {
title: '基本信息编辑',
auth: true
},
component: _import('business/details/modiy/basic/index')
},
// 联系信息 设置
{
path: 'contactModiy/:id',
name: 'contactModiy/:id',
meta: {
title: '联系信息编辑',
auth: true
},
component: _import('business/details/modiy/contact/index')
},
// 收款信息 设置
{
path: 'chargeModiy/:id',
name: 'chargeModiy/:id',
meta: {
title: '收款信息编辑',
auth: true
},
component: _import('business/details/modiy/charge/index')
},
// 商家介绍
{
path: 'businessIntroduce/:id',
name: 'businessIntroduce/:id',
meta: {
title: '商家介绍',
auth: true
},
component: _import('business/details/introduce/index')
},
// 刷新页面 必须保留
{
path: 'refresh',
name: 'refresh',
hidden: true,
component: _import('system/function/refresh')
},
// 页面重定向 必须保留
{
path: 'redirect/:route*',
name: 'redirect',
hidden: true,
component: _import('system/function/redirect')
}
]
}
]
/**
* 在主框架之外显示
*/
const frameOut = [
// 登录
{
path: '/login',
name: 'login',
component: _import('system/login')
}
]
/**
* 错误页面
*/
const errorPage = [
{
path: '*',
name: '404',
component: _import('system/error/404')
}
]
// 导出需要显示菜单的
export const frameInRoutes = frameIn
// 重新组织后导出
export default [
...frameIn,
...frameOut,
...errorPage
]
export default {
// 快捷键
// 支持快捷键 例如 ctrl+shift+s
hotkey: {
search: {
open: 's',
close: 'esc'
}
},
// 侧边栏默认配置
menu: {
asideCollapse: false,
asideTransition: true
},
// 在读取持久化数据失败时默认页面
page: {
opened: [
{
name: 'index',
fullPath: '/index',
meta: {
title: '首页',
auth: false
}
}
]
},
// 菜单搜索
search: {
enable: true
},
// 注册的主题
theme: {
list: [
{
title: 'd2admin 经典',
name: 'd2',
preview: 'image/theme/d2/preview@2x.png'
},
{
title: 'Chester',
name: 'chester',
preview: 'image/theme/chester/preview@2x.png'
},
{
title: 'Element',
name: 'element',
preview: 'image/theme/element/preview@2x.png'
},
{
title: '紫罗兰',
name: 'violet',
preview: 'image/theme/violet/preview@2x.png'
},
{
title: '简约线条',
name: 'line',
backgroundImage: 'image/theme/line/bg.jpg',
preview: 'image/theme/line/preview@2x.png'
},
{
title: '流星',
name: 'star',
backgroundImage: 'image/theme/star/bg.jpg',
preview: 'image/theme/star/preview@2x.png'
},
{
title: 'Tomorrow Night Blue (vsCode)',
name: 'tomorrow-night-blue',
preview: 'image/theme/tomorrow-night-blue/preview@2x.png'
}
]
},
// 是否默认开启页面切换动画
transition: {
active: true
}
}
import Vue from 'vue'
import Vuex from 'vuex'
import d2admin from './modules/d2admin'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
d2admin
}
})
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('./modules', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default {
namespaced: true,
modules
}
import { Message, MessageBox } from 'element-ui'
import util from '@/libs/util.js'
import router from '@/router'
import api from '@/api'
export default {
namespaced: true,
actions: {
/**
* @description 登录
* @param {Object} context
* @param {Object} payload username {String} 用户账号
* @param {Object} payload password {String} 密码
* @param {Object} payload route {Object} 登录成功后定向的路由对象 任何 vue-router 支持的格式
*/
async login ({ dispatch }, {
username = '',
password = ''
} = {}) {
const res = await api.SYS_USER_LOGIN({ username, password })
console.log(res,'登录的信息')
// 设置 cookie 一定要存 uuid 和 token 两个 cookie
// 整个系统依赖这两个数据进行校验和存储
// uuid 是用户身份唯一标识 用户注册的时候确定 并且不可改变 不可重复
// token 代表用户当前登录状态 建议在网络请求中携带 token
// 如有必要 token 需要定时更新,默认保存一天
util.cookies.set('uuid', res.uuid)
util.cookies.set('token', res.token)
util.cookies.set('companyName',res.companyName)
// 设置 vuex 用户信息
await dispatch('d2admin/user/set', { name: res.username }, { root: true })
// 用户登录后从持久化数据加载一系列的设置
await dispatch('load')
},
/**
* @description 注销用户并返回登录页面
* @param {Object} context
* @param {Object} payload confirm {Boolean} 是否需要确认
*/
logout ({ commit, dispatch }, { confirm = false } = {}) {
/**
* @description 注销
*/
async function logout () {
// 删除cookie
util.cookies.remove('token')
util.cookies.remove('uuid')
// 清空 vuex 用户信息
await dispatch('d2admin/user/set', {}, { root: true })
// 跳转路由
router.push({ name: 'login' })
}
// 判断是否需要确认
if (confirm) {
commit('d2admin/gray/set', true, { root: true })
MessageBox.confirm('确定要注销当前用户吗', '注销用户', { type: 'warning' })
.then(() => {
commit('d2admin/gray/set', false, { root: true })
logout()
})
.catch(() => {
commit('d2admin/gray/set', false, { root: true })
Message({ message: '取消注销操作' })
})
} else {
logout()
}
},
/**
* @description 用户登录后从持久化数据加载一系列的设置
* @param {Object} context
*/
async load ({ dispatch }) {
// 加载用户名
await dispatch('d2admin/user/load', null, { root: true })
// 加载主题
await dispatch('d2admin/theme/load', null, { root: true })
// 加载页面过渡效果设置
await dispatch('d2admin/transition/load', null, { root: true })
// 持久化数据加载上次退出时的多页列表
await dispatch('d2admin/page/openedLoad', null, { root: true })
// 持久化数据加载侧边栏配置
await dispatch('d2admin/menu/asideLoad', null, { root: true })
// 持久化数据加载全局尺寸
await dispatch('d2admin/size/load', null, { root: true })
// 持久化数据加载颜色设置
await dispatch('d2admin/color/load', null, { root: true })
}
}
}
import { cloneDeep } from 'lodash'
import client from 'webpack-theme-color-replacer/client'
import forElementUI from 'webpack-theme-color-replacer/forElementUI'
export default {
namespaced: true,
state: {
// 颜色
value: process.env.VUE_APP_ELEMENT_COLOR
},
actions: {
/**
* @description 设置颜色
* @param {Object} context
* @param {String} color 尺寸
*/
async set ({ state, dispatch, commit }, color) {
// 记录上个值
const old = state.value
// store 赋值
state.value = color || process.env.VUE_APP_ELEMENT_COLOR
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'color.value',
value: state.value,
user: true
}, { root: true })
// 应用
commit('apply', {
oldColor: old,
newColor: state.value
})
},
/**
* @description 从持久化数据读取颜色设置
* @param {Object} context
*/
async load ({ state, dispatch, commit }) {
// 记录上个值
const old = state.value
// store 赋值
state.value = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'color.value',
defaultValue: process.env.VUE_APP_ELEMENT_COLOR,
user: true
}, { root: true })
// 应用
commit('apply', {
oldColor: old,
newColor: state.value
})
}
},
mutations: {
/**
* @description 将 vuex 中的主题颜色设置应用到系统中
* @param {Object} context
* @param {Object} payload oldColor {String} 旧的颜色
* @param {Object} payload newColor {String} 新颜色
*/
apply (state, { oldColor, newColor }) {
var options = {
oldColors: cloneDeep(forElementUI.getElementUISeries(oldColor)),
newColors: cloneDeep(forElementUI.getElementUISeries(newColor))
}
client.changer.changeColor(options)
}
}
}
import router from '@/router'
import { cloneDeep } from 'lodash'
import { database as getDatabase, dbGet, dbSet } from '@/libs/util.db'
export default {
namespaced: true,
actions: {
/**
* @description 将数据存储到指定位置 | 路径不存在会自动初始化
* @description 效果类似于取值 dbName.path = value
* @param {Object} context
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload value {*} 需要存储的值
* @param {Object} payload user {Boolean} 是否区分用户
*/
set (context, {
dbName = 'database',
path = '',
value = '',
user = false
}) {
dbSet({ dbName, path, value, user })
},
/**
* @description 获取数据
* @description 效果类似于取值 dbName.path || defaultValue
* @param {Object} context
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload defaultValue {*} 取值失败的默认值
* @param {Object} payload user {Boolean} 是否区分用户
*/
get (context, {
dbName = 'database',
path = '',
defaultValue = '',
user = false
}) {
return dbGet({ dbName, path, defaultValue, user })
},
/**
* @description 获取存储数据库对象
* @param {Object} context
* @param {Object} payload user {Boolean} 是否区分用户
*/
database (context, {
user = false
} = {}) {
return getDatabase({
user,
defaultValue: {}
})
},
/**
* @description 清空存储数据库对象
* @param {Object} context
* @param {Object} payload user {Boolean} 是否区分用户
*/
databaseClear (context, {
user = false
} = {}) {
return getDatabase({
user,
validator: () => false,
defaultValue: {}
})
},
/**
* @description 获取存储数据库对象 [ 区分页面 ]
* @param {Object} context
* @param {Object} payload basis {String} 页面区分依据 [ name | path | fullPath ]
* @param {Object} payload user {Boolean} 是否区分用户
*/
databasePage (context, {
basis = 'fullPath',
user = false
} = {}) {
return getDatabase({
path: `$page.${router.app.$route[basis]}`,
user,
defaultValue: {}
})
},
/**
* @description 清空存储数据库对象 [ 区分页面 ]
* @param {Object} context
* @param {Object} payload basis {String} 页面区分依据 [ name | path | fullPath ]
* @param {Object} payload user {Boolean} 是否区分用户
*/
databasePageClear (context, {
basis = 'fullPath',
user = false
} = {}) {
return getDatabase({
path: `$page.${router.app.$route[basis]}`,
user,
validator: () => false,
defaultValue: {}
})
},
/**
* @description 快速将页面当前的数据 ( $data ) 持久化
* @param {Object} context
* @param {Object} payload instance {Object} vue 实例
* @param {Object} payload basis {String} 页面区分依据 [ name | path | fullPath ]
* @param {Object} payload user {Boolean} 是否区分用户
*/
pageSet (context, {
instance,
basis = 'fullPath',
user = false
}) {
return getDatabase({
path: `$page.${router.app.$route[basis]}.$data`,
user,
validator: () => false,
defaultValue: cloneDeep(instance.$data)
})
},
/**
* @description 快速获取页面快速持久化的数据
* @param {Object} context
* @param {Object} payload instance {Object} vue 实例
* @param {Object} payload basis {String} 页面区分依据 [ name | path | fullPath ]
* @param {Object} payload user {Boolean} 是否区分用户
*/
pageGet (context, {
instance,
basis = 'fullPath',
user = false
}) {
return dbGet({
path: `$page.${router.app.$route[basis]}.$data`,
user,
defaultValue: cloneDeep(instance.$data)
})
},
/**
* @description 清空页面快照
* @param {Object} context
* @param {Object} payload basis {String} 页面区分依据 [ name | path | fullPath ]
* @param {Object} payload user {Boolean} 是否区分用户
*/
pageClear (context, {
basis = 'fullPath',
user = false
}) {
return getDatabase({
path: `$page.${router.app.$route[basis]}.$data`,
user,
validator: () => false,
defaultValue: {}
})
}
}
}
import screenfull from 'screenfull'
export default {
namespaced: true,
state: {
// 全屏激活
active: false
},
actions: {
/**
* @description 初始化监听
* @param {Object} context
*/
listen ({ commit }) {
if (screenfull.isEnabled) {
screenfull.on('change', () => {
if (!screenfull.isFullscreen) commit('set', false)
})
}
},
/**
* @description 切换全屏
* @param {Object} context
*/
toggle ({ commit }) {
if (screenfull.isFullscreen) {
screenfull.exit()
commit('set', false)
} else {
screenfull.request()
commit('set', true)
}
}
},
mutations: {
/**
* @description 设置 store 里的全屏状态
* @param {Object} state state
* @param {Boolean} active active
*/
set (state, active) {
state.active = active
}
}
}
export default {
namespaced: true,
state: {
// 灰度
active: false
},
mutations: {
/**
* @description 切换灰度状态
* @param {Object} state state
*/
toggle (state) {
state.active = !state.active
},
/**
* @description 设置灰度模式
* @param {Object} state state
* @param {Boolean} active active
*/
set (state, active) {
state.active = active
}
}
}
import dayjs from 'dayjs'
import { get } from 'lodash'
import util from '@/libs/util.js'
export default {
namespaced: true,
state: {
// 错误日志
// + 日志条目的属性
// - message 必须 日志信息
// - type 非必须 类型 success | warning | info(默认) | danger
// - time 必须 日志记录时间
// - meta 非必须 其它携带信息
log: []
},
getters: {
/**
* @description 返回现存 log (all) 的条数
* @param {*} state vuex state
*/
length (state) {
return state.log.length
},
/**
* @description 返回现存 log (error) 的条数
* @param {*} state vuex state
*/
lengthError (state) {
return state.log.filter(log => log.type === 'danger').length
}
},
actions: {
/**
* @description 添加一个日志
* @param {Object} context
* @param {String} param message {String} 信息
* @param {String} param type {String} 类型
* @param {Object} payload meta {Object} 附带的信息
*/
push ({ rootState, commit }, { message, type = 'info', meta }) {
commit('push', {
message,
type,
time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
meta: {
// 当前用户信息
user: rootState.d2admin.user.info,
// 当前用户的 uuid
uuid: util.cookies.get('uuid'),
// 当前的 token
token: util.cookies.get('token'),
// 当前地址
url: get(window, 'location.href', ''),
// 用户设置
...meta
}
})
}
},
mutations: {
/**
* @description 添加日志
* @param {Object} state state
* @param {Object} log data
*/
push (state, log) {
state.log.push(log)
},
/**
* @description 清空日志
* @param {Object} state state
*/
clean (state) {
// store 赋值
state.log = []
}
}
}
// 设置文件
import setting from '@/setting.js'
export default {
namespaced: true,
state: {
// 顶栏菜单
header: [],
// 侧栏菜单
aside: [],
// 侧边栏收缩
asideCollapse: setting.menu.asideCollapse,
// 侧边栏折叠动画
asideTransition: setting.menu.asideTransition
},
actions: {
/**
* 设置侧边栏展开或者收缩
* @param {Object} context
* @param {Boolean} collapse is collapse
*/
async asideCollapseSet ({ state, dispatch }, collapse) {
// store 赋值
state.asideCollapse = collapse
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'menu.asideCollapse',
value: state.asideCollapse,
user: true
}, { root: true })
},
/**
* 切换侧边栏展开和收缩
* @param {Object} context
*/
async asideCollapseToggle ({ state, dispatch }) {
// store 赋值
state.asideCollapse = !state.asideCollapse
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'menu.asideCollapse',
value: state.asideCollapse,
user: true
}, { root: true })
},
/**
* 设置侧边栏折叠动画
* @param {Object} context
* @param {Boolean} transition is transition
*/
async asideTransitionSet ({ state, dispatch }, transition) {
// store 赋值
state.asideTransition = transition
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'menu.asideTransition',
value: state.asideTransition,
user: true
}, { root: true })
},
/**
* 切换侧边栏折叠动画
* @param {Object} context
*/
async asideTransitionToggle ({ state, dispatch }) {
// store 赋值
state.asideTransition = !state.asideTransition
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'menu.asideTransition',
value: state.asideTransition,
user: true
}, { root: true })
},
/**
* 持久化数据加载侧边栏设置
* @param {Object} context
*/
async asideLoad ({ state, dispatch }) {
// store 赋值
const menu = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'menu',
defaultValue: setting.menu,
user: true
}, { root: true })
state.asideCollapse = menu.asideCollapse !== undefined ? menu.asideCollapse : setting.menu.asideCollapse
state.asideTransition = menu.asideTransition !== undefined ? menu.asideTransition : setting.menu.asideTransition
}
},
mutations: {
/**
* @description 设置顶栏菜单
* @param {Object} state state
* @param {Array} menu menu setting
*/
headerSet (state, menu) {
// store 赋值
state.header = menu
},
/**
* @description 设置侧边栏菜单
* @param {Object} state state
* @param {Array} menu menu setting
*/
asideSet (state, menu) {
// store 赋值
state.aside = menu
}
}
}
import { cloneDeep, uniq, get } from 'lodash'
import router from '@/router'
import setting from '@/setting.js'
// 判定是否需要缓存
const isKeepAlive = data => get(data, 'meta.cache', false)
export default {
namespaced: true,
state: {
// 可以在多页 tab 模式下显示的页面
pool: [],
// 当前显示的多页面列表
opened: get(setting, 'page.opened', []),
// 已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
openedLoaded: false,
// 当前页面
current: '',
// 需要缓存的页面 name
keepAlive: []
},
actions: {
/**
* @description 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
* @param {Object} context
*/
isLoaded ({ state }) {
if (state.openedLoaded) return Promise.resolve()
return new Promise(resolve => {
const timer = setInterval(() => {
if (state.openedLoaded) resolve(clearInterval(timer))
}, 10)
})
},
/**
* @class opened
* @description 从持久化数据载入标签页列表
* @param {Object} context
*/
async openedLoad ({ state, commit, dispatch }) {
// store 赋值
const value = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'page.opened',
defaultValue: setting.page.opened,
user: true
}, { root: true })
// 在处理函数中进行数据优化 过滤掉现在已经失效的页签或者已经改变了信息的页签
// 以 fullPath 字段为准
// 如果页面过多的话可能需要优化算法
// valid 有效列表 1, 1, 0, 1 => 有效, 有效, 失效, 有效
const valid = []
// 处理数据
state.opened = value
.map(opened => {
// 忽略首页
if (opened.fullPath === '/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)
// 标记已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
state.openedLoaded = true
// 根据 opened 数据生成缓存设置
commit('keepAliveRefresh')
},
/**
* 将 opened 属性赋值并持久化 在这之前请先确保已经更新了 state.opened
* @param {Object} context
*/
async opened2db ({ state, dispatch }) {
// 设置数据
dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'page.opened',
value: state.opened,
user: true
}, { root: true })
},
/**
* @class opened
* @description 更新页面列表上的某一项
* @param {Object} context
* @param {Object} payload { index, params, query, fullPath } 路由信息
*/
async openedUpdate ({ state, commit, dispatch }, { index, params, query, fullPath }) {
// 更新页面列表某一项
const page = state.opened[index]
page.params = params || page.params
page.query = query || page.query
page.fullPath = fullPath || page.fullPath
state.opened.splice(index, 1, page)
// 持久化
await dispatch('opened2db')
},
/**
* @class opened
* @description 重排页面列表上的某一项
* @param {Object} context
* @param {Object} payload { oldIndex, newIndex } 位置信息
*/
async openedSort ({ state, commit, dispatch }, { oldIndex, newIndex }) {
// 重排页面列表某一项
const page = state.opened[oldIndex]
state.opened.splice(oldIndex, 1)
state.opened.splice(newIndex, 0, page)
// 持久化
await dispatch('opened2db')
},
/**
* @class opened
* @description 新增一个 tag (打开一个页面)
* @param {Object} context
* @param {Object} payload new tag info
*/
async add ({ state, commit, dispatch }, { tag, params, query, fullPath }) {
// 设置新的 tag 在新打开一个以前没打开过的页面时使用
const newTag = tag
newTag.params = params || newTag.params
newTag.query = query || newTag.query
newTag.fullPath = fullPath || newTag.fullPath
// 添加进当前显示的页面数组
state.opened.push(newTag)
// 如果这个页面需要缓存 将其添加到缓存设置
if (isKeepAlive(newTag)) commit('keepAlivePush', tag.name)
// 持久化
await dispatch('opened2db')
},
/**
* @class current
* @description 打开一个新的页面
* @param {Object} context
* @param {Object} payload 从路由钩子的 to 对象上获取 { name, params, query, fullPath, meta } 路由信息
*/
async open ({ state, commit, dispatch }, { name, params, query, fullPath, meta }) {
// 已经打开的页面
const opened = state.opened
// 判断此页面是否已经打开 并且记录位置
let pageOpendIndex = 0
const pageOpend = opened.find((page, index) => {
const same = page.fullPath === fullPath
pageOpendIndex = same ? index : pageOpendIndex
return same
})
if (pageOpend) {
// 页面以前打开过
await dispatch('openedUpdate', {
index: pageOpendIndex,
params,
query,
fullPath
})
} else {
// 页面以前没有打开过
const page = state.pool.find(t => t.name === name)
// 如果这里没有找到 page 代表这个路由虽然在框架内 但是不参与标签页显示
if (page) {
await dispatch('add', {
tag: Object.assign({}, page),
params,
query,
fullPath
})
}
}
// 如果这个页面需要缓存 将其添加到缓存设置
if (isKeepAlive({ meta })) commit('keepAlivePush', name)
// 设置当前的页面
commit('currentSet', fullPath)
},
/**
* @class opened
* @description 关闭一个 tag (关闭一个页面)
* @param {Object} context
* @param {Object} payload { tagName: 要关闭的标签名字 }
*/
async close ({ state, commit, dispatch }, { tagName }) {
// 预定下个新页面
let newPage = {}
const isCurrent = state.current === tagName
// 如果关闭的页面就是当前显示的页面
if (isCurrent) {
// 去找一个新的页面
const len = state.opened.length
for (let i = 0; i < len; i++) {
if (state.opened[i].fullPath === tagName) {
newPage = i < len - 1 ? state.opened[i + 1] : state.opened[i - 1]
break
}
}
}
// 找到这个页面在已经打开的数据里是第几个
const index = state.opened.findIndex(page => page.fullPath === tagName)
if (index >= 0) {
// 如果这个页面是缓存的页面 将其在缓存设置中删除
commit('keepAliveRemove', state.opened[index].name)
// 更新数据 删除关闭的页面
state.opened.splice(index, 1)
}
// 持久化
await dispatch('opened2db')
// 决定最后停留的页面
if (isCurrent) {
const { name = 'index', params = {}, query = {} } = newPage
const routerObj = { name, params, query }
await router.push(routerObj)
}
},
/**
* @class opened
* @description 关闭当前标签左边的标签
* @param {Object} context
* @param {Object} payload { pageSelect: 当前选中的tagName }
*/
async closeLeft ({ state, commit, dispatch }, { pageSelect } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.fullPath === pageAim) currentIndex = index
})
if (currentIndex > 0) {
// 删除打开的页面 并在缓存设置中删除
for (let i = state.opened.length - 1; i >= 0; i--) {
if (state.opened[i].name === 'index' || i >= currentIndex) {
continue
}
commit('keepAliveRemove', state.opened[i].name)
state.opened.splice(i, 1)
}
}
// 持久化
await dispatch('opened2db')
// 设置当前的页面
state.current = pageAim
if (router.app.$route.fullPath !== pageAim) await router.push(pageAim)
},
/**
* @class opened
* @description 关闭当前标签右边的标签
* @param {Object} context
* @param {Object} payload { pageSelect: 当前选中的tagName }
*/
async closeRight ({ state, commit, dispatch }, { pageSelect } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.fullPath === pageAim) currentIndex = index
})
// 删除打开的页面 并在缓存设置中删除
for (let i = state.opened.length - 1; i >= 0; i--) {
if (state.opened[i].name === 'index' || currentIndex >= i) {
continue
}
commit('keepAliveRemove', state.opened[i].name)
state.opened.splice(i, 1)
}
// 持久化
await dispatch('opened2db')
// 设置当前的页面
state.current = pageAim
if (router.app.$route.fullPath !== pageAim) await router.push(pageAim)
},
/**
* @class opened
* @description 关闭当前激活之外的 tag
* @param {Object} context
* @param {Object} payload { pageSelect: 当前选中的tagName }
*/
async closeOther ({ state, commit, dispatch }, { pageSelect } = {}) {
const pageAim = pageSelect || state.current
let currentIndex = 0
state.opened.forEach((page, index) => {
if (page.fullPath === pageAim) currentIndex = index
})
// 删除打开的页面数据 并更新缓存设置
for (let i = state.opened.length - 1; i >= 0; i--) {
if (state.opened[i].name === 'index' || currentIndex === i) {
continue
}
commit('keepAliveRemove', state.opened[i].name)
state.opened.splice(i, 1)
}
// 持久化
await dispatch('opened2db')
// 设置新的页面
state.current = pageAim
if (router.app.$route.fullPath !== pageAim) await router.push(pageAim)
},
/**
* @class opened
* @description 关闭所有 tag
* @param {Object} context
*/
async closeAll ({ state, commit, dispatch }) {
// 删除打开的页面 并在缓存设置中删除
for (let i = state.opened.length - 1; i >= 0; i--) {
if (state.opened[i].name === 'index') {
continue
}
commit('keepAliveRemove', state.opened[i].name)
state.opened.splice(i, 1)
}
// 持久化
await dispatch('opened2db')
// 关闭所有的标签页后需要判断一次现在是不是在首页
if (router.app.$route.name !== 'index') {
await router.push({ name: 'index' })
}
}
},
mutations: {
/**
* @class keepAlive
* @description 从已经打开的页面记录中更新需要缓存的页面记录
* @param {Object} state state
*/
keepAliveRefresh (state) {
state.keepAlive = state.opened.filter(item => isKeepAlive(item)).map(e => e.name)
},
/**
* @description 删除一个页面的缓存设置
* @param {Object} state state
* @param {String} name name
*/
keepAliveRemove (state, name) {
const list = cloneDeep(state.keepAlive)
const index = list.findIndex(item => item === name)
if (index !== -1) {
list.splice(index, 1)
state.keepAlive = list
}
},
/**
* @description 增加一个页面的缓存设置
* @param {Object} state state
* @param {String} name name
*/
keepAlivePush (state, name) {
const keep = cloneDeep(state.keepAlive)
keep.push(name)
state.keepAlive = uniq(keep)
},
/**
* @description 清空页面缓存设置
* @param {Object} state state
*/
keepAliveClean (state) {
state.keepAlive = []
},
/**
* @class current
* @description 设置当前激活的页面 fullPath
* @param {Object} state state
* @param {String} fullPath new fullPath
*/
currentSet (state, fullPath) {
state.current = fullPath
},
/**
* @class pool
* @description 保存 pool (候选池)
* @param {Object} state state
* @param {Array} routes routes
*/
init (state, routes) {
const pool = []
const push = function (routes) {
routes.forEach(route => {
if (route.children && route.children.length > 0) {
push(route.children)
} else {
if (!route.hidden) {
const { meta, name, path } = route
pool.push({ meta, name, path })
}
}
})
}
push(routes)
state.pool = pool
}
}
}
import util from '@/libs/util.js'
export default {
namespaced: true,
mutations: {
/**
* @description 显示版本信息
* @param {Object} state state
*/
versionShow () {
util.log.capsule('D2Admin', `v${process.env.VUE_APP_VERSION}`)
console.log('D2 Admin https://github.com/d2-projects/d2-admin')
console.log('D2 Crud https://github.com/d2-projects/d2-crud')
console.log('Document https://d2.pub/zh/doc/d2-admin')
console.log('请不要吝啬您的 star,谢谢 ~')
}
}
}
import setting from '@/setting.js'
export default {
namespaced: true,
state: {
// 搜索面板激活状态
active: false,
// 快捷键
hotkey: {
open: setting.hotkey.search.open,
close: setting.hotkey.search.close
},
// 所有可以搜索的页面
pool: []
},
mutations: {
/**
* @description 切换激活状态
* @param {Object} state state
*/
toggle (state) {
state.active = !state.active
},
/**
* @description 设置激活模式
* @param {Object} state state
* @param {Boolean} active active
*/
set (state, active) {
state.active = active
},
/**
* @description 初始化
* @param {Object} state state
* @param {Array} menu menu
*/
init (state, menu) {
const pool = []
const push = function (menu, titlePrefix = []) {
menu.forEach(m => {
if (m.children) {
push(m.children, [...titlePrefix, m.title])
} else {
pool.push({
...m,
fullTitle: [...titlePrefix, m.title].join(' / ')
})
}
})
}
push(menu)
state.pool = pool
}
}
}
import Vue from 'vue'
import router from '@/router'
export default {
namespaced: true,
state: {
// 尺寸
value: '' // medium small mini
},
actions: {
/**
* @description 将当前的设置应用到 element
* @param {Object} context
* @param {Boolean} refresh 是否在设置之后刷新页面
*/
apply ({ state, commit }, refresh) {
Vue.prototype.$ELEMENT.size = state.value
if (refresh) {
commit('d2admin/page/keepAliveClean', null, { root: true })
router.replace('/refresh')
}
},
/**
* @description 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
* @param {Object} context
*/
isLoaded ({ state }) {
if (state.value) return Promise.resolve()
return new Promise(resolve => {
const timer = setInterval(() => {
if (state.value) resolve(clearInterval(timer))
}, 10)
})
},
/**
* @description 设置尺寸
* @param {Object} context
* @param {String} size 尺寸
*/
async set ({ state, dispatch }, size) {
// store 赋值
state.value = size
// 应用
dispatch('apply', true)
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'size.value',
value: state.value,
user: true
}, { root: true })
},
/**
* @description 从持久化数据读取尺寸设置
* @param {Object} context
*/
async load ({ state, dispatch }) {
// store 赋值
state.value = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'size.value',
defaultValue: 'default',
user: true
}, { root: true })
// 应用
dispatch('apply')
}
}
}
import { get } from 'lodash'
import setting from '@/setting.js'
export default {
namespaced: true,
state: {
// 主题
list: get(setting, 'theme.list', []),
// 现在激活的主题 这应该是一个名字 不是对象
activeName: get(setting, 'theme.list[0].name', 'd2')
},
getters: {
/**
* @description 返回当前的主题信息 不是一个名字 而是当前激活主题的所有数据
* @param {Object} state state
*/
activeSetting (state) {
return state.list.find(theme => theme.name === state.activeName)
}
},
actions: {
/**
* @description 激活一个主题
* @param {String} themeValue 需要激活的主题名称
*/
async set ({ state, commit, dispatch }, themeName) {
// 检查这个主题在主题列表里是否存在
state.activeName = state.list.find(e => e.name === themeName) ? themeName : state.list[0].name
// 将 vuex 中的主题应用到 dom
commit('dom')
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'theme.activeName',
value: state.activeName,
user: true
}, { root: true })
},
/**
* @description 从持久化数据加载主题设置 * @param {Object} context
*/
async load ({ state, commit, dispatch }) {
// store 赋值
const activeName = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'theme.activeName',
defaultValue: state.list[0].name,
user: true
}, { root: true })
// 检查这个主题在主题列表里是否存在
if (state.list.find(e => e.name === activeName)) {
state.activeName = activeName
} else {
state.activeName = state.list[0].name
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'theme.activeName',
value: state.activeName,
user: true
}, { root: true })
}
// 将 vuex 中的主题应用到 dom
commit('dom')
}
},
mutations: {
/**
* @description 将 vuex 中的主题应用到 dom
* @param {Object} state state
*/
dom (state) {
document.body.className = `theme-${state.activeName}`
}
}
}
// 设置文件
import setting from '@/setting.js'
export default {
namespaced: true,
state: {
// 是否开启页面过度动画
active: setting.transition.active
},
actions: {
/**
* @description 设置开启状态
* @param {Object} context
* @param {Boolean} active 新的状态
*/
async set ({ state, dispatch }, active) {
// store 赋值
state.active = active
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'transition.active',
value: state.active,
user: true
}, { root: true })
},
/**
* 从数据库读取页面过渡动画设置
* @param {Object} context
*/
async load ({ state, dispatch }) {
// store 赋值
state.active = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'transition.active',
defaultValue: setting.transition.active,
user: true
}, { root: true })
}
}
}
import UaParser from 'ua-parser-js'
export default {
namespaced: true,
state: {
// 用户 UA
data: {}
},
mutations: {
/**
* @description 记录 UA
* @param {Object} state state
*/
get (state) {
state.data = new UaParser().getResult()
}
}
}
export default {
namespaced: true,
state: {
// 用户信息
info: {}
},
actions: {
/**
* @description 设置用户数据
* @param {Object} context
* @param {*} info info
*/
async set ({ state, dispatch }, info) {
// store 赋值
state.info = info
// 持久化
await dispatch('d2admin/db/set', {
dbName: 'sys',
path: 'user.info',
value: info,
user: true
}, { root: true })
},
/**
* @description 从数据库取用户数据
* @param {Object} context
*/
async load ({ state, dispatch }) {
// store 赋值
state.info = await dispatch('d2admin/db/get', {
dbName: 'sys',
path: 'user.info',
defaultValue: {},
user: true
}, { root: true })
}
}
}
function selectListALL (myJson, isAddDefault = false) {
const myArray = []
if (isAddDefault) {
const defaultItem = {
value: undefined,
label: '未选择'
}
myArray.push(defaultItem)
}
let q_value = undefined
let q_label = ''
for (const key in myJson) {
const content = myJson[key]
if (content == '其它' || content == '全时段') {
q_value = Number(key)
q_label = content
} else {
const item = {
value: undefined,
label: '未选择'
}
item.value = Number(key)
item.label = content
myArray.push(item)
}
}
if (q_value != undefined) {
if (q_label != '') {
const item = {
value: q_value,
label: q_label
}
myArray.push(item)
}
}
var item = {
label: '全部',
value: ''
}
myArray.unshift(item)
return myArray
}
function selectList (myJson, isAddDefault = false) {
const myArray = []
// if (isAddDefault) {
// const defaultItem = {
// value: undefined,
// label: '未选择'
// }
// myArray.push(defaultItem)
// }
for (const key in myJson) {
const content = myJson[key]
const item = {
value: undefined,
label: '未选择'
}
item.value = Number(key)
item.label = content
myArray.push(item)
}
// var item = {
// label: '全部',
// value: ''
// }
// myArray.unshift(item)
return myArray
}
const weekList = [
{
value: '一',
label: '周一'
},
{
value: '二',
label: '周二'
},
{
value: '三',
label: '周三'
},
{
value: '四',
label: '周四'
},
{
value: '五',
label: '周五'
},
{
value: '六',
label: '周六'
},
{
value: '日',
label: '周日'
}
]
function generateTiemList () {
var arr = []
for (var i = 1; i < 25; i++) {
var data = {}
if (i < 10) {
data.value = '0' + i
data.label = '0' + i + ':00'
} else {
data.value = String(i)
data.label = '' + i + ':00'
}
arr.push(data)
}
return arr
}
export {
generateTiemList,
selectListALL,
selectList,
weekList
}
\ No newline at end of file
// import api from '@/api'
export function debounce (fn, delay) {
let timer
return function () {
const args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
timer = null
fn.apply(this, args)
}, delay)
}
}
export function throttle (fn, delay) {
let prev = Date.now()
return function () {
const now = Date.now()
if (now - prev > delay) {
fn()
prev = Date.now()
}
}
}
// export function getOlderType() {
// const res = api.GET_OLDER_TYPE()
// console.log(res)
// }
export function getCurrentDate () {
const d = new Date()
let str = ''
str += d.getFullYear() + '-' // 获取当前年份
str += d.getMonth() + 1 + '-' // 获取当前月份(0——11)
str += d.getDate() + ' '
str += d.getHours() + ':'
str += d.getMinutes() + ':'
str += d.getSeconds() + ':'
return str
}
export function filterParams (obj) {
const _newPar = {}
for (const key in obj) {
// 如果对象属性的值不为空,就保存该属性(这里我做了限制,如果属性的值为0,保存该属性。如果属性的值全部是空格,属于为空。)
if ((obj[key] === 0 || obj[key]) && obj[key].toString().replace(/(^\s*)|(\s*$)/g, '') !== '') {
// 记录属性
_newPar[key] = obj[key]
}
}
// 返回对象
return _newPar
}
\ No newline at end of file
<template>
<d2-container v-loading="isLoading">
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="商家名称:"
prop="companyName">
<el-input v-model="form.companyName"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="机构编码:">
<el-input v-model="form.companyCode"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="商家性质:"
prop="nature">
<el-select v-model="form.nature"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in businessNatureList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商家类型:"
prop="businessType">
<el-select v-model="form.businessType"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in businessTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="所在地区:"
prop="a">
<el-row :gutter="10"
style="width: 800px">
<el-col :span="5">
<el-form-item label=""
prop="provinceId">
<el-select v-model="form.provinceId"
:loading="isLoadProvinceId"
loading-text="信息加载中"
@change="provinceIdChange"
placeholder="选择省"
style="margin-left: -7px">
<el-option v-for="item in provinceIdList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label=""
prop="cityId">
<el-select v-model="form.cityId"
@change="cityIdChange"
placeholder="选择市">
<el-option v-for="item in cityIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label=""
prop="countyId"
placeholder="选择区/县">
<el-select v-model="form.countyId"
@change="fourChange">
<el-option v-for="item in countyIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5"
v-if="isFourChage">
<el-form-item label=""
prop="countyId"
placeholder="选择乡镇">
<el-select v-model="form.townId">
<el-option v-for="item in fourIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="详细地址:"
prop="addrDetail">
<el-input v-model="form.addrDetail"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="营业时间:"
prop="a"
style="width: 600px">
<el-col :span="11">
<el-select v-model="form.startWeek"
placeholder="请选择">
<el-option v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="2"
class="line"></el-col>
<el-col :span="11">
<el-select v-model="form.endWeek"
placeholder="请选择">
<el-option v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item label=""
prop="name"
style="width: 600px">
<el-col :span="11">
<el-select v-model="form.startTime"
placeholder="请选择">
<el-option v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="2"
class="line">-</el-col>
<el-col :span="11">
<el-select v-model="form.endTime"
placeholder="请选择">
<el-option v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item label="负责人:"
prop="contactName">
<el-input v-model="form.contactName"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系电话:"
prop="contactPhone">
<el-input v-model="form.contactPhone"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系邮箱:"
prop="email">
<el-input v-model="form.email"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
<div class="zhu">
注:此邮箱后续会作为接收电子账单使用,请认真填写
</div>
</el-form-item>
<el-form-item label="有效期:"
prop="value1">
<el-date-picker v-model="form.value1"
type="date"
:picker-options="pickerOptions"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
<el-form-item label="账户名称:"
prop="loginName">
<el-input v-model="form.loginName"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="登录密码:"
prop="loginPwd">
<el-input v-model="form.loginPwd"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
<div class="zhu">注:密码应为数字+字母混排,字母区分大小写</div>
</el-form-item>
<el-form-item label="营业执照:">
<update-image v-model="form.businessLicenseImageList"
:limitKb="2048"
:drag="true"
:isShowTip="false">
</update-image>
<div class="zhu">注:图片格式为png、jpg,大小不超过2MB</div>
</el-form-item>
<el-form-item label="支付费率:"
prop="payRate">
<el-input placeholder="请填写"
type="number"
:step="0.1"
v-model="form.payRate"
max="99.9"
min="0.1"
class="filter-item-same-width">
<template slot="append">%</template>
</el-input>
<div class="zhu">
注:费率设置单位为百分数,最小基本单位为0.1,有效数据0.1-99.9之间
</div>
</el-form-item>
<el-form-item label="服务费率:"
prop="serviceRate">
<el-input placeholder="请填写"
type="number"
:step="0.1"
v-model="form.serviceRate"
max="99.9"
min="0.1"
class="filter-item-same-width">
<template slot="append">%</template>
</el-input>
<div class="zhu">
注:费率设置单位为百分数,最小基本单位为0.1,有效数据0.1-99.9之间
</div>
</el-form-item>
<el-form-item label="微信商户号:"
prop="merchantId">
<el-input placeholder="请填写"
v-model="form.merchantId"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width">
</el-input>
</el-form-item>
<el-form-item label="商户号名称:"
prop="">
<el-input placeholder="请填写"
v-model="form.merchantName"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width">
</el-input>
<div class="zhu">
注:收款账户为平台服务商下申请的支付商户号,请谨慎填写!!
</div>
</el-form-item>
<!-- <el-form-item label="测试">
<el-button @click="ls()">测试</el-button>
</el-form-item> -->
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from "vuex";
import {
getNature,
getBusinessType,
jdAddressCascader,
addBusiness,
getBusiness,
} from "@/libs/common/constant";
import { selectList, generateTiemList, weekList } from "@/utils/json";
import dayjs from "dayjs";
export default {
data() {
return {
isLoading: false,
form: {
companyName: "", // 商家名称
companyCode: "", // 机构编码
nature: "", // 商家性质
businessType: "", // 商家类型
provinceId: "",
cityId: "",
countyId: "",
townId: "0",
addrDetail: "", // 详细地址
contactName: "", // 公司联系人
contactPhone: "", // 联系人电话
email: "", // 联系邮箱
loginName: "", // 账户名
loginPwd: "", // 账户密码
merchantId: "", // 商户号
merchantName: "", // 商户号名称
value1: "",
validUntil: "", // 有效期
startWeek: "",
endWeek: "",
startTime: "",
endTime: "",
businessHours: "",
businessLicenseImageList: "", // 营业执照图片
payRate: "", // 支付费率
serviceRate: "", // 服务费率
a: "1111",
},
// 商家性质
businessNatureList: [],
// 商家 类型
businessTypeList: [],
// 地址
isLoadProvinceId: true,
// 一级
provinceIdList: [],
// 二级
cityIdList: [],
// 三级
countyIdList: [],
// 四级
fourIdList: [],
// 四级地址是否显示
isFourChage: false,
weekList: [],
timeList: [],
startWeek: "",
endWeek: "",
startTime: "",
endTime: "",
// 禁用今天之前的日期
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now();
},
},
ifSubmit: false,
// 编辑
editId: this.$route.params.id,
rules: {
companyName: [{ required: true, message: "请填写", trigger: "blur" }],
contactName: [{ required: true, message: "请填写", trigger: "blur" }],
contactPhone: [{ required: true, message: "请填写", trigger: "blur" }],
email: [{ required: true, message: "请填写", trigger: "blur" }],
value1: [{ required: true, message: "请填写", trigger: "blur" }],
loginName: [{ required: true, message: "请填写", trigger: "blur" }],
loginPwd: [{ required: true, message: "请填写", trigger: "blur" }],
payRate: [{ required: true, message: "请填写", trigger: "blur" }],
serviceRate: [{ required: true, message: "请填写", trigger: "blur" }],
merchantId: [{ required: true, message: "请填写", trigger: "blur" }],
nature: [{ required: true, message: "请选择", trigger: "change" }],
businessType: [
{ required: true, message: "请选择", trigger: "change" },
],
a: [{ required: true, message: "请填写", trigger: "blur" }],
provinceId: [{ required: true, message: "请选择", trigger: "change" }],
cityId: [{ required: true, message: "请选择", trigger: "change" }],
countyId: [{ required: true, message: "请选择", trigger: "change" }],
addrDetail: [{ required: true, message: "请填写", trigger: "blur" }],
},
};
},
async created() {
// 获取 字典 数据
await this.getConstant();
if (this.editId) {
// 传过来 openid 获取 详情
this.isLoading = true;
await this.getBusiness(this.editId);
}
},
computed: {
// 获取 此页面 路径
...mapState("d2admin/page", ["current"]),
},
methods: {
...mapActions("d2admin/page", ["close", "add"]),
// 获取 字典数据
async getConstant() {
await this.getNatureList();
await this.getBusinessTypeList();
this.provinceIdList = await this.getProvinceList();
this.timeList = await this.generateTiemList();
this.weekList = await this.weekLista();
},
selectList(myJson, a) {
return selectList(myJson, a);
},
weekLista() {
return weekList;
},
generateTiemList() {
return generateTiemList();
},
// 获取 商家性质
async getNatureList() {
const res = await this.$api.GET(`${getNature.url}`);
this.businessNatureList = this.selectList(res, true);
},
// 获取 商家类型
async getBusinessTypeList() {
const res = await this.$api.GET(`${getBusinessType.url}`);
this.businessTypeList = this.selectList(res);
},
// 获取 一级地址
async getProvinceList() {
const res = await this.$api.GET(`${jdAddressCascader.url}`);
this.isLoadProvinceId = false;
return res;
},
// 获取 二级 地址
async getCityList(provinceId) {
const res = await this.$api.GET(`${jdAddressCascader.url2}${provinceId}`);
return res;
},
// 获取 三级 地址
async getCountyId(cityId) {
const res = await this.$api.GET(`${jdAddressCascader.url3}${cityId}`);
return res;
},
// 获取 四级 地址
async getfourList(countyId) {
const res = await this.$api.GET(`${jdAddressCascader.url4}${countyId}`);
console.log(res, "我被调用了");
return res;
},
// 二级地址赋值
async provinceIdChange() {
this.cityIdList = await this.getCityList(this.form.provinceId);
this.form.cityId = "";
this.form.countyId = "";
},
// 三级 地址 赋值
async cityIdChange() {
this.countyIdList = await this.getCountyId(this.form.cityId);
this.form.countyId = "";
},
// 四级 地址 赋值
async fourChange() {
this.fourIdList = await this.getfourList(this.form.countyId);
if (this.fourIdList.length > 0) {
this.isFourChage = true;
this.form.townId = "";
} else {
this.isFourChage = false;
this.form.townId = 0;
}
},
async cancel() {
var tagName = this.current;
await this.close({ tagName });
this.$router.push("/businessList");
},
ls() {
var data = {
companyName: "12315z",
provinceId: "1",
cityId: "72",
countyId: "2799",
townId: "0",
contactName: "124214z1",
contactPhone: "15673701001",
loginName: "zzx131",
loginPwd: "123456z",
merchantId: "1659484214",
merchantName: "大学城餐厅12334",
nature: "1",
businessType: "1",
validUntil: "2025-01-15 00:00:00",
payRate: "3",
serviceRate: "3",
};
this.addBusiness(data);
},
validatePhoneNumber(phoneNumber) {
var pattern = /^1[3456789]\d{9}$/;
return pattern.test(phoneNumber);
},
submit(detail) {
if (!this.validatePhoneNumber(this.form.contactPhone)) {
this.$message.error("请输入正确的手机号!!!");
return;
}
if (this.ifSubmit) {
return;
}
this.ifSubmit = true;
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
var data = await this.arrangeData();
// 判断 参数 确定是修改还是 添加
if (this.editId) {
console.log(data, "编辑提交的参数");
// 提交接口
this.form.openId = this.editId;
const res = await this.$api.POST(`${addBusiness.url}`, data);
} else {
console.log(data, "添加提交的参数");
// 提交 接口
await this.addBusiness(data);
}
await this.cancel();
this.$message({
type: "success",
message: "操作成功!",
});
} catch (error) {}
} else {
console.log("error submit!!");
this.ifSubmit = false;
return false;
}
});
},
// 整理数据
arrangeData() {
this.form.businessHours = `周${this.form.startWeek}至周${this.form.endWeek},${this.form.startTime}:00至${this.form.endTime}:00`;
this.form.validUntil = dayjs(this.form.value1).format(
"YYYY-MM-DD 00:00:00"
);
var list = [];
if (this.form.businessLicenseImageList) {
list[0] = this.form.businessLicenseImageList;
}
var data = {
companyName: this.form.companyName,
provinceId: this.form.provinceId,
cityId: this.form.cityId,
countyId: this.form.countyId,
townId: this.form.townId,
contactName: this.form.contactName,
contactPhone: this.form.contactPhone,
loginName: this.form.loginName,
loginPwd: this.form.loginPwd,
merchantId: this.form.merchantId,
merchantName: this.form.merchantName,
nature: this.form.nature,
businessType: this.form.businessType,
validUntil: this.form.validUntil,
payRate: this.form.payRate,
serviceRate: this.form.serviceRate,
addrDetail: this.form.addrDetail,
businessHours: this.form.businessHours,
email: this.form.email,
// 非必填
companyCode: this.form.companyCode,
businessLicenseImageList: list || [],
merchantName: this.form.merchantName,
};
return data;
},
// 添加
async addBusiness(data) {
const res = await this.$api.POST(`${addBusiness.url}`, data);
console.log(res, "123");
},
// 编辑
// 获取 详情
async getBusiness(openId) {
const res = await this.$api.GET(`${getBusiness.url}${openId}`);
this.form = { ...res.company, ...res.info };
await this.provinceIdChange();
this.form.cityId = res.company.cityId;
await this.cityIdChange();
this.form.countyId = res.company.countyId;
await this.fourChange();
this.form.townId = res.company.townId;
if (this.form.businessHours) {
let arr = [...this.form.businessHours];
this.form.startWeek = arr[1];
this.form.endWeek = arr[4];
this.form.startTime = arr[6] + arr[7];
this.form.endTime = arr[12] + arr[13];
}
this.form.loginName = res.company.userList[0].loginName;
this.form.loginPwd = res.company.userList[0].loginPwd;
console.log(this.form);
this.isLoading = false;
console.log(res);
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container v-loading="isLoading">
<div>
<div class="providerOrderList-container">
<div class="d-f-j"
style="background: #f2f3f5">
<div>
<div class="name">基本信息</div>
</div>
<div class="name modify"
@click="basicModiy()">修改</div>
</div>
</div>
<div class="df"
style="margin-top: 20px">
<div class="df left">
<div>商家名称:</div>
<div>{{ datas.companyName }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>社会代码:</div>
<div>{{ datas.companyCode }}</div>
</div>
</div>
<div class="df">
<div class="df left">
<div>商家类型:</div>
<div>{{ datas.businessTypeValue }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>商家性质:</div>
<div>{{ datas.natureValue }}</div>
</div>
</div>
<div class="df">
<div class="df left">
<div>登录账号:</div>
<div>{{ datas.loginName }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>账号状态:</div>
<div>{{ datas.statusValue }}</div>
</div>
</div>
<div class="df">
<div class="df left">
<div>创建时间:</div>
<div>{{ datas.createTime }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>有效日期:</div>
<div>{{ datas.validUntil }}</div>
</div>
</div>
<div class="df">
<div class="df left">
<div>营业执照:</div>
<div style="text-decoration: underline;cursor: pointer; color: #6767ff;"
@click="lookImage('license')">点击查看</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>商家Logo:</div>
<div style="text-decoration: underline;cursor: pointer; color: #6767ff;"
@click="lookImage('logo')">点击查看</div>
</div>
</div>
<div class="providerOrderList-container"
style="margin-top: 20px">
<div class="d-f-j"
style="background: #f2f3f5">
<div style=""
class="d-f-j">
<div class="name">联系信息</div>
</div>
<div class="name modify"
@click="contactModiy()">修改</div>
</div>
</div>
<div class="df"
style="margin-top: 20px">
<div class="df left">
<div>联系人:</div>
<div>{{ datas.contactName }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>联系电话:</div>
<div>{{ datas.contactPhone }}</div>
</div>
</div>
<div class="df">
<div class="df left">
<div>联系邮箱:</div>
<div>{{ datas.email }}</div>
<!-- <div>zhangye_text_03@sonnhe.commm</div> -->
</div>
<div class="df right"
style="margin-left: 170px">
<div>营业时间:</div>
<div>{{ datas.businessHours }}</div>
</div>
</div>
<div class="df">
<div class="df left"
style="width: 700px">
<div>详细地址:</div>
<div>{{ datas.companyAddr }}</div>
</div>
<!-- <div class="df right"
style="margin-left:170px">
<div>详细地址:</div>
<div>{{datas.addrDetail}} </div>
</div> -->
</div>
<div class="providerOrderList-container"
style="margin-top: 20px">
<div class="d-f-j"
style="background: #f2f3f5">
<div style=""
class="d-f-j">
<div class="name">收款信息</div>
</div>
<div class="name modify"
@click="chargeModiy()">修改</div>
</div>
</div>
<div class="df"
style="margin-top: 10px">
<div class="df left">
<div>支付费率:</div>
<div>{{ datas.payRate }}%</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>服务费率:</div>
<div>{{ datas.serviceRate }}%</div>
</div>
</div>
<div class="df"
style="margin-top: 10px">
<div class="df left">
<div>微信商户号:</div>
<div>{{ datas.merchantId }}</div>
</div>
<div class="df right"
style="margin-left: 170px">
<div>商户号名称:</div>
<div>{{ datas.merchantName }}</div>
</div>
</div>
</div>
<span slot="footer"
class="dialog-footer">
<el-button type="primary"
@click="lookIntroduce()">商家介绍</el-button>
<el-button @click="resetting()"
style="margin-left: 150px">重置密码</el-button>
</span>
<lookImage :isShowImg='isShowImage'
:initContenta='img'
@noShowImg='noShowImg'></lookImage>
<modifyPws :isModifyPassword='isModifyPassword'
@modifyPsw='modifyPsw'
@noModify='noModify'></modifyPws>
</d2-container>
</template>
<script>
import { getBusiness, modifyPws } from "@/libs/common/constant";
export default {
data() {
return {
isLoading: false,
datas: {},
isShow: true,
lunchTime: "",
dinnerTime: "",
imge: "",
openId: this.$route.params.id,
isShowImage: false,
img: "",
isModifyPassword: false,
};
},
async created() {
await this.getData();
},
methods: {
async getData() {
this.isLoading = true;
await this.getBusiness(this.openId);
},
async getBusiness(openId) {
const res = await this.$api.GET(`${getBusiness.url}${openId}`);
this.datas = { ...res.info, ...res.company };
this.datas.loginName = res.company.userList[0].loginName;
for (var i = 0; i < res.logoImageList.length; i++) {
if (res.logoImageList[i].type == 3) {
this.datas.logImage = res.logoImageList[i].url;
}
}
for (var z = 0; z < res.businessLicenseImageList.length; z++) {
if (res.businessLicenseImageList[z].type == 1) {
this.datas.businessImage = res.businessLicenseImageList[z].url;
}
}
this.isLoading = false;
console.log(res, "11");
},
lookImage(type) {
console.log(type);
if (type == "logo") {
this.img = this.datas.logImage;
// 这是logo
} else if (type == "license") {
// 这是营业执照
this.img = this.datas.businessImage;
}
this.isShowImage = true;
},
noShowImg() {
this.isShowImage = false;
},
async modifyPsw(data) {
const pages = {
loginName: this.datas.loginName,
supplierOpenId: this.openId,
loginPwd: data.password,
};
const res = await this.$api.POST(`${modifyPws.url}`, pages);
console.log(res, "修改结果");
this.isModifyPassword = false;
},
async noModify() {
this.isModifyPassword = false;
},
// 设置基本信息
basicModiy() {
this.$router.push({
path: `/basicModiy/${this.openId}`,
});
},
// 联系信息设置
contactModiy() {
this.$router.push({
path: `/contactModiy/${this.openId}`,
});
},
// 收款信息 设置
chargeModiy() {
this.$router.push({
path: `/chargeModiy/${this.openId}`,
});
},
// 商家介绍
lookIntroduce() {
this.$router.push({
path: `/businessIntroduce/${this.openId}`,
});
},
// 重置密码
resetting() {
this.isModifyPassword = true;
},
},
};
</script>
<style lang="scss" scoped>
.d-f-j {
display: flex;
justify-content: space-between;
}
.df {
display: flex;
}
.name {
height: 50px;
line-height: 50px;
margin-left: 10px;
}
.modify {
margin-right: 20px;
color: blue;
cursor: pointer;
}
.button {
margin-top: 20px;
margin-right: 10px;
}
.left {
width: 400px;
line-height: 40px;
margin-left: 30px;
}
.right {
line-height: 40px;
margin-right: 30px;
}
</style>
<template>
<d2-container v-loading="isLoading">
<div>
<div class="providerOrderList-container"
style="margin-top:20px">
<div class="d-f-j"
style=" background: #f2f3f5;">
<div style=""
class="d-f-j">
<div class="name">商家名称:</div>
<div class="name">{{datas.companyName}}</div>
</div>
<div class="name modify"></div>
</div>
</div>
<div class="providerOrderList-container"
style="margin-top:20px">
<div class="d-f-j"
style=" background: #f2f3f5;">
<div style=""
class="d-f-j">
<div class="name">截止时间:</div>
<div class="name">{{datas.dataTime}}</div>
</div>
<div class="name modify"></div>
</div>
</div>
<div class="providerOrderList-container"
style="margin-top:20px">
<div class="d-f-j"
style=" background: #f2f3f5;">
<div style=""
class="d-f-j">
<div class="name">商家图片</div>
</div>
<div class="name modify"></div>
</div>
</div>
<div class="image">
<img :src="image"
alt="">
</div>
<div class="providerOrderList-container">
<div class="d-f-j"
style=" background: #f2f3f5;">
<div>
<div class="name"> 介绍信息</div>
</div>
<div class="name modify"></div>
</div>
</div>
<div class="input-box">
<el-input type="textarea"
:disabled="true"
:autosize="{ minRows: 6, maxRows: 11}"
placeholder="请输入内容"
height='200'
v-model="textarea2">
</el-input>
</div>
</div>
</d2-container>
</template>
<script>
import {
getBusinessImg,
getBusinessInit,
getBusiness,
getBusinessSupplierDeadline,
} from "@/libs/common/constant";
export default {
data() {
return {
isLoading: false,
openId: this.$route.params.id,
textarea2: "",
image: "",
datas: {},
};
},
async created() {
this.isLoading = true;
await this.obtain();
},
methods: {
async obtain() {
await this.getBusiness();
await this.getBusinessImg();
await this.getBusinessInit();
await this.getBusinessSupplierDeadline();
this.isLoading = false;
},
async getBusiness() {
const res = await this.$api.GET(`${getBusiness.url}${this.openId}`);
this.datas = { ...res.info, ...res.company };
},
async getBusinessImg() {
const res = await this.$api.GET(`${getBusinessImg.url}${this.openId}`);
if (res.length > 0) {
this.image = res[0].url || "";
}
},
async getBusinessInit() {
const res = await this.$api.GET(`${getBusinessInit.url}${this.openId}`);
if (res) {
this.textarea2 = res || "";
}
},
async getBusinessSupplierDeadline() {
const res = await this.$api.GET(
`${getBusinessSupplierDeadline.url}${this.openId}`
);
var lunchTime = "";
var dinnerTime = "";
for (var i = 0; i < res.length; i++) {
if (res[i].type === 2) {
lunchTime = res[i].deadline;
}
if (res[i].type === 3) {
dinnerTime = res[i].deadline;
}
}
var dataTime = `午餐:${lunchTime} 晚餐:${dinnerTime}`;
this.datas.dataTime = dataTime;
},
},
};
</script>
<style lang="scss" scoped>
.d-f-j {
display: flex;
justify-content: space-between;
}
.df {
display: flex;
}
.name {
height: 50px;
line-height: 50px;
margin-left: 10px;
}
.modify {
margin-right: 20px;
color: blue;
cursor: pointer;
}
.button {
margin-top: 20px;
margin-right: 10px;
}
.left {
width: 200px;
line-height: 40px;
margin-left: 30px;
}
.right {
line-height: 40px;
margin-right: 30px;
}
.input-box {
margin-top: 20px;
margin-left: 10px;
width: 400px;
// height: 200px;
}
.image {
margin-top: 20px;
margin-left: 10px;
margin-bottom: 20px;
img {
width: 350px;
}
}
</style>
<template>
<d2-container v-loading="isLoading">
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="商家名称:"
prop="companyName">
<el-input v-model="form.companyName"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="机构编码:">
<el-input v-model="form.companyCode"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="商家性质:"
prop="nature">
<el-select v-model="form.nature"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in businessNatureList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商家类型:"
prop="businessType">
<el-select v-model="form.businessType"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in businessTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="有效期:"
prop="a">
<el-date-picker v-model="value1"
type="date"
:picker-options="pickerOptions"
format="yyyy 年 MM 月 dd 日"
value-format="yyyy-MM-dd 00:00:00"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
<el-form-item label="营业执照:">
<update-image v-model="form.businessLicenseImageList"
:limitKb="2048"
:drag="true"
:isShowTip="false">
</update-image>
<div class="zhu">注:图片格式为png、jpg,大小不超过2MB</div>
</el-form-item>
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from "vuex";
import {
getNature,
getBusinessType,
modiyBusiness,
addBusiness,
getBusiness,
} from "@/libs/common/constant";
import { selectList, generateTiemList, weekList } from "@/utils/json";
export default {
data() {
return {
openid: this.$route.params.id,
isLoading: false,
ifSubmit: false,
form: {
companyName: "",
companyCode: "",
nature: "",
businessType: "",
businessLicenseImageList: "",
a: "111",
},
value1: "",
// 商家性质
businessNatureList: [],
// 商家类型
businessTypeList: [],
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now();
},
},
rules: {
companyName: [{ required: true, message: "请填写", trigger: "blur" }],
contactName: [{ required: true, message: "请填写", trigger: "blur" }],
a: [{ required: true, message: "请填写", trigger: "change" }],
nature: [{ required: true, message: "请选择", trigger: "change" }],
businessType: [
{ required: true, message: "请选择", trigger: "change" },
],
},
};
},
async created() {
if (this.openid) {
this.isLoading = true;
await this.getConstant();
await this.getBusiness();
} else {
await this.cancel();
}
},
computed: {
// 获取 此页面 路径
...mapState("d2admin/page", ["current"]),
},
methods: {
...mapActions("d2admin/page", ["close", "add"]),
// 获取 基本信息
async getBusiness() {
const res = await this.$api.GET(`${getBusiness.url}${this.openid}`);
this.form = { ...res.info, ...res.company };
this.value1 = this.form.validUntil;
// this.value1 = "2024-09-11 00:00:00";
this.form.a = "111";
this.isLoading = false;
},
// 获取字典数据
async getConstant() {
await this.getNatureList();
await this.getBusinessTypeList();
},
selectList(myJson, a) {
return selectList(myJson, a);
},
// 获取 商家性质
async getNatureList() {
const res = await this.$api.GET(`${getNature.url}`);
this.businessNatureList = this.selectList(res, true);
},
// 获取 商家类型
async getBusinessTypeList() {
const res = await this.$api.GET(`${getBusinessType.url}`);
this.businessTypeList = this.selectList(res);
},
async cancel() {
var tagName = this.current;
await this.close({ tagName });
this.$router.push("/businessList");
},
validatePhoneNumber(phoneNumber) {
var pattern = /^1[3456789]\d{9}$/;
return pattern.test(phoneNumber);
},
submit(detail) {
if (!this.validatePhoneNumber(this.form.contactPhone)) {
this.$message.error("请输入正确的手机号!!!");
return;
}
if (this.ifSubmit) {
return;
}
this.ifSubmit = true;
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
var data = await this.arrangeData();
// 判断 参数 确定是修改还是 添加
if (this.openid) {
console.log(data, "编辑提交的参数");
// 提交接口
data.companyOpenId = this.openid;
const res = await this.$api.POST(`${modiyBusiness.url}`, data);
} else {
console.log(data, "添加提交的参数");
}
await this.cancel();
this.$message({
type: "success",
message: "操作成功!",
});
} catch (error) {}
} else {
console.log("error submit!!");
this.ifSubmit = false;
return false;
}
});
},
arrangeData() {
// if (this.form.validUntil != this.form.value1) {
// this.form.validUntil = dayjs(this.form.value1).format(
// "YYYY-MM-DD 00:00:00"
// );
// }
var data = {
companyName: this.form.companyName,
provinceId: this.form.provinceId,
cityId: this.form.cityId,
countyId: this.form.countyId,
townId: this.form.townId,
contactName: this.form.contactName,
contactPhone: this.form.contactPhone,
loginName: this.form.loginName,
loginPwd: this.form.loginPwd,
merchantId: this.form.merchantId,
merchantName: this.form.merchantName,
nature: this.form.nature,
businessType: this.form.businessType,
validUntil: this.value1,
payRate: this.form.payRate,
serviceRate: this.form.serviceRate,
addrDetail: this.form.addrDetail,
businessHours: this.form.businessHours,
// 非必填
companyCode: this.form.companyCode,
merchantName: this.form.merchantName,
};
if(this.form.businessLicenseImageList){
data.businessLicenseImageList = this.form.businessLicenseImageList
}
return data;
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container v-loading="isLoading">
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="支付费率:"
prop="payRate">
<el-input placeholder="请填写"
type="number"
:step="0.1"
v-model="form.payRate"
max='99.9'
min="0.1"
class="filter-item-same-width">
<template slot="append">%</template>
</el-input>
<div class="zhu">注:费率设置单位为百分数,最小基本单位为0.1,有效数据0.1-99.9之间</div>
</el-form-item>
<el-form-item label="服务费率:"
prop="serviceRate">
<el-input placeholder="请填写"
type="number"
:step="0.1"
v-model="form.serviceRate"
max='99.9'
min="0.1"
class="filter-item-same-width">
<template slot="append">%</template>
</el-input>
<div class="zhu">注:费率设置单位为百分数,最小基本单位为0.1,有效数据0.1-99.9之间</div>
</el-form-item>
<el-form-item label="微信商户号:"
prop="merchantId">
<el-input placeholder="请填写"
v-model="form.merchantId"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width">
</el-input>
</el-form-item>
<el-form-item label="商户号名称:"
prop="">
<el-input placeholder="请填写"
v-model="form.merchantName"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width">
</el-input>
<div class="zhu">注:收款账户为平台服务商下申请的支付商户号,请谨慎填写!!</div>
</el-form-item>
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import {
modiyBusiness,
getBusiness
} from '@/libs/common/constant'
export default {
data () {
return {
openId: this.$route.params.id,
isLoading: false,
ifSubmit: false,
form: {},
rules: {
payRate: [{ required: true, message: '请填写', trigger: 'blur' }],
serviceRate: [{ required: true, message: '请填写', trigger: 'blur' }],
merchantId: [{ required: true, message: '请填写', trigger: 'blur' }]
}
}
},
async created () {
if (this.openId) {
this.isLoading = true
await this.getBusiness()
} else {
await this.cancel()
}
},
computed: {
// 获取 此页面 路径
...mapState('d2admin/page', ['current'])
},
methods: {
...mapActions('d2admin/page', ['close', 'add']),
// 获取 基本信息
async getBusiness () {
const res = await this.$api.GET(`${getBusiness.url}${this.openId}`)
this.form = { ...res.info, ...res.company }
this.isLoading = false
},
async cancel () {
var tagName = this.current
await this.close({ tagName })
this.$router.push('/businessList')
},
validatePhoneNumber (phoneNumber) {
var pattern = /^1[3456789]\d{9}$/
return pattern.test(phoneNumber)
},
submit (detail) {
if (!this.validatePhoneNumber(this.form.contactPhone)) {
this.$message.error('请输入正确的手机号!!!')
return
}
if (this.ifSubmit) {
return
}
this.ifSubmit = true
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
var data = await this.arrangeData()
// 判断 参数 确定是修改还是 添加
if (this.openId) {
console.log(data, '编辑提交的参数')
// 提交接口
data.companyOpenId = this.openId
const res = await this.$api.POST(`${modiyBusiness.url}`, data)
} else {
console.log(data, '添加提交的参数')
}
await this.cancel()
this.$message({
type: 'success',
message: '操作成功!'
})
} catch (error) {}
} else {
console.log('error submit!!')
this.ifSubmit = false
return false
}
})
},
arrangeData () {
// if (this.form.validUntil != this.form.value1) {
// this.form.validUntil = dayjs(this.form.value1).format(
// "YYYY-MM-DD 00:00:00"
// );
// }
var data = {
companyName: this.form.companyName,
provinceId: this.form.provinceId,
cityId: this.form.cityId,
countyId: this.form.countyId,
townId: this.form.townId,
contactName: this.form.contactName,
contactPhone: this.form.contactPhone,
loginName: this.form.loginName,
loginPwd: this.form.loginPwd,
merchantId: this.form.merchantId,
merchantName: this.form.merchantName,
nature: this.form.nature,
businessType: this.form.businessType,
validUntil: this.form.validUntil,
payRate: this.form.payRate,
serviceRate: this.form.serviceRate,
addrDetail: this.form.addrDetail,
businessHours: this.form.businessHours,
// 非必填
companyCode: this.form.companyCode,
businessLicenseImageList: this.form.businessLicenseImageList || []
}
return data
}
}
}
</script>
<style>
</style>
<template>
<d2-container v-loading='isLoading'>
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="负责人:"
prop="contactName">
<el-input v-model="form.contactName"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系电话:"
prop="contactPhone">
<el-input v-model="form.contactPhone"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系邮箱:"
prop="email">
<el-input v-model="form.email"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="所在地区:"
prop="a">
<el-row :gutter="10"
style="width:800px">
<el-col :span="5">
<el-form-item label=""
prop="provinceId">
<el-select v-model="form.provinceId"
:loading="isLoadProvinceId"
loading-text="信息加载中"
@change="provinceIdChange"
placeholder="选择省"
style="margin-left:-7px">
<el-option v-for="item in provinceIdList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label=""
prop="cityId">
<el-select v-model="form.cityId"
@change="cityIdChange"
placeholder="选择市">
<el-option v-for="item in cityIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label=""
prop="countyId"
placeholder="选择区/县">
<el-select v-model="form.countyId"
@change="fourChange">
<el-option v-for="item in countyIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5"
v-if="isFourChage">
<el-form-item label=""
prop="countyId"
placeholder="选择乡镇">
<el-select v-model="form.townId">
<el-option v-for="item in fourIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="详细地址:"
prop="addrDetail">
<el-input v-model="form.addrDetail"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="营业时间:"
prop="a"
style="width:600px">
<el-col :span="11">
<el-select v-model="startWeek"
placeholder="请选择">
<el-option v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="2"
class="line"></el-col>
<el-col :span="11">
<el-select v-model="endWeek"
placeholder="请选择">
<el-option v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item label=""
prop="name"
style="width:600px">
<el-col :span="11">
<el-select v-model="startTime"
placeholder="请选择">
<el-option v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="2"
class="line">-</el-col>
<el-col :span="11">
<el-select v-model="endTime"
placeholder="请选择">
<el-option v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import {
getBusiness,
jdAddressCascader,
modiyBusiness
} from '@/libs/common/constant'
import { selectList, generateTiemList, weekList } from '@/utils/json'
export default {
data () {
return {
openId: this.$route.params.id,
isLoading: false,
isLoadProvinceId: false,
// 一级
provinceIdList: [],
// 二级
cityIdList: [],
// 三级
countyIdList: [],
// 四级
fourIdList: [],
isFourChage: false,
ifSubmit: false,
weekList: [],
timeList: [],
startWeek: '',
endWeek: '',
startTime: '',
endTime: '',
form: {
startWeek: '',
endWeek: '',
startTime: '',
endTime: ''
},
rules: {
email: [{ required: true, message: '请填写', trigger: 'blur' }],
addrDetail: [{ required: true, message: '请填写', trigger: 'blur' }],
contactPhone: [{ required: true, message: '请填写', trigger: 'blur' }],
contactName: [{ required: true, message: '请填写', trigger: 'blur' }],
a: [{ required: true, message: '请填写', trigger: 'blur' }],
provinceId: [{ required: true, message: '请选择', trigger: 'change' }],
cityId: [{ required: true, message: '请选择', trigger: 'change' }],
countyId: [{ required: true, message: '请选择', trigger: 'change' }]
}
}
},
async created () {
if (this.openId) {
this.isLoading = true
await this.getBusiness()
await this.getConstant()
}
},
computed: {
// 获取 此页面 路径
...mapState('d2admin/page', ['current'])
},
methods: {
...mapActions('d2admin/page', ['close', 'add']),
// 获取基本信息
async getBusiness () {
const res = await this.$api.GET(`${getBusiness.url}${this.openId}`)
this.form = { ...res.info, ...res.company }
const arr = [...this.form.businessHours]
this.startWeek = arr[1]
this.endWeek = arr[4]
this.startTime = arr[6] + arr[7]
this.endTime = arr[12] + arr[13]
this.provinceIdList = await this.getProvinceList()
this.cityIdList = await this.getCityList(this.form.provinceId)
this.countyIdList = await this.getCountyId(this.form.cityId)
if (this.form.townId) {
this.fourIdList = await this.getfourList(this.form.countyId)
this.isFourChage = true
} else {
this.isFourChage = false
}
console.log(this.form)
this.form.a = '1111'
this.isLoading = false
},
async getConstant () {
this.timeList = await this.generateTiemList()
this.weekList = await this.weekLista()
},
// 获取 一级地址
async getProvinceList () {
const res = await this.$api.GET(`${jdAddressCascader.url}`)
this.isLoadProvinceId = false
return res
},
// 获取 二级 地址
async getCityList (provinceId) {
const res = await this.$api.GET(`${jdAddressCascader.url2}${provinceId}`)
return res
},
// 获取 三级 地址
async getCountyId (cityId) {
const res = await this.$api.GET(`${jdAddressCascader.url3}${cityId}`)
return res
},
// 获取 四级 地址
async getfourList (countyId) {
const res = await this.$api.GET(`${jdAddressCascader.url4}${countyId}`)
console.log(res, '我被调用了')
return res
},
// 二级地址赋值
async provinceIdChange () {
this.cityIdList = await this.getCityList(this.form.provinceId)
this.form.cityId = ''
this.form.countyId = ''
},
// 三级 地址 赋值
async cityIdChange () {
this.countyIdList = await this.getCountyId(this.form.cityId)
this.form.countyId = ''
},
// 四级 地址 赋值
async fourChange () {
this.fourIdList = await this.getfourList(this.form.countyId)
// return
// if (this.fourIdList.length > 0) {
// this.isFourChage = true
// this.form.townId = ''
// } else {
// this.isFourChage = false
// this.form.townId = 0
// }
},
selectList (myJson, a) {
return selectList(myJson, a)
},
weekLista () {
return weekList
},
generateTiemList () {
return generateTiemList()
},
async cancel () {
var tagName = this.current
await this.close({ tagName })
this.$router.push('/businessList')
},
validatePhoneNumber (phoneNumber) {
var pattern = /^1[3456789]\d{9}$/
return pattern.test(phoneNumber)
},
submit (detail) {
if (!this.validatePhoneNumber(this.form.contactPhone)) {
this.$message.error('请输入正确的手机号!!!')
return
}
if (this.ifSubmit) {
return
}
this.ifSubmit = true
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
var data = await this.arrangeData()
// 判断 参数 确定是修改还是 添加
if (this.openId) {
console.log(data, '编辑提交的参数')
// 提交接口
data.companyOpenId = this.openId
const res = await this.$api.POST(`${modiyBusiness.url}`, data)
console.log(res)
} else {
console.log(data, '添加提交的参数')
}
await this.cancel()
this.$message({
type: 'success',
message: '操作成功!'
})
} catch (error) {}
} else {
console.log('error submit!!')
this.ifSubmit = false
return false
}
})
},
arrangeData () {
if (this.startWeek) {
this.form.businessHours = `周${this.startWeek}至周${this.endWeek},${this.startTime}:00至${this.endTime}:00`
}
// if (this.form.validUntil != this.form.value1) {
// this.form.validUntil = dayjs(this.form.value1).format(
// "YYYY-MM-DD 00:00:00"
// );
// }
// this.form.validUntil = "2025-01-15 00:00:00";
var data = {
companyName: this.form.companyName,
provinceId: this.form.provinceId,
cityId: this.form.cityId,
countyId: this.form.countyId,
townId: this.form.townId,
contactName: this.form.contactName,
contactPhone: this.form.contactPhone,
loginName: this.form.loginName,
loginPwd: this.form.loginPwd,
merchantId: this.form.merchantId,
merchantName: this.form.merchantName,
nature: this.form.nature,
businessType: this.form.businessType,
validUntil: this.form.validUntil,
payRate: this.form.payRate,
serviceRate: this.form.serviceRate,
addrDetail: this.form.addrDetail,
businessHours: this.form.businessHours,
email: this.form.email,
// 非必填
companyCode: this.form.companyCode,
}
if(this.form.businessLicenseImageList){
data.businessLicenseImageList = this.form.businessLicenseImageList
}
return data
}
}
}
</script>
<style>
</style>
<template>
<d2-container>
<div class="providerOrderList-container">
<div class="filter-box">
<div class="filter-item">
<div class="spana">商家名称:</div>
<el-input v-model="conditionCollection.companyNameLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-item">
<div class="spana">商家性质:</div>
<el-select v-model="conditionCollection.nature"
placeholder="全部">
<el-option v-for="item in natureList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter-item">
<div class="spana">商家类型:</div>
<el-select v-model="conditionCollection.businessType"
placeholder="全部">
<el-option v-for="item in businessTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter-item">
<div class="spana">账户状态:</div>
<el-select v-model="conditionCollection.status"
placeholder="全部">
<el-option v-for="item in statusList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<!-- 城市 -->
<el-form :model="conditionCollection">
<el-form-item label="所在城市:">
<el-row :gutter="10"
style="width: 500px">
<el-col :span="9">
<el-form-item label="">
<el-select v-model="conditionCollection.provinceId"
:loading="isLoadProvinceId"
loading-text="信息加载中"
@change="provinceIdChange"
placeholder="选择省"
style="margin-left: 35px">
<el-option v-for="item in provinceIdList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item label="">
<el-select v-model="conditionCollection.cityId"
placeholder="选择市">
<el-option v-for="item in cityIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-form>
<div class="filter-item">
<div class="spana">负责人:</div>
<el-input v-model="conditionCollection.contactNameLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-item">
<div class="spana">联系电话:</div>
<el-input v-model="conditionCollection.contactPhoneLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-btn">
<el-button type="primary"
size=""
@click="queryData()">查询</el-button>
<el-button type=""
size=""
@click="reset()">重置</el-button>
<el-button type="success"
icon="el-icon-circle-plus-outline"
@click="addBusiness()">添加商家</el-button>
</div>
</div>
<div>
<el-table :data="dataList"
size="medium"
style="width: 100%; text-align: center;"
:key="tablekey"
v-loading="loadings">
<!-- <el-table-column prop="userCode" label="序号"> </el-table-column> -->
<el-table-column prop="id"
label="商家编号"> </el-table-column>
<el-table-column prop="companyName"
label="商家名称"></el-table-column>
<el-table-column prop="natureValue"
label="商家性质"> </el-table-column>
<el-table-column prop="businessTypeValue"
label="商家类型"> </el-table-column>
<el-table-column prop="contactName"
label="负责人"></el-table-column>
<el-table-column prop="contactPhone"
label="联系电话"> </el-table-column>
<el-table-column prop="companyAddrTwo"
label="所在城市"> </el-table-column>
<el-table-column prop="loginName"
label="登录账号"> </el-table-column>
<el-table-column prop="statusValue"
label="状态"> </el-table-column>
<el-table-column fixed="right"
label="操作"
width="120">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button type="text"
style="font-size: 14px; margin-left: 0px"
@click="detils(scope.row.openId)">查看详情</el-button>
<!-- <el-button type="text"
v-if="scope.row.status == 0"
style="font-size: 14px; width: 90px; margin-left: 0"
@click="edit(scope.row.openId)">收款设置</el-button> -->
<el-button type="text"
v-if="scope.row.status == 1"
style="font-size: 14px; margin-left: 10px"
@click="enable(scope.row.openId)">启用</el-button>
<el-button type="text"
v-if="scope.row.status == 0"
style="font-size: 14px; margin-left: 10px;color:red"
@click="disable(scope.row.openId)">停用</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<pagination v-show="total >= 0"
:total="total"
:page.sync="searchCriteria.pageNow"
:limit.sync="searchCriteria.pageSize"
@pagination="pagingEvent" />
</div>
</div>
</d2-container>
</template>
<script>
import {
jdAddressCascader,
getNature,
getBusinessType,
getBusinessList,
enableBusiness,
disableBusiness
} from '@/libs/common/constant'
import { selectListALL } from '@/utils/json'
export default {
data () {
return {
conditionCollection: {
companyNameLike: '',
nature: '',
businessType: '',
status: '',
provinceId: '',
cityId: '',
contactNameLike: '',
contactPhoneLike: ''
},
dataList: [],
searchCriteria: {
pageNow: 1,
pageSize: 10
},
total: 0,
tablekey: '',
loadings: true,
isLoadProvinceId: true,
// 商家 性质
natureList: [],
// 商家类型
businessTypeList: [],
// 账户 状态
statusList: [
{
label: '全部',
value: ''
},
{
label: '启用',
value: 0
},
{
label: '停用',
value: 1
}
],
// 一级地址
provinceIdList: [],
// 二级地址
cityIdList: [],
determineQueryData: {}
}
},
async created () {
// 获取 字典 数据
await this.getConstant()
// 获取 初始 数据
await this.queryData()
},
methods: {
// 查询
async queryData () {
this.determineQueryData = this.conditionCollection
var data = this.conditionCollection
data.currentPage = this.searchCriteria.pageNow
data.pageSize = this.searchCriteria.pageSize
await this.getListData(data)
},
// 重置
reset () {
var obj = this.conditionCollection
for (const key in obj) {
obj[key] = ''
}
},
// 获取 列表 参数
async getListData (data) {
this.loadings = true
const dataList = await this.getBusinessList(data)
console.log(dataList)
this.total = dataList.total
for (var i = 0; i < dataList.list.length; i++) {
dataList.list[i].natureValue =
dataList.list[i].supplierInfoBo.natureValue
dataList.list[i].businessTypeValue =
dataList.list[i].supplierInfoBo.businessTypeValue
dataList.list[i].loginName = dataList.list[i].userList[0].loginName
dataList.list[i].companyAddrTwo = dataList.list[i].companyAddr.split(" ")[0] + " "+ dataList.list[i].companyAddr.split(" ")[1]
}
this.dataList = dataList.list
this.loadings = false
},
async getBusinessList (data) {
const res = await this.$api.GET(`${getBusinessList.url}`, data)
return res
},
async pagingEvent () {
// await this.queryData();
var data = this.determineQueryData
data.currentPage = this.searchCriteria.pageNow
data.pageSize = this.searchCriteria.pageSize
await this.getListData(data)
},
// 添加 供应商
addBusiness () {
this.$router.push({
path: '/addBusiness'
})
},
// 编辑 供应商
edit (openId) {
this.$router.push({
path: `/editBusiness/${openId}`
})
},
// 查看详情
detils (openId) {
this.$router.push({
path: `/detailsBusiness/${openId}`
})
},
selectListALL (myJson) {
return selectListALL(myJson)
},
// 获取 常量
async getConstant () {
await this.getProvinceIdList()
await this.getNatureList()
await this.getBusinessTypeList()
},
// 获取 一级地址
async getProvinceIdList () {
const res = await this.$api.GET(`${jdAddressCascader.url}`)
this.provinceIdList = res
this.isLoadProvinceId = false
},
// 获取 商家性质
async getNatureList () {
const res = await this.$api.GET(`${getNature.url}`)
this.natureList = this.selectListALL(res)
},
// 获取 商家类型
async getBusinessTypeList () {
const res = await this.$api.GET(`${getBusinessType.url}`)
this.businessTypeList = this.selectListALL(res)
},
// 获取 二级地址
async provinceIdChange () {
this.cityIdList = await this.$api.GET(
`${jdAddressCascader.url2}${this.conditionCollection.provinceId}`
)
},
async enable (openId) {
this.$confirm('确定启用此供应商吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
// type: 'warning',
customClass: 'custom-confirm'
})
.then(() => {
this.enableBusiness(openId)
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消启用!'
})
})
},
async enableBusiness (openId) {
const res = await this.$api.POST(`${enableBusiness.url}`, {
companyOpenId: openId
})
console.log(res)
await this.queryData()
this.$message({
type: 'success',
message: '启用成功!'
})
},
disable (openId) {
this.$confirm('确定停用此供应商吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
// type: 'warning',
customClass: 'custom-confirm'
})
.then(() => {
this.disableBusiness(openId)
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消停用!'
})
})
},
async disableBusiness (openId) {
const res = await this.$api.POST(`${disableBusiness.url}`, {
companyOpenId: openId
})
console.log(res)
await this.queryData()
this.$message({
type: 'success',
message: '停用成功!'
})
}
}
}
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container v-loading="isLoading">
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="客户名称:"
prop="name">
<el-input v-model="form.name"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="机构编码:">
<el-input v-model="form.coding"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="客户性质:"
prop="nature">
<el-select v-model="form.nature"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in customerNatureList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="客户类型:"
prop="type">
<el-select v-model="form.type"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in customerTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="所在地区:"
prop="a">
<el-row :gutter="10"
style="width: 800px">
<el-col :span="5">
<el-form-item label=""
prop="provinceId">
<el-select v-model="form.provinceId"
:loading="isLoadProvinceId"
loading-text="信息加载中"
@change="provinceIdChange"
placeholder="选择省"
style="margin-left: -7px">
<el-option v-for="item in provinceIdList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label=""
prop="cityId">
<el-select v-model="form.cityId"
@change="cityIdChange"
placeholder="选择市">
<el-option v-for="item in cityIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="联系人:"
prop="contact">
<el-input v-model="form.contact"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系电话:"
prop="contactPhone">
<el-input v-model="form.contactPhone"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="联系邮箱:"
prop="">
<el-input v-model="form.email"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
<div class="zhu">
注:此邮箱后续会作为接收电子账单使用,请认真填写
</div>
</el-form-item>
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from "vuex";
import {
getCustomerNature,
getCustomerType,
jdAddressCascader,
addBusiness,
getBusiness,
addCustomer,
} from "@/libs/common/constant";
import { selectList, generateTiemList, weekList } from "@/utils/json";
import dayjs from "dayjs";
export default {
data() {
return {
isLoading: false,
form: {
coding: "",
name: "",
nature: "",
type: "",
provinceId: "",
cityId: "",
countyId: "",
townId: "",
contact: "",
contactPhone: "",
email: "",
a: "11111",
},
// 客户性质
customerNatureList: [],
// 客户 类型
customerTypeList: [],
// 地址
isLoadProvinceId: true,
// 一级
provinceIdList: [],
// 二级
cityIdList: [],
// 三级
countyIdList: [],
// 四级
fourIdList: [],
// 四级地址是否显示
isFourChage: false,
weekList: [],
timeList: [],
startWeek: "",
endWeek: "",
startTime: "",
endTime: "",
// 禁用今天之前的日期
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now();
},
},
ifSubmit: false,
// 编辑
editId: this.$route.params.id,
rules: {
name: [{ required: true, message: "请填写", trigger: "blur" }],
contact: [{ required: true, message: "请填写", trigger: "blur" }],
contactPhone: [{ required: true, message: "请填写", trigger: "blur" }],
nature: [{ required: true, message: "请选择", trigger: "change" }],
type: [{ required: true, message: "请选择", trigger: "change" }],
a: [{ required: true, message: "请填写", trigger: "blur" }],
provinceId: [{ required: true, message: "请选择", trigger: "change" }],
cityId: [{ required: true, message: "请选择", trigger: "change" }],
countyId: [{ required: true, message: "请选择", trigger: "change" }],
},
};
},
async created() {
// 获取 字典 数据
await this.getConstant();
if (this.editId) {
// 传过来 openid 获取 详情
this.isLoading = true;
await this.getBusiness(this.editId);
}
},
computed: {
// 获取 此页面 路径
...mapState("d2admin/page", ["current"]),
},
methods: {
...mapActions("d2admin/page", ["close", "add"]),
// 获取 字典数据
async getConstant() {
await this.getNatureList();
await this.getCustomerTypeList();
this.provinceIdList = await this.getProvinceList();
this.timeList = await this.generateTiemList();
this.weekList = await this.weekLista();
},
selectList(myJson, a) {
return selectList(myJson, a);
},
weekLista() {
return weekList;
},
generateTiemList() {
return generateTiemList();
},
// 获取 客户性质
async getNatureList() {
const res = await this.$api.GET(`${getCustomerNature.url}`);
this.customerNatureList = this.selectList(res, true);
},
// 获取 客户类型
async getCustomerTypeList() {
const res = await this.$api.GET(`${getCustomerType.url}`);
this.customerTypeList = this.selectList(res);
},
// 获取 一级地址
async getProvinceList() {
const res = await this.$api.GET(`${jdAddressCascader.url}`);
this.isLoadProvinceId = false;
return res;
},
// 获取 二级 地址
async getCityList(provinceId) {
const res = await this.$api.GET(`${jdAddressCascader.url2}${provinceId}`);
return res;
},
// 获取 三级 地址
async getCountyId(cityId) {
const res = await this.$api.GET(`${jdAddressCascader.url3}${cityId}`);
return res;
},
// 获取 四级 地址
async getfourList(countyId) {
const res = await this.$api.GET(`${jdAddressCascader.url4}${countyId}`);
console.log(res, "我被调用了");
return res;
},
// 二级地址赋值
async provinceIdChange() {
this.cityIdList = await this.getCityList(this.form.provinceId);
this.form.cityId = "";
this.form.countyId = "";
},
// 三级 地址 赋值
async cityIdChange() {
this.countyIdList = await this.getCountyId(this.form.cityId);
this.form.countyId = "";
},
// 四级 地址 赋值
async fourChange() {
this.fourIdList = await this.getfourList(this.form.countyId);
if (this.fourIdList.length > 0) {
this.isFourChage = true;
this.form.townId = "";
} else {
this.isFourChage = false;
this.form.townId = 0;
}
},
async cancel() {
var tagName = this.current;
await this.close({ tagName });
this.$router.push("/customerList");
},
ls() {
var data = {
companyName: "12315z",
provinceId: "1",
cityId: "72",
countyId: "2799",
townId: "0",
contactName: "124214z1",
contactPhone: "15673701001",
loginName: "zzx131",
loginPwd: "123456z",
merchantId: "1659484214",
merchantName: "大学城餐厅12334",
nature: "1",
businessType: "1",
validUntil: "2025-01-15 00:00:00",
payRate: "3",
serviceRate: "3",
};
this.addBusiness(data);
},
validatePhoneNumber(phoneNumber) {
var pattern = /^1[3456789]\d{9}$/;
return pattern.test(phoneNumber);
},
submit(detail) {
if (!this.validatePhoneNumber(this.form.contactPhone)) {
this.$message.error("请输入正确的手机号!!!");
return;
}
if (this.ifSubmit) {
return;
}
this.ifSubmit = true;
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
// var data = await this.arrangeData();
// 判断 参数 确定是修改还是 添加
if (this.editId) {
console.log(data, "编辑提交的参数");
// 提交接口
this.form.openId = this.editId;
const res = await this.$api.POST(`${addBusiness.url}`, data);
} else {
console.log(this.form, "添加提交的参数");
// 提交 接口
await this.addCustomer(this.form);
}
await this.cancel();
this.$message({
type: "success",
message: "操作成功!",
});
} catch (error) {}
} else {
console.log("error submit!!");
this.ifSubmit = false;
return false;
}
});
},
// 添加 客户
async addCustomer(data) {
const res = await this.$api.POST(`${addCustomer.url}`, data);
console.log(res, "添加客户接口成功的返回");
},
// 整理数据
arrangeData() {
this.form.businessHours = `周${this.form.startWeek}至周${this.form.endWeek},${this.form.startTime}:00至${this.form.endTime}:00`;
this.form.validUntil = dayjs(this.form.value1).format(
"YYYY-MM-DD 00:00:00"
);
var list = [];
if (this.form.businessLicenseImageList) {
list[0] = this.form.businessLicenseImageList;
}
var data = {
companyName: this.form.companyName,
provinceId: this.form.provinceId,
cityId: this.form.cityId,
countyId: this.form.countyId,
townId: this.form.townId,
contactName: this.form.contactName,
contactPhone: this.form.contactPhone,
loginName: this.form.loginName,
loginPwd: this.form.loginPwd,
merchantId: this.form.merchantId,
merchantName: this.form.merchantName,
nature: this.form.nature,
businessType: this.form.businessType,
validUntil: this.form.validUntil,
payRate: this.form.payRate,
serviceRate: this.form.serviceRate,
addrDetail: this.form.addrDetail,
businessHours: this.form.businessHours,
email: this.form.email,
// 非必填
companyCode: this.form.companyCode,
businessLicenseImageList: list || [],
merchantName: this.form.merchantName,
};
return data;
},
// 添加
async addBusiness(data) {
const res = await this.$api.POST(`${addBusiness.url}`, data);
console.log(res, "123");
},
// 编辑
// 获取 详情
async getBusiness(openId) {
const res = await this.$api.GET(`${getBusiness.url}${openId}`);
this.form = { ...res.company, ...res.info };
await this.provinceIdChange();
this.form.cityId = res.company.cityId;
await this.cityIdChange();
this.form.countyId = res.company.countyId;
await this.fourChange();
this.form.townId = res.company.townId;
if (this.form.businessHours) {
let arr = [...this.form.businessHours];
this.form.startWeek = arr[1];
this.form.endWeek = arr[4];
this.form.startTime = arr[6] + arr[7];
this.form.endTime = arr[12] + arr[13];
}
this.form.loginName = res.company.userList[0].loginName;
this.form.loginPwd = res.company.userList[0].loginPwd;
console.log(this.form);
this.isLoading = false;
console.log(res);
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container>
<div class="providerOrderList-container">
<div class="filter-box">
<div class="filter-item">
<div class="spana">客户名称:</div>
<el-input v-model="conditionCollection.companyNameLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-item">
<div class="spana">客户性质:</div>
<el-select v-model="conditionCollection.nature"
placeholder="全部">
<el-option v-for="item in natureList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter-item">
<div class="spana">客户类型:</div>
<el-select v-model="conditionCollection.businessType"
placeholder="全部">
<el-option v-for="item in businessTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter-item">
<div class="spana">账户状态:</div>
<el-select v-model="conditionCollection.status"
placeholder="全部">
<el-option v-for="item in statusList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<!-- 城市 -->
<el-form :model="conditionCollection">
<el-form-item label="所在城市:">
<el-row :gutter="10"
style="width: 500px">
<el-col :span="9">
<el-form-item label="">
<el-select v-model="conditionCollection.provinceId"
:loading="isLoadProvinceId"
loading-text="信息加载中"
@change="provinceIdChange"
placeholder="选择省"
style="margin-left: 35px">
<el-option v-for="item in provinceIdList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item label="">
<el-select v-model="conditionCollection.cityId"
placeholder="选择市">
<el-option v-for="item in cityIdList"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-form>
<div class="filter-item">
<div class="spana">负责人:</div>
<el-input v-model="conditionCollection.contactNameLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-item">
<div class="spana">联系电话:</div>
<el-input v-model="conditionCollection.contactPhoneLike"
placeholder="请输入"></el-input>
</div>
<div class="filter-btn">
<el-button type="primary"
size=""
@click="queryData()">查询</el-button>
<el-button type=""
size=""
@click="reset()">重置</el-button>
<el-button type="success"
icon="el-icon-circle-plus-outline"
@click="addBusiness()">新建客户</el-button>
</div>
</div>
<div>
<el-table :data="dataList"
size="medium"
style="width: 100%; text-align: center;"
:key="tablekey"
v-loading="loadings">
<!-- <el-table-column prop="userCode" label="序号"> </el-table-column> -->
<el-table-column prop="id"
label="客户编号"> </el-table-column>
<el-table-column prop="companyName"
label="客户名称"></el-table-column>
<el-table-column prop="natureValue"
label="客户性质"> </el-table-column>
<el-table-column prop="businessTypeValue"
label="客户类型"> </el-table-column>
<el-table-column prop="contactName"
label="联系人"></el-table-column>
<el-table-column prop="contactPhone"
label="联系电话"> </el-table-column>
<el-table-column prop="companyAddrTwo"
label="所在城市"> </el-table-column>
<el-table-column prop="loginName"
label="创建时间"> </el-table-column>
<el-table-column prop="statusValue"
label="状态"> </el-table-column>
<el-table-column fixed="right"
label="操作"
width="120">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button type="text"
style="font-size: 14px; margin-left: 0px"
@click="detils(scope.row.openId)">查看详情</el-button>
<!-- <el-button type="text"
v-if="scope.row.status == 0"
style="font-size: 14px; width: 90px; margin-left: 0"
@click="edit(scope.row.openId)">收款设置</el-button> -->
<el-button type="text"
v-if="scope.row.status == 1"
style="font-size: 14px; margin-left: 10px"
@click="enable(scope.row.openId)">启用</el-button>
<el-button type="text"
v-if="scope.row.status == 0"
style="font-size: 14px; margin-left: 10px;color:red"
@click="disable(scope.row.openId)">停用</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<pagination v-show="total >= 0"
:total="total"
:page.sync="searchCriteria.pageNow"
:limit.sync="searchCriteria.pageSize"
@pagination="pagingEvent" />
</div>
</div>
</d2-container>
</template>
<script>
import {
jdAddressCascader,
getNature,
getBusinessType,
getBusinessList,
enableBusiness,
disableBusiness,
} from "@/libs/common/constant";
import { selectListALL } from "@/utils/json";
export default {
data() {
return {
conditionCollection: {
companyNameLike: "",
nature: "",
businessType: "",
status: "",
provinceId: "",
cityId: "",
contactNameLike: "",
contactPhoneLike: "",
},
dataList: [],
searchCriteria: {
pageNow: 1,
pageSize: 10,
},
total: 0,
tablekey: "",
loadings: true,
isLoadProvinceId: true,
// 商家 性质
natureList: [],
// 商家类型
businessTypeList: [],
// 账户 状态
statusList: [
{
label: "全部",
value: "",
},
{
label: "启用",
value: 0,
},
{
label: "停用",
value: 1,
},
],
// 一级地址
provinceIdList: [],
// 二级地址
cityIdList: [],
determineQueryData: {},
};
},
async created() {
// 获取 字典 数据
await this.getConstant();
// 获取 初始 数据
await this.queryData();
},
methods: {
// 查询
async queryData() {
this.determineQueryData = this.conditionCollection;
var data = this.conditionCollection;
data.currentPage = this.searchCriteria.pageNow;
data.pageSize = this.searchCriteria.pageSize;
await this.getListData(data);
},
// 重置
reset() {
var obj = this.conditionCollection;
for (const key in obj) {
obj[key] = "";
}
},
// 获取 列表 参数
async getListData(data) {
this.loadings = true;
const dataList = await this.getBusinessList(data);
console.log(dataList);
this.total = dataList.total;
for (var i = 0; i < dataList.list.length; i++) {
dataList.list[i].natureValue =
dataList.list[i].supplierInfoBo.natureValue;
dataList.list[i].businessTypeValue =
dataList.list[i].supplierInfoBo.businessTypeValue;
dataList.list[i].loginName = dataList.list[i].userList[0].loginName;
dataList.list[i].companyAddrTwo =
dataList.list[i].companyAddr.split(" ")[0] +
" " +
dataList.list[i].companyAddr.split(" ")[1];
}
this.dataList = dataList.list;
this.loadings = false;
},
async getBusinessList(data) {
const res = await this.$api.GET(`${getBusinessList.url}`, data);
return res;
},
async pagingEvent() {
// await this.queryData();
var data = this.determineQueryData;
data.currentPage = this.searchCriteria.pageNow;
data.pageSize = this.searchCriteria.pageSize;
await this.getListData(data);
},
// 添加 供应商
addBusiness() {
this.$router.push({
path: "/addCustomer",
});
},
// 编辑 供应商
edit(openId) {
this.$router.push({
path: `/editBusiness/${openId}`,
});
},
// 查看详情
detils(openId) {
this.$router.push({
path: `/detailsBusiness/${openId}`,
});
},
selectListALL(myJson) {
return selectListALL(myJson);
},
// 获取 常量
async getConstant() {
await this.getProvinceIdList();
await this.getNatureList();
await this.getBusinessTypeList();
},
// 获取 一级地址
async getProvinceIdList() {
const res = await this.$api.GET(`${jdAddressCascader.url}`);
this.provinceIdList = res;
this.isLoadProvinceId = false;
},
// 获取 商家性质
async getNatureList() {
const res = await this.$api.GET(`${getNature.url}`);
this.natureList = this.selectListALL(res);
},
// 获取 商家类型
async getBusinessTypeList() {
const res = await this.$api.GET(`${getBusinessType.url}`);
this.businessTypeList = this.selectListALL(res);
},
// 获取 二级地址
async provinceIdChange() {
this.cityIdList = await this.$api.GET(
`${jdAddressCascader.url2}${this.conditionCollection.provinceId}`
);
},
async enable(openId) {
this.$confirm("确定启用此供应商吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
// type: 'warning',
customClass: "custom-confirm",
})
.then(() => {
this.enableBusiness(openId);
})
.catch(() => {
this.$message({
type: "info",
message: "已取消启用!",
});
});
},
async enableBusiness(openId) {
const res = await this.$api.POST(`${enableBusiness.url}`, {
companyOpenId: openId,
});
console.log(res);
await this.queryData();
this.$message({
type: "success",
message: "启用成功!",
});
},
disable(openId) {
this.$confirm("确定停用此供应商吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
// type: 'warning',
customClass: "custom-confirm",
})
.then(() => {
this.disableBusiness(openId);
})
.catch(() => {
this.$message({
type: "info",
message: "已取消停用!",
});
});
},
async disableBusiness(openId) {
const res = await this.$api.POST(`${disableBusiness.url}`, {
companyOpenId: openId,
});
console.log(res);
await this.queryData();
this.$message({
type: "success",
message: "停用成功!",
});
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container>
<div>首页</div>
</d2-container>
</template>
<script>
// import { ref } from "vue";
export default {
// setup() {
// const name = ref("首页");
// return {
// name,
// };
// },
}
</script>
<style>
</style>
<template>
<d2-container v-loading="isLoading">
<div class="addHouser-container">
<el-form :model="form"
label-width="120px"
ref="form"
:rules="rules">
<el-form-item label="产品名称:"
prop="name">
<el-input v-model="form.name"
placeholder="请填写"
autocomplete="off"
maxlength="50"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="产品类型:"
prop="type">
<el-select v-model="form.type"
placeholder="请选择"
class="filter-item-same-width">
<el-option v-for="item in typeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="产品型号:"
prop="model">
<el-input v-model="form.model"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="搜索前缀:"
prop="">
<el-input v-model="form.searchPrefix"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
<el-form-item label="备注:"
prop="">
<el-input v-model="form.remark"
placeholder="请填写"
autocomplete="off"
maxlength="30"
:show-word-limit="true"
class="filter-item-same-width" />
</el-form-item>
</el-form>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary"
style="margin-left: 150px"
:loading="ifSubmit"
@click="submit('form')">保存</el-button>
</span>
</d2-container>
</template>
<script>
import { mapState, mapActions } from "vuex";
import {
addProduct,
getProductType,
getOneProduct,
editProduct
} from "@/libs/common/constant";
import { selectList, generateTiemList, weekList } from "@/utils/json";
import dayjs from "dayjs";
export default {
data() {
return {
isLoading: false,
form: {
name: "",
type: "",
model: "",
searchPrefix: "",
remark: "",
},
ifSubmit: false,
typeList: [],
// 编辑
editId: this.$route.params.id,
rules: {
name: [{ required: true, message: "请填写", trigger: "blur" }],
model: [{ required: true, message: "请填写", trigger: "blur" }],
type: [{ required: true, message: "请选择", trigger: "change" }],
},
};
},
async created() {
// 获取 字典 数据
await this.getConstant();
if (this.editId) {
// 传过来 openid 获取 详情
this.isLoading = true;
await this.getOneProduct(this.editId);
}
},
computed: {
// 获取 此页面 路径
...mapState("d2admin/page", ["current"]),
},
methods: {
...mapActions("d2admin/page", ["close", "add"]),
// 获取 字典数据
async getConstant() {
await this.getProductType();
},
selectList(myJson, a) {
return selectList(myJson, a);
},
weekLista() {
return weekList;
},
generateTiemList() {
return generateTiemList();
},
// 获取 产品 类型
async getProductType() {
const res = await this.$api.GET(`${getProductType.url}`);
this.typeList = this.selectList(res);
},
async cancel() {
var tagName = this.current;
await this.close({ tagName });
this.$router.push("/productList");
},
submit(detail) {
if (this.ifSubmit) {
return;
}
this.ifSubmit = true;
this.$refs[detail].validate(async (valid) => {
if (valid) {
try {
// 提交前 处理 数据
// 拼接 营业时间 转换 时间
// var data = await this.arrangeData();
// 判断 参数 确定是修改还是 添加
if (this.editId) {
console.log(this.form, "编辑提交的参数");
// 提交接口
this.form.openId = this.editId;
const res = await this.$api.POST(`${editProduct.url}`, this.form);
} else {
console.log(this.form, "添加提交的参数");
// 提交 接口
await this.addProduct(this.form);
}
await this.cancel();
this.$message({
type: "success",
message: "操作成功!",
});
} catch (error) {}
} else {
console.log("error submit!!");
this.ifSubmit = false;
return false;
}
});
},
// 添加 产品
async addProduct(data) {
const res = await this.$api.POST(`${addProduct.url}`, data);
console.log(res, "添加产品接口成功的返回");
},
// 编辑
// 获取 详情
async getOneProduct(openId) {
const res = await this.$api.GET(`${getOneProduct.url}${openId}`);
console.log(res);
this.form = res
this.isLoading = false;
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<d2-container>
<div class="providerOrderList-container">
<div class="filter-box">
<div class="filter-item">
<div class="spana">产品名称:</div>
<el-input v-model="conditionCollection.name"
placeholder="请输入"></el-input>
</div>
<div class="filter-item">
<div class="spana">产品类型:</div>
<el-select v-model="conditionCollection.type"
placeholder="全部">
<el-option v-for="item in typeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter-item">
<div class="spana">产品型号:</div>
<el-input v-model="conditionCollection.model"
placeholder="请输入"></el-input>
</div>
<div class="filter-btn">
<el-button type="primary"
size=""
@click="queryData()">查询</el-button>
<el-button type=""
size=""
@click="reset()">重置</el-button>
<el-button type="success"
icon="el-icon-circle-plus-outline"
@click="addBusiness()">新建产品</el-button>
</div>
</div>
<div>
<el-table :data="dataList"
size="medium"
style="width: 100%; text-align: center;"
:key="tablekey"
v-loading="loadings">
<!-- <el-table-column prop="userCode" label="序号"> </el-table-column> -->
<el-table-column prop="id"
label="产品ID"> </el-table-column>
<el-table-column prop="name"
label="产品名称"></el-table-column>
<el-table-column prop="typeValue"
label="产品类型"> </el-table-column>
<el-table-column prop="model"
label="产品型号"> </el-table-column>
<el-table-column prop="searchPrefix"
label="搜索前缀"></el-table-column>
<el-table-column prop="updateTime"
label="创建时间"> </el-table-column>
<el-table-column prop="remark"
label="备注"> </el-table-column>
<el-table-column fixed="right"
label="操作"
width="120">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button type="text"
style="font-size: 14px; margin-left: 0px"
@click="detils(scope.row.openId)">查看详情</el-button>
<el-button type="text"
style="font-size: 14px; margin-left: 10px"
@click="enable(scope.row.openId)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<pagination v-show="total >= 0"
:total="total"
:page.sync="searchCriteria.pageNow"
:limit.sync="searchCriteria.pageSize"
@pagination="pagingEvent" />
</div>
</div>
</d2-container>
</template>
<script>
import {
getProduct,
getProductType,
} from "@/libs/common/constant";
import { selectListALL } from "@/utils/json";
export default {
data() {
return {
conditionCollection: {
name: "",
type: "",
model: "",
},
dataList: [],
searchCriteria: {
pageNow: 1,
pageSize: 10,
},
total: 0,
tablekey: "",
loadings: true,
isLoadProvinceId: true,
// 产品 类型
typeList: [],
determineQueryData: {},
};
},
async created() {
// 获取 字典 数据
await this.getConstant();
// 获取 初始 数据
await this.queryData();
},
methods: {
// 查询
async queryData() {
this.determineQueryData = this.conditionCollection;
var data = this.conditionCollection;
data.currentPage = this.searchCriteria.pageNow;
data.pageSize = this.searchCriteria.pageSize;
await this.getListData(data);
},
// 重置
reset() {
var obj = this.conditionCollection;
for (const key in obj) {
obj[key] = "";
}
},
// 获取 列表 参数
async getListData(data) {
this.loadings = true;
const dataList = await this.getProduct(data);
console.log(dataList);
this.total = dataList.total;
this.dataList = dataList.list;
this.loadings = false;
},
async getProduct(data) {
const res = await this.$api.GET(`${getProduct.url}`, data);
return res;
},
async pagingEvent() {
// await this.queryData();
var data = this.determineQueryData;
data.currentPage = this.searchCriteria.pageNow;
data.pageSize = this.searchCriteria.pageSize;
await this.getListData(data);
},
// 添加 产品
addBusiness() {
this.$router.push({
path: "/addProduct",
});
},
// 查看详情
detils(openId) {
this.$router.push({
path: `/detailsBusiness/${openId}`,
});
},
selectListALL(myJson) {
return selectListALL(myJson);
},
// 获取 常量
async getConstant() {
await this.getProductType();
},
// 获取 产品类型
async getProductType() {
const res = await this.$api.GET(`${getProductType.url}`);
this.typeList = this.selectListALL(res);
},
// 编辑产品
async enable(openId) {
this.$router.push({
path: `/editProduct/${openId}`,
});
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<div class="page">
<p class="page_title">404 page not found</p>
<el-button class="d2-mt" @click="$router.replace({ path: '/' })">
返回首页
</el-button>
</div>
</template>
<style lang="scss" scoped>
.page {
background: #303133;
background-blend-mode: multiply,multiply;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.page_title {
font-size: 20px;
color: #FFF;
}
}
</style>
export default {
beforeRouteEnter (to, from, next) {
next(instance => instance.$router.replace(JSON.parse(from.params.route)))
},
render: h => h()
}
export default {
beforeRouteEnter (to, from, next) {
from.meta[`__stamp-${from.path}`] = Date.now()
next(instance => instance.$router.replace({ path: from.fullPath, meta: from.meta }))
},
render: h => h()
}
<template>
<div class="d2-badge">
<p v-for="(group, groupIndex) of badges" :key="groupIndex" align="center">
<a v-for="(badge, badgeIndex) of group" :key="badgeIndex" :href="badge.link" target="_blank">
<img :src="badge.img"/>
</a>
</p>
</div>
</template>
<script>
const linkD2Admin = 'https://github.com/d2-projects/d2-admin'
export default {
data () {
return {
badges: [
[
{ img: 'https://img.shields.io/github/stars/d2-projects/d2-admin.svg', link: `${linkD2Admin}/stargazers` },
{ img: 'https://img.shields.io/github/forks/d2-projects/d2-admin.svg', link: `${linkD2Admin}/network/members` },
{ img: 'https://img.shields.io/github/issues/d2-projects/d2-admin.svg', link: `${linkD2Admin}/issues` },
{ img: 'https://img.shields.io/github/issues-closed/d2-projects/d2-admin.svg', link: `${linkD2Admin}/issues?q=is%3Aissue+is%3Aclosed` },
{ img: 'https://img.shields.io/github/issues-pr/d2-projects/d2-admin.svg', link: `${linkD2Admin}/pulls` },
{ img: 'https://img.shields.io/github/issues-pr-closed/d2-projects/d2-admin.svg', link: `${linkD2Admin}/pulls?q=is%3Apr+is%3Aclosed` },
{ img: 'https://img.shields.io/github/last-commit/d2-projects/d2-admin.svg', link: linkD2Admin }
],
[
{ img: 'https://github.com/d2-projects/d2-admin/workflows/Deploy%20https%3A%2F%2Fd2.pub/badge.svg', link: `${linkD2Admin}/actions?query=workflow%3A%22Deploy+https%3A%2F%2Fd2.pub%22` },
{ img: 'https://github.com/d2-projects/d2-admin/workflows/Deploy%20https%3A%2F%2Fcdn.d2.pub/badge.svg', link: `${linkD2Admin}/actions?query=workflow%3A%22Deploy+https%3A%2F%2Fcdn.d2.pub%22` },
{ img: 'https://github.com/d2-projects/d2-admin/workflows/Deploy%20Github/badge.svg', link: `${linkD2Admin}/actions?query=workflow%3A%22Deploy+Github%22` },
{ img: 'https://api.netlify.com/api/v1/badges/a5dd4bbd-da3f-4145-98a9-8012577bdcf5/deploy-status', link: 'https://app.netlify.com/sites/d2-admin/deploys' }
],
[
{ img: 'https://visitor-badge.glitch.me/badge?page_id=d2-projects.d2-admin', link: linkD2Admin },
{ img: 'https://img.shields.io/github/release/d2-projects/d2-admin.svg', link: `${linkD2Admin}/releases` }
]
]
}
}
}
</script>
<style lang="scss" scoped>
.d2-badge {
margin-bottom: 20px;
p {
margin: 0px;
margin-bottom: 2px;
&:last-child {
margin-bottom: 0px;
}
img {
display: inline-block;
margin: 0px 2px;
}
}
}
</style>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" xmlns:xlink="http://www.w3.org/1999/xlink" width="804" height="804" viewBox="0 0 804 804">
<desc></desc>
<rect width="804" height="804" fill="#ffffff" cx="0" cy="0" />
<defs>
<rect id="p" width="12" height="12" />
</defs>
<g fill="#000000">
<use x="12" y="12" xlink:href="#p" />
<use x="24" y="12" xlink:href="#p" />
<use x="36" y="12" xlink:href="#p" />
<use x="48" y="12" xlink:href="#p" />
<use x="60" y="12" xlink:href="#p" />
<use x="72" y="12" xlink:href="#p" />
<use x="84" y="12" xlink:href="#p" />
<use x="108" y="12" xlink:href="#p" />
<use x="156" y="12" xlink:href="#p" />
<use x="168" y="12" xlink:href="#p" />
<use x="216" y="12" xlink:href="#p" />
<use x="252" y="12" xlink:href="#p" />
<use x="276" y="12" xlink:href="#p" />
<use x="288" y="12" xlink:href="#p" />
<use x="312" y="12" xlink:href="#p" />
<use x="324" y="12" xlink:href="#p" />
<use x="348" y="12" xlink:href="#p" />
<use x="360" y="12" xlink:href="#p" />
<use x="444" y="12" xlink:href="#p" />
<use x="468" y="12" xlink:href="#p" />
<use x="480" y="12" xlink:href="#p" />
<use x="492" y="12" xlink:href="#p" />
<use x="504" y="12" xlink:href="#p" />
<use x="540" y="12" xlink:href="#p" />
<use x="552" y="12" xlink:href="#p" />
<use x="612" y="12" xlink:href="#p" />
<use x="648" y="12" xlink:href="#p" />
<use x="672" y="12" xlink:href="#p" />
<use x="708" y="12" xlink:href="#p" />
<use x="720" y="12" xlink:href="#p" />
<use x="732" y="12" xlink:href="#p" />
<use x="744" y="12" xlink:href="#p" />
<use x="756" y="12" xlink:href="#p" />
<use x="768" y="12" xlink:href="#p" />
<use x="780" y="12" xlink:href="#p" />
<use x="12" y="24" xlink:href="#p" />
<use x="84" y="24" xlink:href="#p" />
<use x="144" y="24" xlink:href="#p" />
<use x="156" y="24" xlink:href="#p" />
<use x="168" y="24" xlink:href="#p" />
<use x="204" y="24" xlink:href="#p" />
<use x="216" y="24" xlink:href="#p" />
<use x="240" y="24" xlink:href="#p" />
<use x="264" y="24" xlink:href="#p" />
<use x="300" y="24" xlink:href="#p" />
<use x="312" y="24" xlink:href="#p" />
<use x="336" y="24" xlink:href="#p" />
<use x="360" y="24" xlink:href="#p" />
<use x="372" y="24" xlink:href="#p" />
<use x="396" y="24" xlink:href="#p" />
<use x="504" y="24" xlink:href="#p" />
<use x="516" y="24" xlink:href="#p" />
<use x="540" y="24" xlink:href="#p" />
<use x="588" y="24" xlink:href="#p" />
<use x="600" y="24" xlink:href="#p" />
<use x="612" y="24" xlink:href="#p" />
<use x="684" y="24" xlink:href="#p" />
<use x="708" y="24" xlink:href="#p" />
<use x="780" y="24" xlink:href="#p" />
<use x="12" y="36" xlink:href="#p" />
<use x="36" y="36" xlink:href="#p" />
<use x="48" y="36" xlink:href="#p" />
<use x="60" y="36" xlink:href="#p" />
<use x="84" y="36" xlink:href="#p" />
<use x="108" y="36" xlink:href="#p" />
<use x="132" y="36" xlink:href="#p" />
<use x="156" y="36" xlink:href="#p" />
<use x="300" y="36" xlink:href="#p" />
<use x="336" y="36" xlink:href="#p" />
<use x="348" y="36" xlink:href="#p" />
<use x="360" y="36" xlink:href="#p" />
<use x="372" y="36" xlink:href="#p" />
<use x="384" y="36" xlink:href="#p" />
<use x="456" y="36" xlink:href="#p" />
<use x="468" y="36" xlink:href="#p" />
<use x="492" y="36" xlink:href="#p" />
<use x="504" y="36" xlink:href="#p" />
<use x="528" y="36" xlink:href="#p" />
<use x="576" y="36" xlink:href="#p" />
<use x="588" y="36" xlink:href="#p" />
<use x="600" y="36" xlink:href="#p" />
<use x="660" y="36" xlink:href="#p" />
<use x="684" y="36" xlink:href="#p" />
<use x="708" y="36" xlink:href="#p" />
<use x="732" y="36" xlink:href="#p" />
<use x="744" y="36" xlink:href="#p" />
<use x="756" y="36" xlink:href="#p" />
<use x="780" y="36" xlink:href="#p" />
<use x="12" y="48" xlink:href="#p" />
<use x="36" y="48" xlink:href="#p" />
<use x="48" y="48" xlink:href="#p" />
<use x="60" y="48" xlink:href="#p" />
<use x="84" y="48" xlink:href="#p" />
<use x="132" y="48" xlink:href="#p" />
<use x="156" y="48" xlink:href="#p" />
<use x="168" y="48" xlink:href="#p" />
<use x="180" y="48" xlink:href="#p" />
<use x="192" y="48" xlink:href="#p" />
<use x="228" y="48" xlink:href="#p" />
<use x="240" y="48" xlink:href="#p" />
<use x="252" y="48" xlink:href="#p" />
<use x="276" y="48" xlink:href="#p" />
<use x="300" y="48" xlink:href="#p" />
<use x="432" y="48" xlink:href="#p" />
<use x="444" y="48" xlink:href="#p" />
<use x="492" y="48" xlink:href="#p" />
<use x="504" y="48" xlink:href="#p" />
<use x="516" y="48" xlink:href="#p" />
<use x="564" y="48" xlink:href="#p" />
<use x="612" y="48" xlink:href="#p" />
<use x="624" y="48" xlink:href="#p" />
<use x="636" y="48" xlink:href="#p" />
<use x="648" y="48" xlink:href="#p" />
<use x="660" y="48" xlink:href="#p" />
<use x="672" y="48" xlink:href="#p" />
<use x="708" y="48" xlink:href="#p" />
<use x="732" y="48" xlink:href="#p" />
<use x="744" y="48" xlink:href="#p" />
<use x="756" y="48" xlink:href="#p" />
<use x="780" y="48" xlink:href="#p" />
<use x="12" y="60" xlink:href="#p" />
<use x="36" y="60" xlink:href="#p" />
<use x="48" y="60" xlink:href="#p" />
<use x="60" y="60" xlink:href="#p" />
<use x="84" y="60" xlink:href="#p" />
<use x="108" y="60" xlink:href="#p" />
<use x="120" y="60" xlink:href="#p" />
<use x="180" y="60" xlink:href="#p" />
<use x="216" y="60" xlink:href="#p" />
<use x="228" y="60" xlink:href="#p" />
<use x="240" y="60" xlink:href="#p" />
<use x="264" y="60" xlink:href="#p" />
<use x="276" y="60" xlink:href="#p" />
<use x="300" y="60" xlink:href="#p" />
<use x="312" y="60" xlink:href="#p" />
<use x="324" y="60" xlink:href="#p" />
<use x="336" y="60" xlink:href="#p" />
<use x="372" y="60" xlink:href="#p" />
<use x="384" y="60" xlink:href="#p" />
<use x="396" y="60" xlink:href="#p" />
<use x="408" y="60" xlink:href="#p" />
<use x="420" y="60" xlink:href="#p" />
<use x="432" y="60" xlink:href="#p" />
<use x="444" y="60" xlink:href="#p" />
<use x="480" y="60" xlink:href="#p" />
<use x="516" y="60" xlink:href="#p" />
<use x="564" y="60" xlink:href="#p" />
<use x="576" y="60" xlink:href="#p" />
<use x="624" y="60" xlink:href="#p" />
<use x="636" y="60" xlink:href="#p" />
<use x="648" y="60" xlink:href="#p" />
<use x="684" y="60" xlink:href="#p" />
<use x="708" y="60" xlink:href="#p" />
<use x="732" y="60" xlink:href="#p" />
<use x="744" y="60" xlink:href="#p" />
<use x="756" y="60" xlink:href="#p" />
<use x="780" y="60" xlink:href="#p" />
<use x="12" y="72" xlink:href="#p" />
<use x="84" y="72" xlink:href="#p" />
<use x="120" y="72" xlink:href="#p" />
<use x="132" y="72" xlink:href="#p" />
<use x="144" y="72" xlink:href="#p" />
<use x="168" y="72" xlink:href="#p" />
<use x="180" y="72" xlink:href="#p" />
<use x="204" y="72" xlink:href="#p" />
<use x="216" y="72" xlink:href="#p" />
<use x="228" y="72" xlink:href="#p" />
<use x="240" y="72" xlink:href="#p" />
<use x="288" y="72" xlink:href="#p" />
<use x="312" y="72" xlink:href="#p" />
<use x="324" y="72" xlink:href="#p" />
<use x="348" y="72" xlink:href="#p" />
<use x="372" y="72" xlink:href="#p" />
<use x="420" y="72" xlink:href="#p" />
<use x="432" y="72" xlink:href="#p" />
<use x="468" y="72" xlink:href="#p" />
<use x="480" y="72" xlink:href="#p" />
<use x="492" y="72" xlink:href="#p" />
<use x="504" y="72" xlink:href="#p" />
<use x="516" y="72" xlink:href="#p" />
<use x="528" y="72" xlink:href="#p" />
<use x="552" y="72" xlink:href="#p" />
<use x="564" y="72" xlink:href="#p" />
<use x="588" y="72" xlink:href="#p" />
<use x="600" y="72" xlink:href="#p" />
<use x="624" y="72" xlink:href="#p" />
<use x="648" y="72" xlink:href="#p" />
<use x="660" y="72" xlink:href="#p" />
<use x="708" y="72" xlink:href="#p" />
<use x="780" y="72" xlink:href="#p" />
<use x="12" y="84" xlink:href="#p" />
<use x="24" y="84" xlink:href="#p" />
<use x="36" y="84" xlink:href="#p" />
<use x="48" y="84" xlink:href="#p" />
<use x="60" y="84" xlink:href="#p" />
<use x="72" y="84" xlink:href="#p" />
<use x="84" y="84" xlink:href="#p" />
<use x="108" y="84" xlink:href="#p" />
<use x="132" y="84" xlink:href="#p" />
<use x="156" y="84" xlink:href="#p" />
<use x="180" y="84" xlink:href="#p" />
<use x="204" y="84" xlink:href="#p" />
<use x="228" y="84" xlink:href="#p" />
<use x="252" y="84" xlink:href="#p" />
<use x="276" y="84" xlink:href="#p" />
<use x="300" y="84" xlink:href="#p" />
<use x="324" y="84" xlink:href="#p" />
<use x="348" y="84" xlink:href="#p" />
<use x="372" y="84" xlink:href="#p" />
<use x="396" y="84" xlink:href="#p" />
<use x="420" y="84" xlink:href="#p" />
<use x="444" y="84" xlink:href="#p" />
<use x="468" y="84" xlink:href="#p" />
<use x="492" y="84" xlink:href="#p" />
<use x="516" y="84" xlink:href="#p" />
<use x="540" y="84" xlink:href="#p" />
<use x="564" y="84" xlink:href="#p" />
<use x="588" y="84" xlink:href="#p" />
<use x="612" y="84" xlink:href="#p" />
<use x="636" y="84" xlink:href="#p" />
<use x="660" y="84" xlink:href="#p" />
<use x="684" y="84" xlink:href="#p" />
<use x="708" y="84" xlink:href="#p" />
<use x="720" y="84" xlink:href="#p" />
<use x="732" y="84" xlink:href="#p" />
<use x="744" y="84" xlink:href="#p" />
<use x="756" y="84" xlink:href="#p" />
<use x="768" y="84" xlink:href="#p" />
<use x="780" y="84" xlink:href="#p" />
<use x="108" y="96" xlink:href="#p" />
<use x="120" y="96" xlink:href="#p" />
<use x="132" y="96" xlink:href="#p" />
<use x="144" y="96" xlink:href="#p" />
<use x="156" y="96" xlink:href="#p" />
<use x="180" y="96" xlink:href="#p" />
<use x="192" y="96" xlink:href="#p" />
<use x="216" y="96" xlink:href="#p" />
<use x="228" y="96" xlink:href="#p" />
<use x="300" y="96" xlink:href="#p" />
<use x="336" y="96" xlink:href="#p" />
<use x="372" y="96" xlink:href="#p" />
<use x="420" y="96" xlink:href="#p" />
<use x="432" y="96" xlink:href="#p" />
<use x="456" y="96" xlink:href="#p" />
<use x="468" y="96" xlink:href="#p" />
<use x="492" y="96" xlink:href="#p" />
<use x="576" y="96" xlink:href="#p" />
<use x="588" y="96" xlink:href="#p" />
<use x="600" y="96" xlink:href="#p" />
<use x="660" y="96" xlink:href="#p" />
<use x="672" y="96" xlink:href="#p" />
<use x="72" y="108" xlink:href="#p" />
<use x="84" y="108" xlink:href="#p" />
<use x="120" y="108" xlink:href="#p" />
<use x="132" y="108" xlink:href="#p" />
<use x="144" y="108" xlink:href="#p" />
<use x="180" y="108" xlink:href="#p" />
<use x="204" y="108" xlink:href="#p" />
<use x="276" y="108" xlink:href="#p" />
<use x="324" y="108" xlink:href="#p" />
<use x="372" y="108" xlink:href="#p" />
<use x="384" y="108" xlink:href="#p" />
<use x="396" y="108" xlink:href="#p" />
<use x="408" y="108" xlink:href="#p" />
<use x="420" y="108" xlink:href="#p" />
<use x="444" y="108" xlink:href="#p" />
<use x="456" y="108" xlink:href="#p" />
<use x="468" y="108" xlink:href="#p" />
<use x="492" y="108" xlink:href="#p" />
<use x="504" y="108" xlink:href="#p" />
<use x="516" y="108" xlink:href="#p" />
<use x="528" y="108" xlink:href="#p" />
<use x="540" y="108" xlink:href="#p" />
<use x="564" y="108" xlink:href="#p" />
<use x="576" y="108" xlink:href="#p" />
<use x="588" y="108" xlink:href="#p" />
<use x="624" y="108" xlink:href="#p" />
<use x="648" y="108" xlink:href="#p" />
<use x="660" y="108" xlink:href="#p" />
<use x="672" y="108" xlink:href="#p" />
<use x="684" y="108" xlink:href="#p" />
<use x="708" y="108" xlink:href="#p" />
<use x="732" y="108" xlink:href="#p" />
<use x="756" y="108" xlink:href="#p" />
<use x="780" y="108" xlink:href="#p" />
<use x="12" y="120" xlink:href="#p" />
<use x="36" y="120" xlink:href="#p" />
<use x="48" y="120" xlink:href="#p" />
<use x="168" y="120" xlink:href="#p" />
<use x="204" y="120" xlink:href="#p" />
<use x="228" y="120" xlink:href="#p" />
<use x="240" y="120" xlink:href="#p" />
<use x="276" y="120" xlink:href="#p" />
<use x="300" y="120" xlink:href="#p" />
<use x="396" y="120" xlink:href="#p" />
<use x="420" y="120" xlink:href="#p" />
<use x="456" y="120" xlink:href="#p" />
<use x="468" y="120" xlink:href="#p" />
<use x="504" y="120" xlink:href="#p" />
<use x="516" y="120" xlink:href="#p" />
<use x="552" y="120" xlink:href="#p" />
<use x="564" y="120" xlink:href="#p" />
<use x="600" y="120" xlink:href="#p" />
<use x="636" y="120" xlink:href="#p" />
<use x="648" y="120" xlink:href="#p" />
<use x="660" y="120" xlink:href="#p" />
<use x="696" y="120" xlink:href="#p" />
<use x="732" y="120" xlink:href="#p" />
<use x="744" y="120" xlink:href="#p" />
<use x="756" y="120" xlink:href="#p" />
<use x="24" y="132" xlink:href="#p" />
<use x="36" y="132" xlink:href="#p" />
<use x="48" y="132" xlink:href="#p" />
<use x="72" y="132" xlink:href="#p" />
<use x="84" y="132" xlink:href="#p" />
<use x="96" y="132" xlink:href="#p" />
<use x="108" y="132" xlink:href="#p" />
<use x="132" y="132" xlink:href="#p" />
<use x="204" y="132" xlink:href="#p" />
<use x="228" y="132" xlink:href="#p" />
<use x="240" y="132" xlink:href="#p" />
<use x="312" y="132" xlink:href="#p" />
<use x="348" y="132" xlink:href="#p" />
<use x="372" y="132" xlink:href="#p" />
<use x="384" y="132" xlink:href="#p" />
<use x="408" y="132" xlink:href="#p" />
<use x="480" y="132" xlink:href="#p" />
<use x="516" y="132" xlink:href="#p" />
<use x="528" y="132" xlink:href="#p" />
<use x="540" y="132" xlink:href="#p" />
<use x="564" y="132" xlink:href="#p" />
<use x="600" y="132" xlink:href="#p" />
<use x="612" y="132" xlink:href="#p" />
<use x="624" y="132" xlink:href="#p" />
<use x="648" y="132" xlink:href="#p" />
<use x="660" y="132" xlink:href="#p" />
<use x="684" y="132" xlink:href="#p" />
<use x="720" y="132" xlink:href="#p" />
<use x="732" y="132" xlink:href="#p" />
<use x="24" y="144" xlink:href="#p" />
<use x="132" y="144" xlink:href="#p" />
<use x="144" y="144" xlink:href="#p" />
<use x="168" y="144" xlink:href="#p" />
<use x="180" y="144" xlink:href="#p" />
<use x="216" y="144" xlink:href="#p" />
<use x="228" y="144" xlink:href="#p" />
<use x="252" y="144" xlink:href="#p" />
<use x="264" y="144" xlink:href="#p" />
<use x="276" y="144" xlink:href="#p" />
<use x="288" y="144" xlink:href="#p" />
<use x="300" y="144" xlink:href="#p" />
<use x="336" y="144" xlink:href="#p" />
<use x="408" y="144" xlink:href="#p" />
<use x="420" y="144" xlink:href="#p" />
<use x="432" y="144" xlink:href="#p" />
<use x="444" y="144" xlink:href="#p" />
<use x="456" y="144" xlink:href="#p" />
<use x="492" y="144" xlink:href="#p" />
<use x="504" y="144" xlink:href="#p" />
<use x="516" y="144" xlink:href="#p" />
<use x="552" y="144" xlink:href="#p" />
<use x="588" y="144" xlink:href="#p" />
<use x="600" y="144" xlink:href="#p" />
<use x="624" y="144" xlink:href="#p" />
<use x="636" y="144" xlink:href="#p" />
<use x="660" y="144" xlink:href="#p" />
<use x="672" y="144" xlink:href="#p" />
<use x="732" y="144" xlink:href="#p" />
<use x="744" y="144" xlink:href="#p" />
<use x="768" y="144" xlink:href="#p" />
<use x="12" y="156" xlink:href="#p" />
<use x="24" y="156" xlink:href="#p" />
<use x="36" y="156" xlink:href="#p" />
<use x="60" y="156" xlink:href="#p" />
<use x="72" y="156" xlink:href="#p" />
<use x="84" y="156" xlink:href="#p" />
<use x="108" y="156" xlink:href="#p" />
<use x="156" y="156" xlink:href="#p" />
<use x="192" y="156" xlink:href="#p" />
<use x="216" y="156" xlink:href="#p" />
<use x="288" y="156" xlink:href="#p" />
<use x="300" y="156" xlink:href="#p" />
<use x="312" y="156" xlink:href="#p" />
<use x="336" y="156" xlink:href="#p" />
<use x="384" y="156" xlink:href="#p" />
<use x="396" y="156" xlink:href="#p" />
<use x="432" y="156" xlink:href="#p" />
<use x="456" y="156" xlink:href="#p" />
<use x="480" y="156" xlink:href="#p" />
<use x="492" y="156" xlink:href="#p" />
<use x="516" y="156" xlink:href="#p" />
<use x="528" y="156" xlink:href="#p" />
<use x="540" y="156" xlink:href="#p" />
<use x="552" y="156" xlink:href="#p" />
<use x="588" y="156" xlink:href="#p" />
<use x="612" y="156" xlink:href="#p" />
<use x="624" y="156" xlink:href="#p" />
<use x="636" y="156" xlink:href="#p" />
<use x="648" y="156" xlink:href="#p" />
<use x="660" y="156" xlink:href="#p" />
<use x="732" y="156" xlink:href="#p" />
<use x="756" y="156" xlink:href="#p" />
<use x="768" y="156" xlink:href="#p" />
<use x="780" y="156" xlink:href="#p" />
<use x="48" y="168" xlink:href="#p" />
<use x="72" y="168" xlink:href="#p" />
<use x="96" y="168" xlink:href="#p" />
<use x="108" y="168" xlink:href="#p" />
<use x="132" y="168" xlink:href="#p" />
<use x="180" y="168" xlink:href="#p" />
<use x="192" y="168" xlink:href="#p" />
<use x="204" y="168" xlink:href="#p" />
<use x="216" y="168" xlink:href="#p" />
<use x="276" y="168" xlink:href="#p" />
<use x="288" y="168" xlink:href="#p" />
<use x="300" y="168" xlink:href="#p" />
<use x="324" y="168" xlink:href="#p" />
<use x="336" y="168" xlink:href="#p" />
<use x="348" y="168" xlink:href="#p" />
<use x="360" y="168" xlink:href="#p" />
<use x="372" y="168" xlink:href="#p" />
<use x="396" y="168" xlink:href="#p" />
<use x="420" y="168" xlink:href="#p" />
<use x="456" y="168" xlink:href="#p" />
<use x="468" y="168" xlink:href="#p" />
<use x="480" y="168" xlink:href="#p" />
<use x="492" y="168" xlink:href="#p" />
<use x="504" y="168" xlink:href="#p" />
<use x="540" y="168" xlink:href="#p" />
<use x="552" y="168" xlink:href="#p" />
<use x="612" y="168" xlink:href="#p" />
<use x="636" y="168" xlink:href="#p" />
<use x="648" y="168" xlink:href="#p" />
<use x="660" y="168" xlink:href="#p" />
<use x="672" y="168" xlink:href="#p" />
<use x="684" y="168" xlink:href="#p" />
<use x="732" y="168" xlink:href="#p" />
<use x="744" y="168" xlink:href="#p" />
<use x="756" y="168" xlink:href="#p" />
<use x="780" y="168" xlink:href="#p" />
<use x="12" y="180" xlink:href="#p" />
<use x="36" y="180" xlink:href="#p" />
<use x="72" y="180" xlink:href="#p" />
<use x="84" y="180" xlink:href="#p" />
<use x="96" y="180" xlink:href="#p" />
<use x="108" y="180" xlink:href="#p" />
<use x="132" y="180" xlink:href="#p" />
<use x="144" y="180" xlink:href="#p" />
<use x="168" y="180" xlink:href="#p" />
<use x="180" y="180" xlink:href="#p" />
<use x="192" y="180" xlink:href="#p" />
<use x="204" y="180" xlink:href="#p" />
<use x="216" y="180" xlink:href="#p" />
<use x="252" y="180" xlink:href="#p" />
<use x="276" y="180" xlink:href="#p" />
<use x="288" y="180" xlink:href="#p" />
<use x="312" y="180" xlink:href="#p" />
<use x="324" y="180" xlink:href="#p" />
<use x="372" y="180" xlink:href="#p" />
<use x="384" y="180" xlink:href="#p" />
<use x="396" y="180" xlink:href="#p" />
<use x="420" y="180" xlink:href="#p" />
<use x="432" y="180" xlink:href="#p" />
<use x="444" y="180" xlink:href="#p" />
<use x="468" y="180" xlink:href="#p" />
<use x="492" y="180" xlink:href="#p" />
<use x="504" y="180" xlink:href="#p" />
<use x="552" y="180" xlink:href="#p" />
<use x="576" y="180" xlink:href="#p" />
<use x="588" y="180" xlink:href="#p" />
<use x="624" y="180" xlink:href="#p" />
<use x="648" y="180" xlink:href="#p" />
<use x="696" y="180" xlink:href="#p" />
<use x="708" y="180" xlink:href="#p" />
<use x="744" y="180" xlink:href="#p" />
<use x="768" y="180" xlink:href="#p" />
<use x="36" y="192" xlink:href="#p" />
<use x="60" y="192" xlink:href="#p" />
<use x="96" y="192" xlink:href="#p" />
<use x="132" y="192" xlink:href="#p" />
<use x="180" y="192" xlink:href="#p" />
<use x="192" y="192" xlink:href="#p" />
<use x="204" y="192" xlink:href="#p" />
<use x="216" y="192" xlink:href="#p" />
<use x="228" y="192" xlink:href="#p" />
<use x="300" y="192" xlink:href="#p" />
<use x="324" y="192" xlink:href="#p" />
<use x="384" y="192" xlink:href="#p" />
<use x="396" y="192" xlink:href="#p" />
<use x="408" y="192" xlink:href="#p" />
<use x="420" y="192" xlink:href="#p" />
<use x="432" y="192" xlink:href="#p" />
<use x="444" y="192" xlink:href="#p" />
<use x="468" y="192" xlink:href="#p" />
<use x="480" y="192" xlink:href="#p" />
<use x="492" y="192" xlink:href="#p" />
<use x="504" y="192" xlink:href="#p" />
<use x="516" y="192" xlink:href="#p" />
<use x="528" y="192" xlink:href="#p" />
<use x="540" y="192" xlink:href="#p" />
<use x="564" y="192" xlink:href="#p" />
<use x="576" y="192" xlink:href="#p" />
<use x="600" y="192" xlink:href="#p" />
<use x="624" y="192" xlink:href="#p" />
<use x="648" y="192" xlink:href="#p" />
<use x="672" y="192" xlink:href="#p" />
<use x="684" y="192" xlink:href="#p" />
<use x="708" y="192" xlink:href="#p" />
<use x="732" y="192" xlink:href="#p" />
<use x="744" y="192" xlink:href="#p" />
<use x="756" y="192" xlink:href="#p" />
<use x="768" y="192" xlink:href="#p" />
<use x="780" y="192" xlink:href="#p" />
<use x="12" y="204" xlink:href="#p" />
<use x="84" y="204" xlink:href="#p" />
<use x="108" y="204" xlink:href="#p" />
<use x="120" y="204" xlink:href="#p" />
<use x="144" y="204" xlink:href="#p" />
<use x="168" y="204" xlink:href="#p" />
<use x="180" y="204" xlink:href="#p" />
<use x="240" y="204" xlink:href="#p" />
<use x="264" y="204" xlink:href="#p" />
<use x="336" y="204" xlink:href="#p" />
<use x="348" y="204" xlink:href="#p" />
<use x="360" y="204" xlink:href="#p" />
<use x="372" y="204" xlink:href="#p" />
<use x="408" y="204" xlink:href="#p" />
<use x="420" y="204" xlink:href="#p" />
<use x="444" y="204" xlink:href="#p" />
<use x="456" y="204" xlink:href="#p" />
<use x="468" y="204" xlink:href="#p" />
<use x="516" y="204" xlink:href="#p" />
<use x="528" y="204" xlink:href="#p" />
<use x="588" y="204" xlink:href="#p" />
<use x="600" y="204" xlink:href="#p" />
<use x="612" y="204" xlink:href="#p" />
<use x="624" y="204" xlink:href="#p" />
<use x="648" y="204" xlink:href="#p" />
<use x="672" y="204" xlink:href="#p" />
<use x="684" y="204" xlink:href="#p" />
<use x="708" y="204" xlink:href="#p" />
<use x="720" y="204" xlink:href="#p" />
<use x="732" y="204" xlink:href="#p" />
<use x="756" y="204" xlink:href="#p" />
<use x="768" y="204" xlink:href="#p" />
<use x="36" y="216" xlink:href="#p" />
<use x="48" y="216" xlink:href="#p" />
<use x="72" y="216" xlink:href="#p" />
<use x="96" y="216" xlink:href="#p" />
<use x="108" y="216" xlink:href="#p" />
<use x="120" y="216" xlink:href="#p" />
<use x="144" y="216" xlink:href="#p" />
<use x="168" y="216" xlink:href="#p" />
<use x="180" y="216" xlink:href="#p" />
<use x="192" y="216" xlink:href="#p" />
<use x="204" y="216" xlink:href="#p" />
<use x="216" y="216" xlink:href="#p" />
<use x="252" y="216" xlink:href="#p" />
<use x="312" y="216" xlink:href="#p" />
<use x="324" y="216" xlink:href="#p" />
<use x="372" y="216" xlink:href="#p" />
<use x="396" y="216" xlink:href="#p" />
<use x="456" y="216" xlink:href="#p" />
<use x="468" y="216" xlink:href="#p" />
<use x="492" y="216" xlink:href="#p" />
<use x="516" y="216" xlink:href="#p" />
<use x="540" y="216" xlink:href="#p" />
<use x="576" y="216" xlink:href="#p" />
<use x="648" y="216" xlink:href="#p" />
<use x="660" y="216" xlink:href="#p" />
<use x="672" y="216" xlink:href="#p" />
<use x="720" y="216" xlink:href="#p" />
<use x="732" y="216" xlink:href="#p" />
<use x="744" y="216" xlink:href="#p" />
<use x="36" y="228" xlink:href="#p" />
<use x="60" y="228" xlink:href="#p" />
<use x="84" y="228" xlink:href="#p" />
<use x="96" y="228" xlink:href="#p" />
<use x="108" y="228" xlink:href="#p" />
<use x="132" y="228" xlink:href="#p" />
<use x="168" y="228" xlink:href="#p" />
<use x="192" y="228" xlink:href="#p" />
<use x="204" y="228" xlink:href="#p" />
<use x="216" y="228" xlink:href="#p" />
<use x="240" y="228" xlink:href="#p" />
<use x="252" y="228" xlink:href="#p" />
<use x="276" y="228" xlink:href="#p" />
<use x="288" y="228" xlink:href="#p" />
<use x="300" y="228" xlink:href="#p" />
<use x="312" y="228" xlink:href="#p" />
<use x="324" y="228" xlink:href="#p" />
<use x="348" y="228" xlink:href="#p" />
<use x="372" y="228" xlink:href="#p" />
<use x="444" y="228" xlink:href="#p" />
<use x="492" y="228" xlink:href="#p" />
<use x="528" y="228" xlink:href="#p" />
<use x="540" y="228" xlink:href="#p" />
<use x="564" y="228" xlink:href="#p" />
<use x="576" y="228" xlink:href="#p" />
<use x="588" y="228" xlink:href="#p" />
<use x="600" y="228" xlink:href="#p" />
<use x="624" y="228" xlink:href="#p" />
<use x="636" y="228" xlink:href="#p" />
<use x="648" y="228" xlink:href="#p" />
<use x="684" y="228" xlink:href="#p" />
<use x="696" y="228" xlink:href="#p" />
<use x="720" y="228" xlink:href="#p" />
<use x="732" y="228" xlink:href="#p" />
<use x="756" y="228" xlink:href="#p" />
<use x="780" y="228" xlink:href="#p" />
<use x="24" y="240" xlink:href="#p" />
<use x="108" y="240" xlink:href="#p" />
<use x="180" y="240" xlink:href="#p" />
<use x="216" y="240" xlink:href="#p" />
<use x="252" y="240" xlink:href="#p" />
<use x="288" y="240" xlink:href="#p" />
<use x="312" y="240" xlink:href="#p" />
<use x="348" y="240" xlink:href="#p" />
<use x="372" y="240" xlink:href="#p" />
<use x="384" y="240" xlink:href="#p" />
<use x="396" y="240" xlink:href="#p" />
<use x="420" y="240" xlink:href="#p" />
<use x="432" y="240" xlink:href="#p" />
<use x="504" y="240" xlink:href="#p" />
<use x="516" y="240" xlink:href="#p" />
<use x="528" y="240" xlink:href="#p" />
<use x="552" y="240" xlink:href="#p" />
<use x="564" y="240" xlink:href="#p" />
<use x="612" y="240" xlink:href="#p" />
<use x="636" y="240" xlink:href="#p" />
<use x="648" y="240" xlink:href="#p" />
<use x="672" y="240" xlink:href="#p" />
<use x="696" y="240" xlink:href="#p" />
<use x="744" y="240" xlink:href="#p" />
<use x="12" y="252" xlink:href="#p" />
<use x="36" y="252" xlink:href="#p" />
<use x="48" y="252" xlink:href="#p" />
<use x="84" y="252" xlink:href="#p" />
<use x="120" y="252" xlink:href="#p" />
<use x="168" y="252" xlink:href="#p" />
<use x="180" y="252" xlink:href="#p" />
<use x="228" y="252" xlink:href="#p" />
<use x="240" y="252" xlink:href="#p" />
<use x="264" y="252" xlink:href="#p" />
<use x="276" y="252" xlink:href="#p" />
<use x="336" y="252" xlink:href="#p" />
<use x="420" y="252" xlink:href="#p" />
<use x="432" y="252" xlink:href="#p" />
<use x="492" y="252" xlink:href="#p" />
<use x="516" y="252" xlink:href="#p" />
<use x="528" y="252" xlink:href="#p" />
<use x="576" y="252" xlink:href="#p" />
<use x="588" y="252" xlink:href="#p" />
<use x="600" y="252" xlink:href="#p" />
<use x="648" y="252" xlink:href="#p" />
<use x="672" y="252" xlink:href="#p" />
<use x="684" y="252" xlink:href="#p" />
<use x="696" y="252" xlink:href="#p" />
<use x="708" y="252" xlink:href="#p" />
<use x="720" y="252" xlink:href="#p" />
<use x="732" y="252" xlink:href="#p" />
<use x="744" y="252" xlink:href="#p" />
<use x="756" y="252" xlink:href="#p" />
<use x="768" y="252" xlink:href="#p" />
<use x="12" y="264" xlink:href="#p" />
<use x="24" y="264" xlink:href="#p" />
<use x="36" y="264" xlink:href="#p" />
<use x="96" y="264" xlink:href="#p" />
<use x="120" y="264" xlink:href="#p" />
<use x="132" y="264" xlink:href="#p" />
<use x="144" y="264" xlink:href="#p" />
<use x="180" y="264" xlink:href="#p" />
<use x="192" y="264" xlink:href="#p" />
<use x="216" y="264" xlink:href="#p" />
<use x="252" y="264" xlink:href="#p" />
<use x="324" y="264" xlink:href="#p" />
<use x="360" y="264" xlink:href="#p" />
<use x="396" y="264" xlink:href="#p" />
<use x="408" y="264" xlink:href="#p" />
<use x="420" y="264" xlink:href="#p" />
<use x="468" y="264" xlink:href="#p" />
<use x="480" y="264" xlink:href="#p" />
<use x="516" y="264" xlink:href="#p" />
<use x="564" y="264" xlink:href="#p" />
<use x="612" y="264" xlink:href="#p" />
<use x="624" y="264" xlink:href="#p" />
<use x="636" y="264" xlink:href="#p" />
<use x="648" y="264" xlink:href="#p" />
<use x="660" y="264" xlink:href="#p" />
<use x="672" y="264" xlink:href="#p" />
<use x="684" y="264" xlink:href="#p" />
<use x="696" y="264" xlink:href="#p" />
<use x="708" y="264" xlink:href="#p" />
<use x="720" y="264" xlink:href="#p" />
<use x="732" y="264" xlink:href="#p" />
<use x="756" y="264" xlink:href="#p" />
<use x="48" y="276" xlink:href="#p" />
<use x="72" y="276" xlink:href="#p" />
<use x="84" y="276" xlink:href="#p" />
<use x="96" y="276" xlink:href="#p" />
<use x="108" y="276" xlink:href="#p" />
<use x="120" y="276" xlink:href="#p" />
<use x="132" y="276" xlink:href="#p" />
<use x="168" y="276" xlink:href="#p" />
<use x="192" y="276" xlink:href="#p" />
<use x="204" y="276" xlink:href="#p" />
<use x="216" y="276" xlink:href="#p" />
<use x="228" y="276" xlink:href="#p" />
<use x="240" y="276" xlink:href="#p" />
<use x="252" y="276" xlink:href="#p" />
<use x="288" y="276" xlink:href="#p" />
<use x="300" y="276" xlink:href="#p" />
<use x="324" y="276" xlink:href="#p" />
<use x="348" y="276" xlink:href="#p" />
<use x="396" y="276" xlink:href="#p" />
<use x="420" y="276" xlink:href="#p" />
<use x="444" y="276" xlink:href="#p" />
<use x="468" y="276" xlink:href="#p" />
<use x="516" y="276" xlink:href="#p" />
<use x="528" y="276" xlink:href="#p" />
<use x="552" y="276" xlink:href="#p" />
<use x="564" y="276" xlink:href="#p" />
<use x="588" y="276" xlink:href="#p" />
<use x="612" y="276" xlink:href="#p" />
<use x="636" y="276" xlink:href="#p" />
<use x="672" y="276" xlink:href="#p" />
<use x="696" y="276" xlink:href="#p" />
<use x="708" y="276" xlink:href="#p" />
<use x="720" y="276" xlink:href="#p" />
<use x="744" y="276" xlink:href="#p" />
<use x="756" y="276" xlink:href="#p" />
<use x="780" y="276" xlink:href="#p" />
<use x="24" y="288" xlink:href="#p" />
<use x="48" y="288" xlink:href="#p" />
<use x="60" y="288" xlink:href="#p" />
<use x="120" y="288" xlink:href="#p" />
<use x="132" y="288" xlink:href="#p" />
<use x="144" y="288" xlink:href="#p" />
<use x="156" y="288" xlink:href="#p" />
<use x="180" y="288" xlink:href="#p" />
<use x="192" y="288" xlink:href="#p" />
<use x="216" y="288" xlink:href="#p" />
<use x="228" y="288" xlink:href="#p" />
<use x="264" y="288" xlink:href="#p" />
<use x="300" y="288" xlink:href="#p" />
<use x="312" y="288" xlink:href="#p" />
<use x="384" y="288" xlink:href="#p" />
<use x="396" y="288" xlink:href="#p" />
<use x="408" y="288" xlink:href="#p" />
<use x="420" y="288" xlink:href="#p" />
<use x="444" y="288" xlink:href="#p" />
<use x="456" y="288" xlink:href="#p" />
<use x="468" y="288" xlink:href="#p" />
<use x="480" y="288" xlink:href="#p" />
<use x="492" y="288" xlink:href="#p" />
<use x="528" y="288" xlink:href="#p" />
<use x="564" y="288" xlink:href="#p" />
<use x="612" y="288" xlink:href="#p" />
<use x="636" y="288" xlink:href="#p" />
<use x="648" y="288" xlink:href="#p" />
<use x="672" y="288" xlink:href="#p" />
<use x="720" y="288" xlink:href="#p" />
<use x="756" y="288" xlink:href="#p" />
<use x="12" y="300" xlink:href="#p" />
<use x="24" y="300" xlink:href="#p" />
<use x="60" y="300" xlink:href="#p" />
<use x="84" y="300" xlink:href="#p" />
<use x="108" y="300" xlink:href="#p" />
<use x="120" y="300" xlink:href="#p" />
<use x="132" y="300" xlink:href="#p" />
<use x="144" y="300" xlink:href="#p" />
<use x="168" y="300" xlink:href="#p" />
<use x="180" y="300" xlink:href="#p" />
<use x="216" y="300" xlink:href="#p" />
<use x="240" y="300" xlink:href="#p" />
<use x="276" y="300" xlink:href="#p" />
<use x="312" y="300" xlink:href="#p" />
<use x="324" y="300" xlink:href="#p" />
<use x="348" y="300" xlink:href="#p" />
<use x="360" y="300" xlink:href="#p" />
<use x="396" y="300" xlink:href="#p" />
<use x="408" y="300" xlink:href="#p" />
<use x="480" y="300" xlink:href="#p" />
<use x="492" y="300" xlink:href="#p" />
<use x="516" y="300" xlink:href="#p" />
<use x="528" y="300" xlink:href="#p" />
<use x="540" y="300" xlink:href="#p" />
<use x="564" y="300" xlink:href="#p" />
<use x="588" y="300" xlink:href="#p" />
<use x="636" y="300" xlink:href="#p" />
<use x="648" y="300" xlink:href="#p" />
<use x="660" y="300" xlink:href="#p" />
<use x="672" y="300" xlink:href="#p" />
<use x="684" y="300" xlink:href="#p" />
<use x="708" y="300" xlink:href="#p" />
<use x="732" y="300" xlink:href="#p" />
<use x="744" y="300" xlink:href="#p" />
<use x="756" y="300" xlink:href="#p" />
<use x="768" y="300" xlink:href="#p" />
<use x="780" y="300" xlink:href="#p" />
<use x="12" y="312" xlink:href="#p" />
<use x="48" y="312" xlink:href="#p" />
<use x="96" y="312" xlink:href="#p" />
<use x="108" y="312" xlink:href="#p" />
<use x="156" y="312" xlink:href="#p" />
<use x="168" y="312" xlink:href="#p" />
<use x="180" y="312" xlink:href="#p" />
<use x="192" y="312" xlink:href="#p" />
<use x="240" y="312" xlink:href="#p" />
<use x="252" y="312" xlink:href="#p" />
<use x="264" y="312" xlink:href="#p" />
<use x="288" y="312" xlink:href="#p" />
<use x="336" y="312" xlink:href="#p" />
<use x="348" y="312" xlink:href="#p" />
<use x="372" y="312" xlink:href="#p" />
<use x="384" y="312" xlink:href="#p" />
<use x="396" y="312" xlink:href="#p" />
<use x="408" y="312" xlink:href="#p" />
<use x="432" y="312" xlink:href="#p" />
<use x="468" y="312" xlink:href="#p" />
<use x="480" y="312" xlink:href="#p" />
<use x="504" y="312" xlink:href="#p" />
<use x="516" y="312" xlink:href="#p" />
<use x="528" y="312" xlink:href="#p" />
<use x="552" y="312" xlink:href="#p" />
<use x="576" y="312" xlink:href="#p" />
<use x="588" y="312" xlink:href="#p" />
<use x="624" y="312" xlink:href="#p" />
<use x="636" y="312" xlink:href="#p" />
<use x="648" y="312" xlink:href="#p" />
<use x="660" y="312" xlink:href="#p" />
<use x="672" y="312" xlink:href="#p" />
<use x="696" y="312" xlink:href="#p" />
<use x="708" y="312" xlink:href="#p" />
<use x="720" y="312" xlink:href="#p" />
<use x="744" y="312" xlink:href="#p" />
<use x="780" y="312" xlink:href="#p" />
<use x="12" y="324" xlink:href="#p" />
<use x="36" y="324" xlink:href="#p" />
<use x="60" y="324" xlink:href="#p" />
<use x="72" y="324" xlink:href="#p" />
<use x="84" y="324" xlink:href="#p" />
<use x="96" y="324" xlink:href="#p" />
<use x="144" y="324" xlink:href="#p" />
<use x="156" y="324" xlink:href="#p" />
<use x="168" y="324" xlink:href="#p" />
<use x="204" y="324" xlink:href="#p" />
<use x="240" y="324" xlink:href="#p" />
<use x="252" y="324" xlink:href="#p" />
<use x="264" y="324" xlink:href="#p" />
<use x="276" y="324" xlink:href="#p" />
<use x="288" y="324" xlink:href="#p" />
<use x="336" y="324" xlink:href="#p" />
<use x="348" y="324" xlink:href="#p" />
<use x="360" y="324" xlink:href="#p" />
<use x="372" y="324" xlink:href="#p" />
<use x="456" y="324" xlink:href="#p" />
<use x="480" y="324" xlink:href="#p" />
<use x="492" y="324" xlink:href="#p" />
<use x="504" y="324" xlink:href="#p" />
<use x="552" y="324" xlink:href="#p" />
<use x="564" y="324" xlink:href="#p" />
<use x="588" y="324" xlink:href="#p" />
<use x="612" y="324" xlink:href="#p" />
<use x="672" y="324" xlink:href="#p" />
<use x="696" y="324" xlink:href="#p" />
<use x="720" y="324" xlink:href="#p" />
<use x="756" y="324" xlink:href="#p" />
<use x="768" y="324" xlink:href="#p" />
<use x="12" y="336" xlink:href="#p" />
<use x="72" y="336" xlink:href="#p" />
<use x="96" y="336" xlink:href="#p" />
<use x="108" y="336" xlink:href="#p" />
<use x="120" y="336" xlink:href="#p" />
<use x="144" y="336" xlink:href="#p" />
<use x="156" y="336" xlink:href="#p" />
<use x="180" y="336" xlink:href="#p" />
<use x="204" y="336" xlink:href="#p" />
<use x="216" y="336" xlink:href="#p" />
<use x="228" y="336" xlink:href="#p" />
<use x="252" y="336" xlink:href="#p" />
<use x="264" y="336" xlink:href="#p" />
<use x="288" y="336" xlink:href="#p" />
<use x="336" y="336" xlink:href="#p" />
<use x="348" y="336" xlink:href="#p" />
<use x="396" y="336" xlink:href="#p" />
<use x="408" y="336" xlink:href="#p" />
<use x="420" y="336" xlink:href="#p" />
<use x="456" y="336" xlink:href="#p" />
<use x="480" y="336" xlink:href="#p" />
<use x="516" y="336" xlink:href="#p" />
<use x="528" y="336" xlink:href="#p" />
<use x="540" y="336" xlink:href="#p" />
<use x="552" y="336" xlink:href="#p" />
<use x="564" y="336" xlink:href="#p" />
<use x="588" y="336" xlink:href="#p" />
<use x="612" y="336" xlink:href="#p" />
<use x="660" y="336" xlink:href="#p" />
<use x="684" y="336" xlink:href="#p" />
<use x="696" y="336" xlink:href="#p" />
<use x="708" y="336" xlink:href="#p" />
<use x="744" y="336" xlink:href="#p" />
<use x="768" y="336" xlink:href="#p" />
<use x="12" y="348" xlink:href="#p" />
<use x="36" y="348" xlink:href="#p" />
<use x="48" y="348" xlink:href="#p" />
<use x="60" y="348" xlink:href="#p" />
<use x="84" y="348" xlink:href="#p" />
<use x="96" y="348" xlink:href="#p" />
<use x="108" y="348" xlink:href="#p" />
<use x="144" y="348" xlink:href="#p" />
<use x="180" y="348" xlink:href="#p" />
<use x="192" y="348" xlink:href="#p" />
<use x="216" y="348" xlink:href="#p" />
<use x="228" y="348" xlink:href="#p" />
<use x="264" y="348" xlink:href="#p" />
<use x="276" y="348" xlink:href="#p" />
<use x="312" y="348" xlink:href="#p" />
<use x="384" y="348" xlink:href="#p" />
<use x="396" y="348" xlink:href="#p" />
<use x="408" y="348" xlink:href="#p" />
<use x="420" y="348" xlink:href="#p" />
<use x="432" y="348" xlink:href="#p" />
<use x="444" y="348" xlink:href="#p" />
<use x="456" y="348" xlink:href="#p" />
<use x="480" y="348" xlink:href="#p" />
<use x="576" y="348" xlink:href="#p" />
<use x="600" y="348" xlink:href="#p" />
<use x="624" y="348" xlink:href="#p" />
<use x="648" y="348" xlink:href="#p" />
<use x="696" y="348" xlink:href="#p" />
<use x="720" y="348" xlink:href="#p" />
<use x="732" y="348" xlink:href="#p" />
<use x="744" y="348" xlink:href="#p" />
<use x="756" y="348" xlink:href="#p" />
<use x="768" y="348" xlink:href="#p" />
<use x="12" y="360" xlink:href="#p" />
<use x="36" y="360" xlink:href="#p" />
<use x="96" y="360" xlink:href="#p" />
<use x="108" y="360" xlink:href="#p" />
<use x="120" y="360" xlink:href="#p" />
<use x="132" y="360" xlink:href="#p" />
<use x="168" y="360" xlink:href="#p" />
<use x="180" y="360" xlink:href="#p" />
<use x="192" y="360" xlink:href="#p" />
<use x="216" y="360" xlink:href="#p" />
<use x="228" y="360" xlink:href="#p" />
<use x="240" y="360" xlink:href="#p" />
<use x="252" y="360" xlink:href="#p" />
<use x="276" y="360" xlink:href="#p" />
<use x="288" y="360" xlink:href="#p" />
<use x="300" y="360" xlink:href="#p" />
<use x="312" y="360" xlink:href="#p" />
<use x="324" y="360" xlink:href="#p" />
<use x="336" y="360" xlink:href="#p" />
<use x="348" y="360" xlink:href="#p" />
<use x="372" y="360" xlink:href="#p" />
<use x="384" y="360" xlink:href="#p" />
<use x="432" y="360" xlink:href="#p" />
<use x="444" y="360" xlink:href="#p" />
<use x="468" y="360" xlink:href="#p" />
<use x="504" y="360" xlink:href="#p" />
<use x="540" y="360" xlink:href="#p" />
<use x="552" y="360" xlink:href="#p" />
<use x="600" y="360" xlink:href="#p" />
<use x="636" y="360" xlink:href="#p" />
<use x="648" y="360" xlink:href="#p" />
<use x="660" y="360" xlink:href="#p" />
<use x="672" y="360" xlink:href="#p" />
<use x="696" y="360" xlink:href="#p" />
<use x="720" y="360" xlink:href="#p" />
<use x="768" y="360" xlink:href="#p" />
<use x="12" y="372" xlink:href="#p" />
<use x="36" y="372" xlink:href="#p" />
<use x="60" y="372" xlink:href="#p" />
<use x="72" y="372" xlink:href="#p" />
<use x="84" y="372" xlink:href="#p" />
<use x="96" y="372" xlink:href="#p" />
<use x="108" y="372" xlink:href="#p" />
<use x="156" y="372" xlink:href="#p" />
<use x="180" y="372" xlink:href="#p" />
<use x="192" y="372" xlink:href="#p" />
<use x="216" y="372" xlink:href="#p" />
<use x="240" y="372" xlink:href="#p" />
<use x="288" y="372" xlink:href="#p" />
<use x="300" y="372" xlink:href="#p" />
<use x="312" y="372" xlink:href="#p" />
<use x="324" y="372" xlink:href="#p" />
<use x="372" y="372" xlink:href="#p" />
<use x="384" y="372" xlink:href="#p" />
<use x="396" y="372" xlink:href="#p" />
<use x="408" y="372" xlink:href="#p" />
<use x="420" y="372" xlink:href="#p" />
<use x="444" y="372" xlink:href="#p" />
<use x="468" y="372" xlink:href="#p" />
<use x="504" y="372" xlink:href="#p" />
<use x="540" y="372" xlink:href="#p" />
<use x="564" y="372" xlink:href="#p" />
<use x="612" y="372" xlink:href="#p" />
<use x="624" y="372" xlink:href="#p" />
<use x="648" y="372" xlink:href="#p" />
<use x="660" y="372" xlink:href="#p" />
<use x="672" y="372" xlink:href="#p" />
<use x="684" y="372" xlink:href="#p" />
<use x="696" y="372" xlink:href="#p" />
<use x="708" y="372" xlink:href="#p" />
<use x="720" y="372" xlink:href="#p" />
<use x="732" y="372" xlink:href="#p" />
<use x="780" y="372" xlink:href="#p" />
<use x="24" y="384" xlink:href="#p" />
<use x="36" y="384" xlink:href="#p" />
<use x="60" y="384" xlink:href="#p" />
<use x="108" y="384" xlink:href="#p" />
<use x="120" y="384" xlink:href="#p" />
<use x="180" y="384" xlink:href="#p" />
<use x="192" y="384" xlink:href="#p" />
<use x="204" y="384" xlink:href="#p" />
<use x="216" y="384" xlink:href="#p" />
<use x="228" y="384" xlink:href="#p" />
<use x="240" y="384" xlink:href="#p" />
<use x="276" y="384" xlink:href="#p" />
<use x="312" y="384" xlink:href="#p" />
<use x="324" y="384" xlink:href="#p" />
<use x="360" y="384" xlink:href="#p" />
<use x="372" y="384" xlink:href="#p" />
<use x="420" y="384" xlink:href="#p" />
<use x="432" y="384" xlink:href="#p" />
<use x="444" y="384" xlink:href="#p" />
<use x="456" y="384" xlink:href="#p" />
<use x="480" y="384" xlink:href="#p" />
<use x="492" y="384" xlink:href="#p" />
<use x="504" y="384" xlink:href="#p" />
<use x="528" y="384" xlink:href="#p" />
<use x="540" y="384" xlink:href="#p" />
<use x="552" y="384" xlink:href="#p" />
<use x="576" y="384" xlink:href="#p" />
<use x="588" y="384" xlink:href="#p" />
<use x="600" y="384" xlink:href="#p" />
<use x="684" y="384" xlink:href="#p" />
<use x="732" y="384" xlink:href="#p" />
<use x="756" y="384" xlink:href="#p" />
<use x="36" y="396" xlink:href="#p" />
<use x="48" y="396" xlink:href="#p" />
<use x="60" y="396" xlink:href="#p" />
<use x="84" y="396" xlink:href="#p" />
<use x="108" y="396" xlink:href="#p" />
<use x="120" y="396" xlink:href="#p" />
<use x="144" y="396" xlink:href="#p" />
<use x="168" y="396" xlink:href="#p" />
<use x="180" y="396" xlink:href="#p" />
<use x="192" y="396" xlink:href="#p" />
<use x="216" y="396" xlink:href="#p" />
<use x="228" y="396" xlink:href="#p" />
<use x="240" y="396" xlink:href="#p" />
<use x="264" y="396" xlink:href="#p" />
<use x="276" y="396" xlink:href="#p" />
<use x="300" y="396" xlink:href="#p" />
<use x="312" y="396" xlink:href="#p" />
<use x="336" y="396" xlink:href="#p" />
<use x="372" y="396" xlink:href="#p" />
<use x="396" y="396" xlink:href="#p" />
<use x="420" y="396" xlink:href="#p" />
<use x="432" y="396" xlink:href="#p" />
<use x="456" y="396" xlink:href="#p" />
<use x="468" y="396" xlink:href="#p" />
<use x="480" y="396" xlink:href="#p" />
<use x="516" y="396" xlink:href="#p" />
<use x="528" y="396" xlink:href="#p" />
<use x="552" y="396" xlink:href="#p" />
<use x="564" y="396" xlink:href="#p" />
<use x="576" y="396" xlink:href="#p" />
<use x="600" y="396" xlink:href="#p" />
<use x="624" y="396" xlink:href="#p" />
<use x="648" y="396" xlink:href="#p" />
<use x="660" y="396" xlink:href="#p" />
<use x="684" y="396" xlink:href="#p" />
<use x="708" y="396" xlink:href="#p" />
<use x="732" y="396" xlink:href="#p" />
<use x="12" y="408" xlink:href="#p" />
<use x="24" y="408" xlink:href="#p" />
<use x="36" y="408" xlink:href="#p" />
<use x="48" y="408" xlink:href="#p" />
<use x="60" y="408" xlink:href="#p" />
<use x="108" y="408" xlink:href="#p" />
<use x="120" y="408" xlink:href="#p" />
<use x="132" y="408" xlink:href="#p" />
<use x="144" y="408" xlink:href="#p" />
<use x="204" y="408" xlink:href="#p" />
<use x="216" y="408" xlink:href="#p" />
<use x="228" y="408" xlink:href="#p" />
<use x="240" y="408" xlink:href="#p" />
<use x="252" y="408" xlink:href="#p" />
<use x="300" y="408" xlink:href="#p" />
<use x="324" y="408" xlink:href="#p" />
<use x="360" y="408" xlink:href="#p" />
<use x="372" y="408" xlink:href="#p" />
<use x="420" y="408" xlink:href="#p" />
<use x="480" y="408" xlink:href="#p" />
<use x="492" y="408" xlink:href="#p" />
<use x="528" y="408" xlink:href="#p" />
<use x="552" y="408" xlink:href="#p" />
<use x="564" y="408" xlink:href="#p" />
<use x="588" y="408" xlink:href="#p" />
<use x="612" y="408" xlink:href="#p" />
<use x="636" y="408" xlink:href="#p" />
<use x="660" y="408" xlink:href="#p" />
<use x="684" y="408" xlink:href="#p" />
<use x="732" y="408" xlink:href="#p" />
<use x="744" y="408" xlink:href="#p" />
<use x="756" y="408" xlink:href="#p" />
<use x="768" y="408" xlink:href="#p" />
<use x="12" y="420" xlink:href="#p" />
<use x="24" y="420" xlink:href="#p" />
<use x="60" y="420" xlink:href="#p" />
<use x="72" y="420" xlink:href="#p" />
<use x="84" y="420" xlink:href="#p" />
<use x="96" y="420" xlink:href="#p" />
<use x="108" y="420" xlink:href="#p" />
<use x="120" y="420" xlink:href="#p" />
<use x="144" y="420" xlink:href="#p" />
<use x="156" y="420" xlink:href="#p" />
<use x="168" y="420" xlink:href="#p" />
<use x="180" y="420" xlink:href="#p" />
<use x="192" y="420" xlink:href="#p" />
<use x="204" y="420" xlink:href="#p" />
<use x="228" y="420" xlink:href="#p" />
<use x="240" y="420" xlink:href="#p" />
<use x="324" y="420" xlink:href="#p" />
<use x="336" y="420" xlink:href="#p" />
<use x="348" y="420" xlink:href="#p" />
<use x="372" y="420" xlink:href="#p" />
<use x="384" y="420" xlink:href="#p" />
<use x="396" y="420" xlink:href="#p" />
<use x="408" y="420" xlink:href="#p" />
<use x="420" y="420" xlink:href="#p" />
<use x="444" y="420" xlink:href="#p" />
<use x="480" y="420" xlink:href="#p" />
<use x="492" y="420" xlink:href="#p" />
<use x="504" y="420" xlink:href="#p" />
<use x="516" y="420" xlink:href="#p" />
<use x="552" y="420" xlink:href="#p" />
<use x="576" y="420" xlink:href="#p" />
<use x="588" y="420" xlink:href="#p" />
<use x="600" y="420" xlink:href="#p" />
<use x="612" y="420" xlink:href="#p" />
<use x="624" y="420" xlink:href="#p" />
<use x="648" y="420" xlink:href="#p" />
<use x="672" y="420" xlink:href="#p" />
<use x="684" y="420" xlink:href="#p" />
<use x="696" y="420" xlink:href="#p" />
<use x="708" y="420" xlink:href="#p" />
<use x="720" y="420" xlink:href="#p" />
<use x="732" y="420" xlink:href="#p" />
<use x="768" y="420" xlink:href="#p" />
<use x="48" y="432" xlink:href="#p" />
<use x="60" y="432" xlink:href="#p" />
<use x="120" y="432" xlink:href="#p" />
<use x="132" y="432" xlink:href="#p" />
<use x="144" y="432" xlink:href="#p" />
<use x="168" y="432" xlink:href="#p" />
<use x="180" y="432" xlink:href="#p" />
<use x="192" y="432" xlink:href="#p" />
<use x="204" y="432" xlink:href="#p" />
<use x="216" y="432" xlink:href="#p" />
<use x="228" y="432" xlink:href="#p" />
<use x="252" y="432" xlink:href="#p" />
<use x="300" y="432" xlink:href="#p" />
<use x="348" y="432" xlink:href="#p" />
<use x="372" y="432" xlink:href="#p" />
<use x="396" y="432" xlink:href="#p" />
<use x="408" y="432" xlink:href="#p" />
<use x="432" y="432" xlink:href="#p" />
<use x="444" y="432" xlink:href="#p" />
<use x="468" y="432" xlink:href="#p" />
<use x="480" y="432" xlink:href="#p" />
<use x="492" y="432" xlink:href="#p" />
<use x="552" y="432" xlink:href="#p" />
<use x="564" y="432" xlink:href="#p" />
<use x="600" y="432" xlink:href="#p" />
<use x="612" y="432" xlink:href="#p" />
<use x="648" y="432" xlink:href="#p" />
<use x="660" y="432" xlink:href="#p" />
<use x="672" y="432" xlink:href="#p" />
<use x="696" y="432" xlink:href="#p" />
<use x="708" y="432" xlink:href="#p" />
<use x="732" y="432" xlink:href="#p" />
<use x="780" y="432" xlink:href="#p" />
<use x="48" y="444" xlink:href="#p" />
<use x="60" y="444" xlink:href="#p" />
<use x="72" y="444" xlink:href="#p" />
<use x="84" y="444" xlink:href="#p" />
<use x="108" y="444" xlink:href="#p" />
<use x="156" y="444" xlink:href="#p" />
<use x="192" y="444" xlink:href="#p" />
<use x="204" y="444" xlink:href="#p" />
<use x="216" y="444" xlink:href="#p" />
<use x="240" y="444" xlink:href="#p" />
<use x="252" y="444" xlink:href="#p" />
<use x="288" y="444" xlink:href="#p" />
<use x="336" y="444" xlink:href="#p" />
<use x="348" y="444" xlink:href="#p" />
<use x="396" y="444" xlink:href="#p" />
<use x="432" y="444" xlink:href="#p" />
<use x="444" y="444" xlink:href="#p" />
<use x="456" y="444" xlink:href="#p" />
<use x="468" y="444" xlink:href="#p" />
<use x="492" y="444" xlink:href="#p" />
<use x="504" y="444" xlink:href="#p" />
<use x="516" y="444" xlink:href="#p" />
<use x="540" y="444" xlink:href="#p" />
<use x="600" y="444" xlink:href="#p" />
<use x="612" y="444" xlink:href="#p" />
<use x="624" y="444" xlink:href="#p" />
<use x="636" y="444" xlink:href="#p" />
<use x="648" y="444" xlink:href="#p" />
<use x="660" y="444" xlink:href="#p" />
<use x="672" y="444" xlink:href="#p" />
<use x="684" y="444" xlink:href="#p" />
<use x="744" y="444" xlink:href="#p" />
<use x="780" y="444" xlink:href="#p" />
<use x="48" y="456" xlink:href="#p" />
<use x="96" y="456" xlink:href="#p" />
<use x="108" y="456" xlink:href="#p" />
<use x="180" y="456" xlink:href="#p" />
<use x="192" y="456" xlink:href="#p" />
<use x="216" y="456" xlink:href="#p" />
<use x="228" y="456" xlink:href="#p" />
<use x="264" y="456" xlink:href="#p" />
<use x="276" y="456" xlink:href="#p" />
<use x="348" y="456" xlink:href="#p" />
<use x="360" y="456" xlink:href="#p" />
<use x="408" y="456" xlink:href="#p" />
<use x="468" y="456" xlink:href="#p" />
<use x="480" y="456" xlink:href="#p" />
<use x="492" y="456" xlink:href="#p" />
<use x="504" y="456" xlink:href="#p" />
<use x="588" y="456" xlink:href="#p" />
<use x="600" y="456" xlink:href="#p" />
<use x="612" y="456" xlink:href="#p" />
<use x="624" y="456" xlink:href="#p" />
<use x="636" y="456" xlink:href="#p" />
<use x="660" y="456" xlink:href="#p" />
<use x="672" y="456" xlink:href="#p" />
<use x="696" y="456" xlink:href="#p" />
<use x="708" y="456" xlink:href="#p" />
<use x="720" y="456" xlink:href="#p" />
<use x="732" y="456" xlink:href="#p" />
<use x="744" y="456" xlink:href="#p" />
<use x="756" y="456" xlink:href="#p" />
<use x="12" y="468" xlink:href="#p" />
<use x="48" y="468" xlink:href="#p" />
<use x="60" y="468" xlink:href="#p" />
<use x="72" y="468" xlink:href="#p" />
<use x="84" y="468" xlink:href="#p" />
<use x="96" y="468" xlink:href="#p" />
<use x="120" y="468" xlink:href="#p" />
<use x="132" y="468" xlink:href="#p" />
<use x="180" y="468" xlink:href="#p" />
<use x="216" y="468" xlink:href="#p" />
<use x="240" y="468" xlink:href="#p" />
<use x="252" y="468" xlink:href="#p" />
<use x="276" y="468" xlink:href="#p" />
<use x="288" y="468" xlink:href="#p" />
<use x="300" y="468" xlink:href="#p" />
<use x="324" y="468" xlink:href="#p" />
<use x="336" y="468" xlink:href="#p" />
<use x="348" y="468" xlink:href="#p" />
<use x="372" y="468" xlink:href="#p" />
<use x="408" y="468" xlink:href="#p" />
<use x="444" y="468" xlink:href="#p" />
<use x="480" y="468" xlink:href="#p" />
<use x="492" y="468" xlink:href="#p" />
<use x="516" y="468" xlink:href="#p" />
<use x="528" y="468" xlink:href="#p" />
<use x="552" y="468" xlink:href="#p" />
<use x="564" y="468" xlink:href="#p" />
<use x="576" y="468" xlink:href="#p" />
<use x="588" y="468" xlink:href="#p" />
<use x="732" y="468" xlink:href="#p" />
<use x="744" y="468" xlink:href="#p" />
<use x="768" y="468" xlink:href="#p" />
<use x="36" y="480" xlink:href="#p" />
<use x="48" y="480" xlink:href="#p" />
<use x="72" y="480" xlink:href="#p" />
<use x="96" y="480" xlink:href="#p" />
<use x="108" y="480" xlink:href="#p" />
<use x="120" y="480" xlink:href="#p" />
<use x="132" y="480" xlink:href="#p" />
<use x="168" y="480" xlink:href="#p" />
<use x="228" y="480" xlink:href="#p" />
<use x="240" y="480" xlink:href="#p" />
<use x="264" y="480" xlink:href="#p" />
<use x="276" y="480" xlink:href="#p" />
<use x="288" y="480" xlink:href="#p" />
<use x="312" y="480" xlink:href="#p" />
<use x="336" y="480" xlink:href="#p" />
<use x="348" y="480" xlink:href="#p" />
<use x="372" y="480" xlink:href="#p" />
<use x="384" y="480" xlink:href="#p" />
<use x="396" y="480" xlink:href="#p" />
<use x="432" y="480" xlink:href="#p" />
<use x="444" y="480" xlink:href="#p" />
<use x="468" y="480" xlink:href="#p" />
<use x="504" y="480" xlink:href="#p" />
<use x="528" y="480" xlink:href="#p" />
<use x="600" y="480" xlink:href="#p" />
<use x="612" y="480" xlink:href="#p" />
<use x="636" y="480" xlink:href="#p" />
<use x="648" y="480" xlink:href="#p" />
<use x="660" y="480" xlink:href="#p" />
<use x="672" y="480" xlink:href="#p" />
<use x="684" y="480" xlink:href="#p" />
<use x="780" y="480" xlink:href="#p" />
<use x="12" y="492" xlink:href="#p" />
<use x="24" y="492" xlink:href="#p" />
<use x="60" y="492" xlink:href="#p" />
<use x="84" y="492" xlink:href="#p" />
<use x="96" y="492" xlink:href="#p" />
<use x="132" y="492" xlink:href="#p" />
<use x="144" y="492" xlink:href="#p" />
<use x="180" y="492" xlink:href="#p" />
<use x="204" y="492" xlink:href="#p" />
<use x="240" y="492" xlink:href="#p" />
<use x="252" y="492" xlink:href="#p" />
<use x="300" y="492" xlink:href="#p" />
<use x="372" y="492" xlink:href="#p" />
<use x="384" y="492" xlink:href="#p" />
<use x="396" y="492" xlink:href="#p" />
<use x="408" y="492" xlink:href="#p" />
<use x="420" y="492" xlink:href="#p" />
<use x="432" y="492" xlink:href="#p" />
<use x="444" y="492" xlink:href="#p" />
<use x="480" y="492" xlink:href="#p" />
<use x="528" y="492" xlink:href="#p" />
<use x="552" y="492" xlink:href="#p" />
<use x="564" y="492" xlink:href="#p" />
<use x="588" y="492" xlink:href="#p" />
<use x="600" y="492" xlink:href="#p" />
<use x="612" y="492" xlink:href="#p" />
<use x="636" y="492" xlink:href="#p" />
<use x="684" y="492" xlink:href="#p" />
<use x="696" y="492" xlink:href="#p" />
<use x="720" y="492" xlink:href="#p" />
<use x="732" y="492" xlink:href="#p" />
<use x="768" y="492" xlink:href="#p" />
<use x="12" y="504" xlink:href="#p" />
<use x="36" y="504" xlink:href="#p" />
<use x="48" y="504" xlink:href="#p" />
<use x="96" y="504" xlink:href="#p" />
<use x="156" y="504" xlink:href="#p" />
<use x="216" y="504" xlink:href="#p" />
<use x="228" y="504" xlink:href="#p" />
<use x="276" y="504" xlink:href="#p" />
<use x="288" y="504" xlink:href="#p" />
<use x="324" y="504" xlink:href="#p" />
<use x="372" y="504" xlink:href="#p" />
<use x="420" y="504" xlink:href="#p" />
<use x="444" y="504" xlink:href="#p" />
<use x="456" y="504" xlink:href="#p" />
<use x="480" y="504" xlink:href="#p" />
<use x="492" y="504" xlink:href="#p" />
<use x="516" y="504" xlink:href="#p" />
<use x="540" y="504" xlink:href="#p" />
<use x="552" y="504" xlink:href="#p" />
<use x="576" y="504" xlink:href="#p" />
<use x="600" y="504" xlink:href="#p" />
<use x="636" y="504" xlink:href="#p" />
<use x="648" y="504" xlink:href="#p" />
<use x="660" y="504" xlink:href="#p" />
<use x="684" y="504" xlink:href="#p" />
<use x="696" y="504" xlink:href="#p" />
<use x="720" y="504" xlink:href="#p" />
<use x="732" y="504" xlink:href="#p" />
<use x="756" y="504" xlink:href="#p" />
<use x="768" y="504" xlink:href="#p" />
<use x="780" y="504" xlink:href="#p" />
<use x="24" y="516" xlink:href="#p" />
<use x="60" y="516" xlink:href="#p" />
<use x="84" y="516" xlink:href="#p" />
<use x="96" y="516" xlink:href="#p" />
<use x="108" y="516" xlink:href="#p" />
<use x="144" y="516" xlink:href="#p" />
<use x="180" y="516" xlink:href="#p" />
<use x="192" y="516" xlink:href="#p" />
<use x="228" y="516" xlink:href="#p" />
<use x="264" y="516" xlink:href="#p" />
<use x="348" y="516" xlink:href="#p" />
<use x="360" y="516" xlink:href="#p" />
<use x="372" y="516" xlink:href="#p" />
<use x="384" y="516" xlink:href="#p" />
<use x="444" y="516" xlink:href="#p" />
<use x="468" y="516" xlink:href="#p" />
<use x="492" y="516" xlink:href="#p" />
<use x="504" y="516" xlink:href="#p" />
<use x="540" y="516" xlink:href="#p" />
<use x="564" y="516" xlink:href="#p" />
<use x="576" y="516" xlink:href="#p" />
<use x="588" y="516" xlink:href="#p" />
<use x="600" y="516" xlink:href="#p" />
<use x="612" y="516" xlink:href="#p" />
<use x="636" y="516" xlink:href="#p" />
<use x="660" y="516" xlink:href="#p" />
<use x="708" y="516" xlink:href="#p" />
<use x="720" y="516" xlink:href="#p" />
<use x="732" y="516" xlink:href="#p" />
<use x="780" y="516" xlink:href="#p" />
<use x="24" y="528" xlink:href="#p" />
<use x="36" y="528" xlink:href="#p" />
<use x="48" y="528" xlink:href="#p" />
<use x="60" y="528" xlink:href="#p" />
<use x="72" y="528" xlink:href="#p" />
<use x="108" y="528" xlink:href="#p" />
<use x="132" y="528" xlink:href="#p" />
<use x="168" y="528" xlink:href="#p" />
<use x="192" y="528" xlink:href="#p" />
<use x="204" y="528" xlink:href="#p" />
<use x="252" y="528" xlink:href="#p" />
<use x="276" y="528" xlink:href="#p" />
<use x="300" y="528" xlink:href="#p" />
<use x="312" y="528" xlink:href="#p" />
<use x="336" y="528" xlink:href="#p" />
<use x="372" y="528" xlink:href="#p" />
<use x="396" y="528" xlink:href="#p" />
<use x="408" y="528" xlink:href="#p" />
<use x="432" y="528" xlink:href="#p" />
<use x="456" y="528" xlink:href="#p" />
<use x="468" y="528" xlink:href="#p" />
<use x="480" y="528" xlink:href="#p" />
<use x="492" y="528" xlink:href="#p" />
<use x="504" y="528" xlink:href="#p" />
<use x="516" y="528" xlink:href="#p" />
<use x="552" y="528" xlink:href="#p" />
<use x="588" y="528" xlink:href="#p" />
<use x="600" y="528" xlink:href="#p" />
<use x="636" y="528" xlink:href="#p" />
<use x="648" y="528" xlink:href="#p" />
<use x="660" y="528" xlink:href="#p" />
<use x="672" y="528" xlink:href="#p" />
<use x="684" y="528" xlink:href="#p" />
<use x="696" y="528" xlink:href="#p" />
<use x="708" y="528" xlink:href="#p" />
<use x="720" y="528" xlink:href="#p" />
<use x="744" y="528" xlink:href="#p" />
<use x="768" y="528" xlink:href="#p" />
<use x="60" y="540" xlink:href="#p" />
<use x="72" y="540" xlink:href="#p" />
<use x="84" y="540" xlink:href="#p" />
<use x="96" y="540" xlink:href="#p" />
<use x="120" y="540" xlink:href="#p" />
<use x="156" y="540" xlink:href="#p" />
<use x="228" y="540" xlink:href="#p" />
<use x="240" y="540" xlink:href="#p" />
<use x="252" y="540" xlink:href="#p" />
<use x="264" y="540" xlink:href="#p" />
<use x="276" y="540" xlink:href="#p" />
<use x="288" y="540" xlink:href="#p" />
<use x="348" y="540" xlink:href="#p" />
<use x="360" y="540" xlink:href="#p" />
<use x="384" y="540" xlink:href="#p" />
<use x="396" y="540" xlink:href="#p" />
<use x="420" y="540" xlink:href="#p" />
<use x="432" y="540" xlink:href="#p" />
<use x="456" y="540" xlink:href="#p" />
<use x="480" y="540" xlink:href="#p" />
<use x="492" y="540" xlink:href="#p" />
<use x="504" y="540" xlink:href="#p" />
<use x="516" y="540" xlink:href="#p" />
<use x="528" y="540" xlink:href="#p" />
<use x="552" y="540" xlink:href="#p" />
<use x="564" y="540" xlink:href="#p" />
<use x="576" y="540" xlink:href="#p" />
<use x="588" y="540" xlink:href="#p" />
<use x="612" y="540" xlink:href="#p" />
<use x="624" y="540" xlink:href="#p" />
<use x="636" y="540" xlink:href="#p" />
<use x="660" y="540" xlink:href="#p" />
<use x="708" y="540" xlink:href="#p" />
<use x="720" y="540" xlink:href="#p" />
<use x="732" y="540" xlink:href="#p" />
<use x="756" y="540" xlink:href="#p" />
<use x="768" y="540" xlink:href="#p" />
<use x="60" y="552" xlink:href="#p" />
<use x="96" y="552" xlink:href="#p" />
<use x="120" y="552" xlink:href="#p" />
<use x="156" y="552" xlink:href="#p" />
<use x="180" y="552" xlink:href="#p" />
<use x="204" y="552" xlink:href="#p" />
<use x="216" y="552" xlink:href="#p" />
<use x="228" y="552" xlink:href="#p" />
<use x="252" y="552" xlink:href="#p" />
<use x="264" y="552" xlink:href="#p" />
<use x="276" y="552" xlink:href="#p" />
<use x="288" y="552" xlink:href="#p" />
<use x="300" y="552" xlink:href="#p" />
<use x="312" y="552" xlink:href="#p" />
<use x="360" y="552" xlink:href="#p" />
<use x="396" y="552" xlink:href="#p" />
<use x="408" y="552" xlink:href="#p" />
<use x="420" y="552" xlink:href="#p" />
<use x="432" y="552" xlink:href="#p" />
<use x="480" y="552" xlink:href="#p" />
<use x="492" y="552" xlink:href="#p" />
<use x="504" y="552" xlink:href="#p" />
<use x="552" y="552" xlink:href="#p" />
<use x="564" y="552" xlink:href="#p" />
<use x="612" y="552" xlink:href="#p" />
<use x="708" y="552" xlink:href="#p" />
<use x="720" y="552" xlink:href="#p" />
<use x="732" y="552" xlink:href="#p" />
<use x="744" y="552" xlink:href="#p" />
<use x="48" y="564" xlink:href="#p" />
<use x="60" y="564" xlink:href="#p" />
<use x="84" y="564" xlink:href="#p" />
<use x="108" y="564" xlink:href="#p" />
<use x="168" y="564" xlink:href="#p" />
<use x="192" y="564" xlink:href="#p" />
<use x="204" y="564" xlink:href="#p" />
<use x="228" y="564" xlink:href="#p" />
<use x="240" y="564" xlink:href="#p" />
<use x="264" y="564" xlink:href="#p" />
<use x="300" y="564" xlink:href="#p" />
<use x="312" y="564" xlink:href="#p" />
<use x="324" y="564" xlink:href="#p" />
<use x="348" y="564" xlink:href="#p" />
<use x="372" y="564" xlink:href="#p" />
<use x="384" y="564" xlink:href="#p" />
<use x="444" y="564" xlink:href="#p" />
<use x="456" y="564" xlink:href="#p" />
<use x="480" y="564" xlink:href="#p" />
<use x="492" y="564" xlink:href="#p" />
<use x="516" y="564" xlink:href="#p" />
<use x="528" y="564" xlink:href="#p" />
<use x="540" y="564" xlink:href="#p" />
<use x="552" y="564" xlink:href="#p" />
<use x="576" y="564" xlink:href="#p" />
<use x="588" y="564" xlink:href="#p" />
<use x="600" y="564" xlink:href="#p" />
<use x="648" y="564" xlink:href="#p" />
<use x="660" y="564" xlink:href="#p" />
<use x="684" y="564" xlink:href="#p" />
<use x="696" y="564" xlink:href="#p" />
<use x="720" y="564" xlink:href="#p" />
<use x="756" y="564" xlink:href="#p" />
<use x="12" y="576" xlink:href="#p" />
<use x="72" y="576" xlink:href="#p" />
<use x="96" y="576" xlink:href="#p" />
<use x="108" y="576" xlink:href="#p" />
<use x="120" y="576" xlink:href="#p" />
<use x="168" y="576" xlink:href="#p" />
<use x="252" y="576" xlink:href="#p" />
<use x="336" y="576" xlink:href="#p" />
<use x="396" y="576" xlink:href="#p" />
<use x="444" y="576" xlink:href="#p" />
<use x="456" y="576" xlink:href="#p" />
<use x="468" y="576" xlink:href="#p" />
<use x="480" y="576" xlink:href="#p" />
<use x="516" y="576" xlink:href="#p" />
<use x="540" y="576" xlink:href="#p" />
<use x="552" y="576" xlink:href="#p" />
<use x="600" y="576" xlink:href="#p" />
<use x="624" y="576" xlink:href="#p" />
<use x="648" y="576" xlink:href="#p" />
<use x="660" y="576" xlink:href="#p" />
<use x="672" y="576" xlink:href="#p" />
<use x="684" y="576" xlink:href="#p" />
<use x="696" y="576" xlink:href="#p" />
<use x="708" y="576" xlink:href="#p" />
<use x="732" y="576" xlink:href="#p" />
<use x="744" y="576" xlink:href="#p" />
<use x="768" y="576" xlink:href="#p" />
<use x="780" y="576" xlink:href="#p" />
<use x="36" y="588" xlink:href="#p" />
<use x="48" y="588" xlink:href="#p" />
<use x="60" y="588" xlink:href="#p" />
<use x="84" y="588" xlink:href="#p" />
<use x="96" y="588" xlink:href="#p" />
<use x="108" y="588" xlink:href="#p" />
<use x="120" y="588" xlink:href="#p" />
<use x="168" y="588" xlink:href="#p" />
<use x="180" y="588" xlink:href="#p" />
<use x="192" y="588" xlink:href="#p" />
<use x="216" y="588" xlink:href="#p" />
<use x="228" y="588" xlink:href="#p" />
<use x="240" y="588" xlink:href="#p" />
<use x="288" y="588" xlink:href="#p" />
<use x="300" y="588" xlink:href="#p" />
<use x="312" y="588" xlink:href="#p" />
<use x="324" y="588" xlink:href="#p" />
<use x="336" y="588" xlink:href="#p" />
<use x="348" y="588" xlink:href="#p" />
<use x="360" y="588" xlink:href="#p" />
<use x="372" y="588" xlink:href="#p" />
<use x="384" y="588" xlink:href="#p" />
<use x="432" y="588" xlink:href="#p" />
<use x="456" y="588" xlink:href="#p" />
<use x="468" y="588" xlink:href="#p" />
<use x="480" y="588" xlink:href="#p" />
<use x="492" y="588" xlink:href="#p" />
<use x="504" y="588" xlink:href="#p" />
<use x="516" y="588" xlink:href="#p" />
<use x="540" y="588" xlink:href="#p" />
<use x="564" y="588" xlink:href="#p" />
<use x="576" y="588" xlink:href="#p" />
<use x="600" y="588" xlink:href="#p" />
<use x="612" y="588" xlink:href="#p" />
<use x="672" y="588" xlink:href="#p" />
<use x="684" y="588" xlink:href="#p" />
<use x="708" y="588" xlink:href="#p" />
<use x="732" y="588" xlink:href="#p" />
<use x="744" y="588" xlink:href="#p" />
<use x="24" y="600" xlink:href="#p" />
<use x="36" y="600" xlink:href="#p" />
<use x="60" y="600" xlink:href="#p" />
<use x="72" y="600" xlink:href="#p" />
<use x="120" y="600" xlink:href="#p" />
<use x="156" y="600" xlink:href="#p" />
<use x="168" y="600" xlink:href="#p" />
<use x="180" y="600" xlink:href="#p" />
<use x="204" y="600" xlink:href="#p" />
<use x="216" y="600" xlink:href="#p" />
<use x="264" y="600" xlink:href="#p" />
<use x="276" y="600" xlink:href="#p" />
<use x="288" y="600" xlink:href="#p" />
<use x="300" y="600" xlink:href="#p" />
<use x="324" y="600" xlink:href="#p" />
<use x="336" y="600" xlink:href="#p" />
<use x="360" y="600" xlink:href="#p" />
<use x="396" y="600" xlink:href="#p" />
<use x="408" y="600" xlink:href="#p" />
<use x="480" y="600" xlink:href="#p" />
<use x="492" y="600" xlink:href="#p" />
<use x="516" y="600" xlink:href="#p" />
<use x="540" y="600" xlink:href="#p" />
<use x="576" y="600" xlink:href="#p" />
<use x="600" y="600" xlink:href="#p" />
<use x="612" y="600" xlink:href="#p" />
<use x="636" y="600" xlink:href="#p" />
<use x="660" y="600" xlink:href="#p" />
<use x="672" y="600" xlink:href="#p" />
<use x="696" y="600" xlink:href="#p" />
<use x="720" y="600" xlink:href="#p" />
<use x="732" y="600" xlink:href="#p" />
<use x="744" y="600" xlink:href="#p" />
<use x="756" y="600" xlink:href="#p" />
<use x="768" y="600" xlink:href="#p" />
<use x="780" y="600" xlink:href="#p" />
<use x="12" y="612" xlink:href="#p" />
<use x="24" y="612" xlink:href="#p" />
<use x="36" y="612" xlink:href="#p" />
<use x="48" y="612" xlink:href="#p" />
<use x="60" y="612" xlink:href="#p" />
<use x="84" y="612" xlink:href="#p" />
<use x="120" y="612" xlink:href="#p" />
<use x="144" y="612" xlink:href="#p" />
<use x="156" y="612" xlink:href="#p" />
<use x="216" y="612" xlink:href="#p" />
<use x="228" y="612" xlink:href="#p" />
<use x="264" y="612" xlink:href="#p" />
<use x="276" y="612" xlink:href="#p" />
<use x="288" y="612" xlink:href="#p" />
<use x="300" y="612" xlink:href="#p" />
<use x="324" y="612" xlink:href="#p" />
<use x="336" y="612" xlink:href="#p" />
<use x="348" y="612" xlink:href="#p" />
<use x="360" y="612" xlink:href="#p" />
<use x="372" y="612" xlink:href="#p" />
<use x="384" y="612" xlink:href="#p" />
<use x="420" y="612" xlink:href="#p" />
<use x="468" y="612" xlink:href="#p" />
<use x="516" y="612" xlink:href="#p" />
<use x="528" y="612" xlink:href="#p" />
<use x="600" y="612" xlink:href="#p" />
<use x="612" y="612" xlink:href="#p" />
<use x="648" y="612" xlink:href="#p" />
<use x="684" y="612" xlink:href="#p" />
<use x="744" y="612" xlink:href="#p" />
<use x="768" y="612" xlink:href="#p" />
<use x="48" y="624" xlink:href="#p" />
<use x="72" y="624" xlink:href="#p" />
<use x="132" y="624" xlink:href="#p" />
<use x="192" y="624" xlink:href="#p" />
<use x="204" y="624" xlink:href="#p" />
<use x="240" y="624" xlink:href="#p" />
<use x="264" y="624" xlink:href="#p" />
<use x="288" y="624" xlink:href="#p" />
<use x="312" y="624" xlink:href="#p" />
<use x="324" y="624" xlink:href="#p" />
<use x="348" y="624" xlink:href="#p" />
<use x="360" y="624" xlink:href="#p" />
<use x="372" y="624" xlink:href="#p" />
<use x="384" y="624" xlink:href="#p" />
<use x="408" y="624" xlink:href="#p" />
<use x="444" y="624" xlink:href="#p" />
<use x="456" y="624" xlink:href="#p" />
<use x="480" y="624" xlink:href="#p" />
<use x="552" y="624" xlink:href="#p" />
<use x="564" y="624" xlink:href="#p" />
<use x="576" y="624" xlink:href="#p" />
<use x="612" y="624" xlink:href="#p" />
<use x="660" y="624" xlink:href="#p" />
<use x="684" y="624" xlink:href="#p" />
<use x="696" y="624" xlink:href="#p" />
<use x="708" y="624" xlink:href="#p" />
<use x="732" y="624" xlink:href="#p" />
<use x="756" y="624" xlink:href="#p" />
<use x="780" y="624" xlink:href="#p" />
<use x="36" y="636" xlink:href="#p" />
<use x="48" y="636" xlink:href="#p" />
<use x="72" y="636" xlink:href="#p" />
<use x="84" y="636" xlink:href="#p" />
<use x="96" y="636" xlink:href="#p" />
<use x="108" y="636" xlink:href="#p" />
<use x="132" y="636" xlink:href="#p" />
<use x="168" y="636" xlink:href="#p" />
<use x="180" y="636" xlink:href="#p" />
<use x="192" y="636" xlink:href="#p" />
<use x="228" y="636" xlink:href="#p" />
<use x="276" y="636" xlink:href="#p" />
<use x="288" y="636" xlink:href="#p" />
<use x="300" y="636" xlink:href="#p" />
<use x="324" y="636" xlink:href="#p" />
<use x="348" y="636" xlink:href="#p" />
<use x="360" y="636" xlink:href="#p" />
<use x="372" y="636" xlink:href="#p" />
<use x="408" y="636" xlink:href="#p" />
<use x="420" y="636" xlink:href="#p" />
<use x="444" y="636" xlink:href="#p" />
<use x="456" y="636" xlink:href="#p" />
<use x="468" y="636" xlink:href="#p" />
<use x="492" y="636" xlink:href="#p" />
<use x="516" y="636" xlink:href="#p" />
<use x="528" y="636" xlink:href="#p" />
<use x="552" y="636" xlink:href="#p" />
<use x="564" y="636" xlink:href="#p" />
<use x="576" y="636" xlink:href="#p" />
<use x="588" y="636" xlink:href="#p" />
<use x="600" y="636" xlink:href="#p" />
<use x="612" y="636" xlink:href="#p" />
<use x="624" y="636" xlink:href="#p" />
<use x="660" y="636" xlink:href="#p" />
<use x="672" y="636" xlink:href="#p" />
<use x="684" y="636" xlink:href="#p" />
<use x="696" y="636" xlink:href="#p" />
<use x="708" y="636" xlink:href="#p" />
<use x="720" y="636" xlink:href="#p" />
<use x="732" y="636" xlink:href="#p" />
<use x="756" y="636" xlink:href="#p" />
<use x="768" y="636" xlink:href="#p" />
<use x="36" y="648" xlink:href="#p" />
<use x="96" y="648" xlink:href="#p" />
<use x="120" y="648" xlink:href="#p" />
<use x="204" y="648" xlink:href="#p" />
<use x="228" y="648" xlink:href="#p" />
<use x="252" y="648" xlink:href="#p" />
<use x="276" y="648" xlink:href="#p" />
<use x="288" y="648" xlink:href="#p" />
<use x="324" y="648" xlink:href="#p" />
<use x="336" y="648" xlink:href="#p" />
<use x="348" y="648" xlink:href="#p" />
<use x="360" y="648" xlink:href="#p" />
<use x="384" y="648" xlink:href="#p" />
<use x="408" y="648" xlink:href="#p" />
<use x="468" y="648" xlink:href="#p" />
<use x="492" y="648" xlink:href="#p" />
<use x="516" y="648" xlink:href="#p" />
<use x="552" y="648" xlink:href="#p" />
<use x="588" y="648" xlink:href="#p" />
<use x="624" y="648" xlink:href="#p" />
<use x="660" y="648" xlink:href="#p" />
<use x="672" y="648" xlink:href="#p" />
<use x="696" y="648" xlink:href="#p" />
<use x="708" y="648" xlink:href="#p" />
<use x="720" y="648" xlink:href="#p" />
<use x="732" y="648" xlink:href="#p" />
<use x="756" y="648" xlink:href="#p" />
<use x="768" y="648" xlink:href="#p" />
<use x="780" y="648" xlink:href="#p" />
<use x="36" y="660" xlink:href="#p" />
<use x="48" y="660" xlink:href="#p" />
<use x="72" y="660" xlink:href="#p" />
<use x="84" y="660" xlink:href="#p" />
<use x="96" y="660" xlink:href="#p" />
<use x="108" y="660" xlink:href="#p" />
<use x="144" y="660" xlink:href="#p" />
<use x="168" y="660" xlink:href="#p" />
<use x="180" y="660" xlink:href="#p" />
<use x="192" y="660" xlink:href="#p" />
<use x="204" y="660" xlink:href="#p" />
<use x="228" y="660" xlink:href="#p" />
<use x="240" y="660" xlink:href="#p" />
<use x="252" y="660" xlink:href="#p" />
<use x="264" y="660" xlink:href="#p" />
<use x="300" y="660" xlink:href="#p" />
<use x="312" y="660" xlink:href="#p" />
<use x="324" y="660" xlink:href="#p" />
<use x="360" y="660" xlink:href="#p" />
<use x="372" y="660" xlink:href="#p" />
<use x="408" y="660" xlink:href="#p" />
<use x="420" y="660" xlink:href="#p" />
<use x="444" y="660" xlink:href="#p" />
<use x="480" y="660" xlink:href="#p" />
<use x="516" y="660" xlink:href="#p" />
<use x="588" y="660" xlink:href="#p" />
<use x="600" y="660" xlink:href="#p" />
<use x="612" y="660" xlink:href="#p" />
<use x="648" y="660" xlink:href="#p" />
<use x="660" y="660" xlink:href="#p" />
<use x="672" y="660" xlink:href="#p" />
<use x="684" y="660" xlink:href="#p" />
<use x="708" y="660" xlink:href="#p" />
<use x="720" y="660" xlink:href="#p" />
<use x="744" y="660" xlink:href="#p" />
<use x="780" y="660" xlink:href="#p" />
<use x="12" y="672" xlink:href="#p" />
<use x="48" y="672" xlink:href="#p" />
<use x="108" y="672" xlink:href="#p" />
<use x="120" y="672" xlink:href="#p" />
<use x="132" y="672" xlink:href="#p" />
<use x="144" y="672" xlink:href="#p" />
<use x="156" y="672" xlink:href="#p" />
<use x="204" y="672" xlink:href="#p" />
<use x="216" y="672" xlink:href="#p" />
<use x="228" y="672" xlink:href="#p" />
<use x="240" y="672" xlink:href="#p" />
<use x="288" y="672" xlink:href="#p" />
<use x="336" y="672" xlink:href="#p" />
<use x="348" y="672" xlink:href="#p" />
<use x="360" y="672" xlink:href="#p" />
<use x="372" y="672" xlink:href="#p" />
<use x="408" y="672" xlink:href="#p" />
<use x="444" y="672" xlink:href="#p" />
<use x="456" y="672" xlink:href="#p" />
<use x="480" y="672" xlink:href="#p" />
<use x="504" y="672" xlink:href="#p" />
<use x="516" y="672" xlink:href="#p" />
<use x="528" y="672" xlink:href="#p" />
<use x="552" y="672" xlink:href="#p" />
<use x="612" y="672" xlink:href="#p" />
<use x="636" y="672" xlink:href="#p" />
<use x="660" y="672" xlink:href="#p" />
<use x="672" y="672" xlink:href="#p" />
<use x="684" y="672" xlink:href="#p" />
<use x="708" y="672" xlink:href="#p" />
<use x="732" y="672" xlink:href="#p" />
<use x="768" y="672" xlink:href="#p" />
<use x="780" y="672" xlink:href="#p" />
<use x="24" y="684" xlink:href="#p" />
<use x="36" y="684" xlink:href="#p" />
<use x="60" y="684" xlink:href="#p" />
<use x="84" y="684" xlink:href="#p" />
<use x="132" y="684" xlink:href="#p" />
<use x="156" y="684" xlink:href="#p" />
<use x="180" y="684" xlink:href="#p" />
<use x="192" y="684" xlink:href="#p" />
<use x="204" y="684" xlink:href="#p" />
<use x="228" y="684" xlink:href="#p" />
<use x="252" y="684" xlink:href="#p" />
<use x="276" y="684" xlink:href="#p" />
<use x="288" y="684" xlink:href="#p" />
<use x="300" y="684" xlink:href="#p" />
<use x="348" y="684" xlink:href="#p" />
<use x="360" y="684" xlink:href="#p" />
<use x="372" y="684" xlink:href="#p" />
<use x="384" y="684" xlink:href="#p" />
<use x="396" y="684" xlink:href="#p" />
<use x="408" y="684" xlink:href="#p" />
<use x="420" y="684" xlink:href="#p" />
<use x="432" y="684" xlink:href="#p" />
<use x="456" y="684" xlink:href="#p" />
<use x="504" y="684" xlink:href="#p" />
<use x="528" y="684" xlink:href="#p" />
<use x="576" y="684" xlink:href="#p" />
<use x="588" y="684" xlink:href="#p" />
<use x="648" y="684" xlink:href="#p" />
<use x="684" y="684" xlink:href="#p" />
<use x="696" y="684" xlink:href="#p" />
<use x="708" y="684" xlink:href="#p" />
<use x="720" y="684" xlink:href="#p" />
<use x="732" y="684" xlink:href="#p" />
<use x="756" y="684" xlink:href="#p" />
<use x="780" y="684" xlink:href="#p" />
<use x="108" y="696" xlink:href="#p" />
<use x="144" y="696" xlink:href="#p" />
<use x="156" y="696" xlink:href="#p" />
<use x="168" y="696" xlink:href="#p" />
<use x="180" y="696" xlink:href="#p" />
<use x="192" y="696" xlink:href="#p" />
<use x="204" y="696" xlink:href="#p" />
<use x="216" y="696" xlink:href="#p" />
<use x="228" y="696" xlink:href="#p" />
<use x="252" y="696" xlink:href="#p" />
<use x="276" y="696" xlink:href="#p" />
<use x="312" y="696" xlink:href="#p" />
<use x="336" y="696" xlink:href="#p" />
<use x="348" y="696" xlink:href="#p" />
<use x="372" y="696" xlink:href="#p" />
<use x="420" y="696" xlink:href="#p" />
<use x="444" y="696" xlink:href="#p" />
<use x="456" y="696" xlink:href="#p" />
<use x="504" y="696" xlink:href="#p" />
<use x="516" y="696" xlink:href="#p" />
<use x="576" y="696" xlink:href="#p" />
<use x="612" y="696" xlink:href="#p" />
<use x="660" y="696" xlink:href="#p" />
<use x="684" y="696" xlink:href="#p" />
<use x="732" y="696" xlink:href="#p" />
<use x="744" y="696" xlink:href="#p" />
<use x="756" y="696" xlink:href="#p" />
<use x="768" y="696" xlink:href="#p" />
<use x="12" y="708" xlink:href="#p" />
<use x="24" y="708" xlink:href="#p" />
<use x="36" y="708" xlink:href="#p" />
<use x="48" y="708" xlink:href="#p" />
<use x="60" y="708" xlink:href="#p" />
<use x="72" y="708" xlink:href="#p" />
<use x="84" y="708" xlink:href="#p" />
<use x="156" y="708" xlink:href="#p" />
<use x="204" y="708" xlink:href="#p" />
<use x="216" y="708" xlink:href="#p" />
<use x="228" y="708" xlink:href="#p" />
<use x="240" y="708" xlink:href="#p" />
<use x="264" y="708" xlink:href="#p" />
<use x="288" y="708" xlink:href="#p" />
<use x="300" y="708" xlink:href="#p" />
<use x="324" y="708" xlink:href="#p" />
<use x="348" y="708" xlink:href="#p" />
<use x="360" y="708" xlink:href="#p" />
<use x="372" y="708" xlink:href="#p" />
<use x="396" y="708" xlink:href="#p" />
<use x="420" y="708" xlink:href="#p" />
<use x="456" y="708" xlink:href="#p" />
<use x="468" y="708" xlink:href="#p" />
<use x="492" y="708" xlink:href="#p" />
<use x="504" y="708" xlink:href="#p" />
<use x="552" y="708" xlink:href="#p" />
<use x="588" y="708" xlink:href="#p" />
<use x="624" y="708" xlink:href="#p" />
<use x="636" y="708" xlink:href="#p" />
<use x="648" y="708" xlink:href="#p" />
<use x="684" y="708" xlink:href="#p" />
<use x="708" y="708" xlink:href="#p" />
<use x="732" y="708" xlink:href="#p" />
<use x="744" y="708" xlink:href="#p" />
<use x="12" y="720" xlink:href="#p" />
<use x="84" y="720" xlink:href="#p" />
<use x="108" y="720" xlink:href="#p" />
<use x="120" y="720" xlink:href="#p" />
<use x="132" y="720" xlink:href="#p" />
<use x="168" y="720" xlink:href="#p" />
<use x="180" y="720" xlink:href="#p" />
<use x="192" y="720" xlink:href="#p" />
<use x="204" y="720" xlink:href="#p" />
<use x="228" y="720" xlink:href="#p" />
<use x="252" y="720" xlink:href="#p" />
<use x="312" y="720" xlink:href="#p" />
<use x="372" y="720" xlink:href="#p" />
<use x="420" y="720" xlink:href="#p" />
<use x="432" y="720" xlink:href="#p" />
<use x="444" y="720" xlink:href="#p" />
<use x="480" y="720" xlink:href="#p" />
<use x="492" y="720" xlink:href="#p" />
<use x="504" y="720" xlink:href="#p" />
<use x="516" y="720" xlink:href="#p" />
<use x="540" y="720" xlink:href="#p" />
<use x="552" y="720" xlink:href="#p" />
<use x="564" y="720" xlink:href="#p" />
<use x="576" y="720" xlink:href="#p" />
<use x="600" y="720" xlink:href="#p" />
<use x="612" y="720" xlink:href="#p" />
<use x="624" y="720" xlink:href="#p" />
<use x="660" y="720" xlink:href="#p" />
<use x="672" y="720" xlink:href="#p" />
<use x="684" y="720" xlink:href="#p" />
<use x="732" y="720" xlink:href="#p" />
<use x="768" y="720" xlink:href="#p" />
<use x="12" y="732" xlink:href="#p" />
<use x="36" y="732" xlink:href="#p" />
<use x="48" y="732" xlink:href="#p" />
<use x="60" y="732" xlink:href="#p" />
<use x="84" y="732" xlink:href="#p" />
<use x="144" y="732" xlink:href="#p" />
<use x="156" y="732" xlink:href="#p" />
<use x="192" y="732" xlink:href="#p" />
<use x="204" y="732" xlink:href="#p" />
<use x="216" y="732" xlink:href="#p" />
<use x="228" y="732" xlink:href="#p" />
<use x="240" y="732" xlink:href="#p" />
<use x="300" y="732" xlink:href="#p" />
<use x="312" y="732" xlink:href="#p" />
<use x="324" y="732" xlink:href="#p" />
<use x="336" y="732" xlink:href="#p" />
<use x="360" y="732" xlink:href="#p" />
<use x="372" y="732" xlink:href="#p" />
<use x="384" y="732" xlink:href="#p" />
<use x="396" y="732" xlink:href="#p" />
<use x="408" y="732" xlink:href="#p" />
<use x="420" y="732" xlink:href="#p" />
<use x="444" y="732" xlink:href="#p" />
<use x="456" y="732" xlink:href="#p" />
<use x="492" y="732" xlink:href="#p" />
<use x="504" y="732" xlink:href="#p" />
<use x="516" y="732" xlink:href="#p" />
<use x="528" y="732" xlink:href="#p" />
<use x="540" y="732" xlink:href="#p" />
<use x="552" y="732" xlink:href="#p" />
<use x="588" y="732" xlink:href="#p" />
<use x="636" y="732" xlink:href="#p" />
<use x="672" y="732" xlink:href="#p" />
<use x="684" y="732" xlink:href="#p" />
<use x="696" y="732" xlink:href="#p" />
<use x="708" y="732" xlink:href="#p" />
<use x="720" y="732" xlink:href="#p" />
<use x="732" y="732" xlink:href="#p" />
<use x="768" y="732" xlink:href="#p" />
<use x="780" y="732" xlink:href="#p" />
<use x="12" y="744" xlink:href="#p" />
<use x="36" y="744" xlink:href="#p" />
<use x="48" y="744" xlink:href="#p" />
<use x="60" y="744" xlink:href="#p" />
<use x="84" y="744" xlink:href="#p" />
<use x="120" y="744" xlink:href="#p" />
<use x="156" y="744" xlink:href="#p" />
<use x="168" y="744" xlink:href="#p" />
<use x="180" y="744" xlink:href="#p" />
<use x="216" y="744" xlink:href="#p" />
<use x="228" y="744" xlink:href="#p" />
<use x="276" y="744" xlink:href="#p" />
<use x="324" y="744" xlink:href="#p" />
<use x="336" y="744" xlink:href="#p" />
<use x="396" y="744" xlink:href="#p" />
<use x="408" y="744" xlink:href="#p" />
<use x="432" y="744" xlink:href="#p" />
<use x="444" y="744" xlink:href="#p" />
<use x="468" y="744" xlink:href="#p" />
<use x="480" y="744" xlink:href="#p" />
<use x="516" y="744" xlink:href="#p" />
<use x="540" y="744" xlink:href="#p" />
<use x="552" y="744" xlink:href="#p" />
<use x="564" y="744" xlink:href="#p" />
<use x="576" y="744" xlink:href="#p" />
<use x="588" y="744" xlink:href="#p" />
<use x="600" y="744" xlink:href="#p" />
<use x="624" y="744" xlink:href="#p" />
<use x="636" y="744" xlink:href="#p" />
<use x="648" y="744" xlink:href="#p" />
<use x="660" y="744" xlink:href="#p" />
<use x="684" y="744" xlink:href="#p" />
<use x="696" y="744" xlink:href="#p" />
<use x="720" y="744" xlink:href="#p" />
<use x="744" y="744" xlink:href="#p" />
<use x="12" y="756" xlink:href="#p" />
<use x="36" y="756" xlink:href="#p" />
<use x="48" y="756" xlink:href="#p" />
<use x="60" y="756" xlink:href="#p" />
<use x="84" y="756" xlink:href="#p" />
<use x="120" y="756" xlink:href="#p" />
<use x="180" y="756" xlink:href="#p" />
<use x="204" y="756" xlink:href="#p" />
<use x="216" y="756" xlink:href="#p" />
<use x="228" y="756" xlink:href="#p" />
<use x="240" y="756" xlink:href="#p" />
<use x="264" y="756" xlink:href="#p" />
<use x="288" y="756" xlink:href="#p" />
<use x="312" y="756" xlink:href="#p" />
<use x="324" y="756" xlink:href="#p" />
<use x="336" y="756" xlink:href="#p" />
<use x="348" y="756" xlink:href="#p" />
<use x="408" y="756" xlink:href="#p" />
<use x="432" y="756" xlink:href="#p" />
<use x="444" y="756" xlink:href="#p" />
<use x="468" y="756" xlink:href="#p" />
<use x="528" y="756" xlink:href="#p" />
<use x="540" y="756" xlink:href="#p" />
<use x="552" y="756" xlink:href="#p" />
<use x="576" y="756" xlink:href="#p" />
<use x="600" y="756" xlink:href="#p" />
<use x="612" y="756" xlink:href="#p" />
<use x="696" y="756" xlink:href="#p" />
<use x="708" y="756" xlink:href="#p" />
<use x="720" y="756" xlink:href="#p" />
<use x="732" y="756" xlink:href="#p" />
<use x="768" y="756" xlink:href="#p" />
<use x="12" y="768" xlink:href="#p" />
<use x="84" y="768" xlink:href="#p" />
<use x="132" y="768" xlink:href="#p" />
<use x="144" y="768" xlink:href="#p" />
<use x="168" y="768" xlink:href="#p" />
<use x="180" y="768" xlink:href="#p" />
<use x="192" y="768" xlink:href="#p" />
<use x="216" y="768" xlink:href="#p" />
<use x="228" y="768" xlink:href="#p" />
<use x="252" y="768" xlink:href="#p" />
<use x="276" y="768" xlink:href="#p" />
<use x="288" y="768" xlink:href="#p" />
<use x="336" y="768" xlink:href="#p" />
<use x="384" y="768" xlink:href="#p" />
<use x="408" y="768" xlink:href="#p" />
<use x="432" y="768" xlink:href="#p" />
<use x="444" y="768" xlink:href="#p" />
<use x="456" y="768" xlink:href="#p" />
<use x="468" y="768" xlink:href="#p" />
<use x="480" y="768" xlink:href="#p" />
<use x="492" y="768" xlink:href="#p" />
<use x="504" y="768" xlink:href="#p" />
<use x="528" y="768" xlink:href="#p" />
<use x="552" y="768" xlink:href="#p" />
<use x="600" y="768" xlink:href="#p" />
<use x="624" y="768" xlink:href="#p" />
<use x="684" y="768" xlink:href="#p" />
<use x="696" y="768" xlink:href="#p" />
<use x="720" y="768" xlink:href="#p" />
<use x="744" y="768" xlink:href="#p" />
<use x="756" y="768" xlink:href="#p" />
<use x="780" y="768" xlink:href="#p" />
<use x="12" y="780" xlink:href="#p" />
<use x="24" y="780" xlink:href="#p" />
<use x="36" y="780" xlink:href="#p" />
<use x="48" y="780" xlink:href="#p" />
<use x="60" y="780" xlink:href="#p" />
<use x="72" y="780" xlink:href="#p" />
<use x="84" y="780" xlink:href="#p" />
<use x="120" y="780" xlink:href="#p" />
<use x="132" y="780" xlink:href="#p" />
<use x="144" y="780" xlink:href="#p" />
<use x="156" y="780" xlink:href="#p" />
<use x="168" y="780" xlink:href="#p" />
<use x="180" y="780" xlink:href="#p" />
<use x="228" y="780" xlink:href="#p" />
<use x="252" y="780" xlink:href="#p" />
<use x="276" y="780" xlink:href="#p" />
<use x="288" y="780" xlink:href="#p" />
<use x="312" y="780" xlink:href="#p" />
<use x="360" y="780" xlink:href="#p" />
<use x="384" y="780" xlink:href="#p" />
<use x="408" y="780" xlink:href="#p" />
<use x="420" y="780" xlink:href="#p" />
<use x="432" y="780" xlink:href="#p" />
<use x="456" y="780" xlink:href="#p" />
<use x="504" y="780" xlink:href="#p" />
<use x="528" y="780" xlink:href="#p" />
<use x="552" y="780" xlink:href="#p" />
<use x="564" y="780" xlink:href="#p" />
<use x="576" y="780" xlink:href="#p" />
<use x="588" y="780" xlink:href="#p" />
<use x="600" y="780" xlink:href="#p" />
<use x="624" y="780" xlink:href="#p" />
<use x="648" y="780" xlink:href="#p" />
<use x="672" y="780" xlink:href="#p" />
<use x="720" y="780" xlink:href="#p" />
<use x="732" y="780" xlink:href="#p" />
<use x="756" y="780" xlink:href="#p" />
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" xmlns:xlink="http://www.w3.org/1999/xlink" width="468" height="468" viewBox="0 0 468 468">
<desc></desc>
<rect width="468" height="468" fill="#ffffff" cx="0" cy="0" />
<defs>
<rect id="p" width="12" height="12" />
</defs>
<g fill="#000000">
<use x="12" y="12" xlink:href="#p" />
<use x="24" y="12" xlink:href="#p" />
<use x="36" y="12" xlink:href="#p" />
<use x="48" y="12" xlink:href="#p" />
<use x="60" y="12" xlink:href="#p" />
<use x="72" y="12" xlink:href="#p" />
<use x="84" y="12" xlink:href="#p" />
<use x="108" y="12" xlink:href="#p" />
<use x="144" y="12" xlink:href="#p" />
<use x="156" y="12" xlink:href="#p" />
<use x="180" y="12" xlink:href="#p" />
<use x="204" y="12" xlink:href="#p" />
<use x="240" y="12" xlink:href="#p" />
<use x="264" y="12" xlink:href="#p" />
<use x="276" y="12" xlink:href="#p" />
<use x="288" y="12" xlink:href="#p" />
<use x="300" y="12" xlink:href="#p" />
<use x="348" y="12" xlink:href="#p" />
<use x="372" y="12" xlink:href="#p" />
<use x="384" y="12" xlink:href="#p" />
<use x="396" y="12" xlink:href="#p" />
<use x="408" y="12" xlink:href="#p" />
<use x="420" y="12" xlink:href="#p" />
<use x="432" y="12" xlink:href="#p" />
<use x="444" y="12" xlink:href="#p" />
<use x="12" y="24" xlink:href="#p" />
<use x="84" y="24" xlink:href="#p" />
<use x="108" y="24" xlink:href="#p" />
<use x="132" y="24" xlink:href="#p" />
<use x="144" y="24" xlink:href="#p" />
<use x="168" y="24" xlink:href="#p" />
<use x="192" y="24" xlink:href="#p" />
<use x="216" y="24" xlink:href="#p" />
<use x="228" y="24" xlink:href="#p" />
<use x="240" y="24" xlink:href="#p" />
<use x="264" y="24" xlink:href="#p" />
<use x="276" y="24" xlink:href="#p" />
<use x="336" y="24" xlink:href="#p" />
<use x="348" y="24" xlink:href="#p" />
<use x="372" y="24" xlink:href="#p" />
<use x="444" y="24" xlink:href="#p" />
<use x="12" y="36" xlink:href="#p" />
<use x="36" y="36" xlink:href="#p" />
<use x="48" y="36" xlink:href="#p" />
<use x="60" y="36" xlink:href="#p" />
<use x="84" y="36" xlink:href="#p" />
<use x="108" y="36" xlink:href="#p" />
<use x="120" y="36" xlink:href="#p" />
<use x="180" y="36" xlink:href="#p" />
<use x="192" y="36" xlink:href="#p" />
<use x="204" y="36" xlink:href="#p" />
<use x="216" y="36" xlink:href="#p" />
<use x="240" y="36" xlink:href="#p" />
<use x="252" y="36" xlink:href="#p" />
<use x="264" y="36" xlink:href="#p" />
<use x="276" y="36" xlink:href="#p" />
<use x="324" y="36" xlink:href="#p" />
<use x="372" y="36" xlink:href="#p" />
<use x="396" y="36" xlink:href="#p" />
<use x="408" y="36" xlink:href="#p" />
<use x="420" y="36" xlink:href="#p" />
<use x="444" y="36" xlink:href="#p" />
<use x="12" y="48" xlink:href="#p" />
<use x="36" y="48" xlink:href="#p" />
<use x="48" y="48" xlink:href="#p" />
<use x="60" y="48" xlink:href="#p" />
<use x="84" y="48" xlink:href="#p" />
<use x="120" y="48" xlink:href="#p" />
<use x="144" y="48" xlink:href="#p" />
<use x="168" y="48" xlink:href="#p" />
<use x="192" y="48" xlink:href="#p" />
<use x="204" y="48" xlink:href="#p" />
<use x="216" y="48" xlink:href="#p" />
<use x="288" y="48" xlink:href="#p" />
<use x="300" y="48" xlink:href="#p" />
<use x="312" y="48" xlink:href="#p" />
<use x="324" y="48" xlink:href="#p" />
<use x="336" y="48" xlink:href="#p" />
<use x="372" y="48" xlink:href="#p" />
<use x="396" y="48" xlink:href="#p" />
<use x="408" y="48" xlink:href="#p" />
<use x="420" y="48" xlink:href="#p" />
<use x="444" y="48" xlink:href="#p" />
<use x="12" y="60" xlink:href="#p" />
<use x="36" y="60" xlink:href="#p" />
<use x="48" y="60" xlink:href="#p" />
<use x="60" y="60" xlink:href="#p" />
<use x="84" y="60" xlink:href="#p" />
<use x="132" y="60" xlink:href="#p" />
<use x="144" y="60" xlink:href="#p" />
<use x="156" y="60" xlink:href="#p" />
<use x="168" y="60" xlink:href="#p" />
<use x="180" y="60" xlink:href="#p" />
<use x="192" y="60" xlink:href="#p" />
<use x="204" y="60" xlink:href="#p" />
<use x="240" y="60" xlink:href="#p" />
<use x="276" y="60" xlink:href="#p" />
<use x="300" y="60" xlink:href="#p" />
<use x="324" y="60" xlink:href="#p" />
<use x="372" y="60" xlink:href="#p" />
<use x="396" y="60" xlink:href="#p" />
<use x="408" y="60" xlink:href="#p" />
<use x="420" y="60" xlink:href="#p" />
<use x="444" y="60" xlink:href="#p" />
<use x="12" y="72" xlink:href="#p" />
<use x="84" y="72" xlink:href="#p" />
<use x="108" y="72" xlink:href="#p" />
<use x="168" y="72" xlink:href="#p" />
<use x="180" y="72" xlink:href="#p" />
<use x="192" y="72" xlink:href="#p" />
<use x="228" y="72" xlink:href="#p" />
<use x="264" y="72" xlink:href="#p" />
<use x="288" y="72" xlink:href="#p" />
<use x="300" y="72" xlink:href="#p" />
<use x="324" y="72" xlink:href="#p" />
<use x="336" y="72" xlink:href="#p" />
<use x="348" y="72" xlink:href="#p" />
<use x="372" y="72" xlink:href="#p" />
<use x="444" y="72" xlink:href="#p" />
<use x="12" y="84" xlink:href="#p" />
<use x="24" y="84" xlink:href="#p" />
<use x="36" y="84" xlink:href="#p" />
<use x="48" y="84" xlink:href="#p" />
<use x="60" y="84" xlink:href="#p" />
<use x="72" y="84" xlink:href="#p" />
<use x="84" y="84" xlink:href="#p" />
<use x="108" y="84" xlink:href="#p" />
<use x="132" y="84" xlink:href="#p" />
<use x="156" y="84" xlink:href="#p" />
<use x="180" y="84" xlink:href="#p" />
<use x="204" y="84" xlink:href="#p" />
<use x="228" y="84" xlink:href="#p" />
<use x="252" y="84" xlink:href="#p" />
<use x="276" y="84" xlink:href="#p" />
<use x="300" y="84" xlink:href="#p" />
<use x="324" y="84" xlink:href="#p" />
<use x="348" y="84" xlink:href="#p" />
<use x="372" y="84" xlink:href="#p" />
<use x="384" y="84" xlink:href="#p" />
<use x="396" y="84" xlink:href="#p" />
<use x="408" y="84" xlink:href="#p" />
<use x="420" y="84" xlink:href="#p" />
<use x="432" y="84" xlink:href="#p" />
<use x="444" y="84" xlink:href="#p" />
<use x="108" y="96" xlink:href="#p" />
<use x="180" y="96" xlink:href="#p" />
<use x="192" y="96" xlink:href="#p" />
<use x="216" y="96" xlink:href="#p" />
<use x="240" y="96" xlink:href="#p" />
<use x="276" y="96" xlink:href="#p" />
<use x="300" y="96" xlink:href="#p" />
<use x="312" y="96" xlink:href="#p" />
<use x="348" y="96" xlink:href="#p" />
<use x="36" y="108" xlink:href="#p" />
<use x="48" y="108" xlink:href="#p" />
<use x="60" y="108" xlink:href="#p" />
<use x="84" y="108" xlink:href="#p" />
<use x="108" y="108" xlink:href="#p" />
<use x="120" y="108" xlink:href="#p" />
<use x="144" y="108" xlink:href="#p" />
<use x="180" y="108" xlink:href="#p" />
<use x="192" y="108" xlink:href="#p" />
<use x="204" y="108" xlink:href="#p" />
<use x="228" y="108" xlink:href="#p" />
<use x="276" y="108" xlink:href="#p" />
<use x="312" y="108" xlink:href="#p" />
<use x="324" y="108" xlink:href="#p" />
<use x="348" y="108" xlink:href="#p" />
<use x="360" y="108" xlink:href="#p" />
<use x="372" y="108" xlink:href="#p" />
<use x="384" y="108" xlink:href="#p" />
<use x="420" y="108" xlink:href="#p" />
<use x="432" y="108" xlink:href="#p" />
<use x="444" y="108" xlink:href="#p" />
<use x="24" y="120" xlink:href="#p" />
<use x="36" y="120" xlink:href="#p" />
<use x="72" y="120" xlink:href="#p" />
<use x="168" y="120" xlink:href="#p" />
<use x="204" y="120" xlink:href="#p" />
<use x="252" y="120" xlink:href="#p" />
<use x="276" y="120" xlink:href="#p" />
<use x="288" y="120" xlink:href="#p" />
<use x="300" y="120" xlink:href="#p" />
<use x="312" y="120" xlink:href="#p" />
<use x="336" y="120" xlink:href="#p" />
<use x="348" y="120" xlink:href="#p" />
<use x="444" y="120" xlink:href="#p" />
<use x="36" y="132" xlink:href="#p" />
<use x="84" y="132" xlink:href="#p" />
<use x="108" y="132" xlink:href="#p" />
<use x="120" y="132" xlink:href="#p" />
<use x="132" y="132" xlink:href="#p" />
<use x="144" y="132" xlink:href="#p" />
<use x="180" y="132" xlink:href="#p" />
<use x="192" y="132" xlink:href="#p" />
<use x="204" y="132" xlink:href="#p" />
<use x="216" y="132" xlink:href="#p" />
<use x="228" y="132" xlink:href="#p" />
<use x="252" y="132" xlink:href="#p" />
<use x="312" y="132" xlink:href="#p" />
<use x="324" y="132" xlink:href="#p" />
<use x="336" y="132" xlink:href="#p" />
<use x="372" y="132" xlink:href="#p" />
<use x="384" y="132" xlink:href="#p" />
<use x="396" y="132" xlink:href="#p" />
<use x="420" y="132" xlink:href="#p" />
<use x="432" y="132" xlink:href="#p" />
<use x="444" y="132" xlink:href="#p" />
<use x="48" y="144" xlink:href="#p" />
<use x="120" y="144" xlink:href="#p" />
<use x="156" y="144" xlink:href="#p" />
<use x="180" y="144" xlink:href="#p" />
<use x="216" y="144" xlink:href="#p" />
<use x="228" y="144" xlink:href="#p" />
<use x="240" y="144" xlink:href="#p" />
<use x="264" y="144" xlink:href="#p" />
<use x="276" y="144" xlink:href="#p" />
<use x="288" y="144" xlink:href="#p" />
<use x="324" y="144" xlink:href="#p" />
<use x="336" y="144" xlink:href="#p" />
<use x="372" y="144" xlink:href="#p" />
<use x="384" y="144" xlink:href="#p" />
<use x="396" y="144" xlink:href="#p" />
<use x="432" y="144" xlink:href="#p" />
<use x="444" y="144" xlink:href="#p" />
<use x="24" y="156" xlink:href="#p" />
<use x="36" y="156" xlink:href="#p" />
<use x="60" y="156" xlink:href="#p" />
<use x="84" y="156" xlink:href="#p" />
<use x="168" y="156" xlink:href="#p" />
<use x="192" y="156" xlink:href="#p" />
<use x="204" y="156" xlink:href="#p" />
<use x="240" y="156" xlink:href="#p" />
<use x="264" y="156" xlink:href="#p" />
<use x="300" y="156" xlink:href="#p" />
<use x="312" y="156" xlink:href="#p" />
<use x="324" y="156" xlink:href="#p" />
<use x="360" y="156" xlink:href="#p" />
<use x="372" y="156" xlink:href="#p" />
<use x="384" y="156" xlink:href="#p" />
<use x="396" y="156" xlink:href="#p" />
<use x="408" y="156" xlink:href="#p" />
<use x="420" y="156" xlink:href="#p" />
<use x="432" y="156" xlink:href="#p" />
<use x="444" y="156" xlink:href="#p" />
<use x="24" y="168" xlink:href="#p" />
<use x="36" y="168" xlink:href="#p" />
<use x="48" y="168" xlink:href="#p" />
<use x="60" y="168" xlink:href="#p" />
<use x="96" y="168" xlink:href="#p" />
<use x="120" y="168" xlink:href="#p" />
<use x="192" y="168" xlink:href="#p" />
<use x="216" y="168" xlink:href="#p" />
<use x="228" y="168" xlink:href="#p" />
<use x="240" y="168" xlink:href="#p" />
<use x="252" y="168" xlink:href="#p" />
<use x="312" y="168" xlink:href="#p" />
<use x="324" y="168" xlink:href="#p" />
<use x="336" y="168" xlink:href="#p" />
<use x="348" y="168" xlink:href="#p" />
<use x="360" y="168" xlink:href="#p" />
<use x="384" y="168" xlink:href="#p" />
<use x="408" y="168" xlink:href="#p" />
<use x="420" y="168" xlink:href="#p" />
<use x="12" y="180" xlink:href="#p" />
<use x="36" y="180" xlink:href="#p" />
<use x="84" y="180" xlink:href="#p" />
<use x="96" y="180" xlink:href="#p" />
<use x="108" y="180" xlink:href="#p" />
<use x="120" y="180" xlink:href="#p" />
<use x="132" y="180" xlink:href="#p" />
<use x="144" y="180" xlink:href="#p" />
<use x="168" y="180" xlink:href="#p" />
<use x="180" y="180" xlink:href="#p" />
<use x="192" y="180" xlink:href="#p" />
<use x="204" y="180" xlink:href="#p" />
<use x="228" y="180" xlink:href="#p" />
<use x="240" y="180" xlink:href="#p" />
<use x="252" y="180" xlink:href="#p" />
<use x="264" y="180" xlink:href="#p" />
<use x="276" y="180" xlink:href="#p" />
<use x="288" y="180" xlink:href="#p" />
<use x="300" y="180" xlink:href="#p" />
<use x="312" y="180" xlink:href="#p" />
<use x="324" y="180" xlink:href="#p" />
<use x="336" y="180" xlink:href="#p" />
<use x="348" y="180" xlink:href="#p" />
<use x="372" y="180" xlink:href="#p" />
<use x="408" y="180" xlink:href="#p" />
<use x="432" y="180" xlink:href="#p" />
<use x="444" y="180" xlink:href="#p" />
<use x="12" y="192" xlink:href="#p" />
<use x="24" y="192" xlink:href="#p" />
<use x="60" y="192" xlink:href="#p" />
<use x="96" y="192" xlink:href="#p" />
<use x="108" y="192" xlink:href="#p" />
<use x="120" y="192" xlink:href="#p" />
<use x="156" y="192" xlink:href="#p" />
<use x="180" y="192" xlink:href="#p" />
<use x="192" y="192" xlink:href="#p" />
<use x="204" y="192" xlink:href="#p" />
<use x="216" y="192" xlink:href="#p" />
<use x="228" y="192" xlink:href="#p" />
<use x="288" y="192" xlink:href="#p" />
<use x="300" y="192" xlink:href="#p" />
<use x="324" y="192" xlink:href="#p" />
<use x="360" y="192" xlink:href="#p" />
<use x="372" y="192" xlink:href="#p" />
<use x="384" y="192" xlink:href="#p" />
<use x="396" y="192" xlink:href="#p" />
<use x="432" y="192" xlink:href="#p" />
<use x="72" y="204" xlink:href="#p" />
<use x="84" y="204" xlink:href="#p" />
<use x="132" y="204" xlink:href="#p" />
<use x="144" y="204" xlink:href="#p" />
<use x="156" y="204" xlink:href="#p" />
<use x="180" y="204" xlink:href="#p" />
<use x="216" y="204" xlink:href="#p" />
<use x="240" y="204" xlink:href="#p" />
<use x="264" y="204" xlink:href="#p" />
<use x="276" y="204" xlink:href="#p" />
<use x="300" y="204" xlink:href="#p" />
<use x="312" y="204" xlink:href="#p" />
<use x="324" y="204" xlink:href="#p" />
<use x="360" y="204" xlink:href="#p" />
<use x="372" y="204" xlink:href="#p" />
<use x="396" y="204" xlink:href="#p" />
<use x="420" y="204" xlink:href="#p" />
<use x="432" y="204" xlink:href="#p" />
<use x="444" y="204" xlink:href="#p" />
<use x="24" y="216" xlink:href="#p" />
<use x="36" y="216" xlink:href="#p" />
<use x="48" y="216" xlink:href="#p" />
<use x="72" y="216" xlink:href="#p" />
<use x="120" y="216" xlink:href="#p" />
<use x="168" y="216" xlink:href="#p" />
<use x="180" y="216" xlink:href="#p" />
<use x="192" y="216" xlink:href="#p" />
<use x="276" y="216" xlink:href="#p" />
<use x="288" y="216" xlink:href="#p" />
<use x="348" y="216" xlink:href="#p" />
<use x="360" y="216" xlink:href="#p" />
<use x="384" y="216" xlink:href="#p" />
<use x="12" y="228" xlink:href="#p" />
<use x="24" y="228" xlink:href="#p" />
<use x="36" y="228" xlink:href="#p" />
<use x="60" y="228" xlink:href="#p" />
<use x="84" y="228" xlink:href="#p" />
<use x="96" y="228" xlink:href="#p" />
<use x="132" y="228" xlink:href="#p" />
<use x="156" y="228" xlink:href="#p" />
<use x="168" y="228" xlink:href="#p" />
<use x="180" y="228" xlink:href="#p" />
<use x="204" y="228" xlink:href="#p" />
<use x="228" y="228" xlink:href="#p" />
<use x="252" y="228" xlink:href="#p" />
<use x="264" y="228" xlink:href="#p" />
<use x="276" y="228" xlink:href="#p" />
<use x="288" y="228" xlink:href="#p" />
<use x="312" y="228" xlink:href="#p" />
<use x="336" y="228" xlink:href="#p" />
<use x="432" y="228" xlink:href="#p" />
<use x="444" y="228" xlink:href="#p" />
<use x="24" y="240" xlink:href="#p" />
<use x="48" y="240" xlink:href="#p" />
<use x="60" y="240" xlink:href="#p" />
<use x="108" y="240" xlink:href="#p" />
<use x="120" y="240" xlink:href="#p" />
<use x="168" y="240" xlink:href="#p" />
<use x="180" y="240" xlink:href="#p" />
<use x="192" y="240" xlink:href="#p" />
<use x="264" y="240" xlink:href="#p" />
<use x="276" y="240" xlink:href="#p" />
<use x="300" y="240" xlink:href="#p" />
<use x="324" y="240" xlink:href="#p" />
<use x="348" y="240" xlink:href="#p" />
<use x="396" y="240" xlink:href="#p" />
<use x="432" y="240" xlink:href="#p" />
<use x="444" y="240" xlink:href="#p" />
<use x="12" y="252" xlink:href="#p" />
<use x="24" y="252" xlink:href="#p" />
<use x="72" y="252" xlink:href="#p" />
<use x="84" y="252" xlink:href="#p" />
<use x="108" y="252" xlink:href="#p" />
<use x="120" y="252" xlink:href="#p" />
<use x="132" y="252" xlink:href="#p" />
<use x="144" y="252" xlink:href="#p" />
<use x="156" y="252" xlink:href="#p" />
<use x="168" y="252" xlink:href="#p" />
<use x="216" y="252" xlink:href="#p" />
<use x="252" y="252" xlink:href="#p" />
<use x="264" y="252" xlink:href="#p" />
<use x="288" y="252" xlink:href="#p" />
<use x="300" y="252" xlink:href="#p" />
<use x="324" y="252" xlink:href="#p" />
<use x="360" y="252" xlink:href="#p" />
<use x="372" y="252" xlink:href="#p" />
<use x="384" y="252" xlink:href="#p" />
<use x="396" y="252" xlink:href="#p" />
<use x="408" y="252" xlink:href="#p" />
<use x="420" y="252" xlink:href="#p" />
<use x="432" y="252" xlink:href="#p" />
<use x="444" y="252" xlink:href="#p" />
<use x="12" y="264" xlink:href="#p" />
<use x="48" y="264" xlink:href="#p" />
<use x="60" y="264" xlink:href="#p" />
<use x="72" y="264" xlink:href="#p" />
<use x="96" y="264" xlink:href="#p" />
<use x="120" y="264" xlink:href="#p" />
<use x="180" y="264" xlink:href="#p" />
<use x="192" y="264" xlink:href="#p" />
<use x="204" y="264" xlink:href="#p" />
<use x="216" y="264" xlink:href="#p" />
<use x="228" y="264" xlink:href="#p" />
<use x="252" y="264" xlink:href="#p" />
<use x="276" y="264" xlink:href="#p" />
<use x="360" y="264" xlink:href="#p" />
<use x="384" y="264" xlink:href="#p" />
<use x="432" y="264" xlink:href="#p" />
<use x="12" y="276" xlink:href="#p" />
<use x="24" y="276" xlink:href="#p" />
<use x="60" y="276" xlink:href="#p" />
<use x="72" y="276" xlink:href="#p" />
<use x="84" y="276" xlink:href="#p" />
<use x="144" y="276" xlink:href="#p" />
<use x="156" y="276" xlink:href="#p" />
<use x="192" y="276" xlink:href="#p" />
<use x="216" y="276" xlink:href="#p" />
<use x="228" y="276" xlink:href="#p" />
<use x="240" y="276" xlink:href="#p" />
<use x="252" y="276" xlink:href="#p" />
<use x="288" y="276" xlink:href="#p" />
<use x="312" y="276" xlink:href="#p" />
<use x="324" y="276" xlink:href="#p" />
<use x="336" y="276" xlink:href="#p" />
<use x="372" y="276" xlink:href="#p" />
<use x="444" y="276" xlink:href="#p" />
<use x="48" y="288" xlink:href="#p" />
<use x="60" y="288" xlink:href="#p" />
<use x="96" y="288" xlink:href="#p" />
<use x="132" y="288" xlink:href="#p" />
<use x="144" y="288" xlink:href="#p" />
<use x="168" y="288" xlink:href="#p" />
<use x="192" y="288" xlink:href="#p" />
<use x="216" y="288" xlink:href="#p" />
<use x="372" y="288" xlink:href="#p" />
<use x="384" y="288" xlink:href="#p" />
<use x="432" y="288" xlink:href="#p" />
<use x="444" y="288" xlink:href="#p" />
<use x="12" y="300" xlink:href="#p" />
<use x="36" y="300" xlink:href="#p" />
<use x="48" y="300" xlink:href="#p" />
<use x="84" y="300" xlink:href="#p" />
<use x="96" y="300" xlink:href="#p" />
<use x="108" y="300" xlink:href="#p" />
<use x="132" y="300" xlink:href="#p" />
<use x="144" y="300" xlink:href="#p" />
<use x="216" y="300" xlink:href="#p" />
<use x="228" y="300" xlink:href="#p" />
<use x="240" y="300" xlink:href="#p" />
<use x="252" y="300" xlink:href="#p" />
<use x="264" y="300" xlink:href="#p" />
<use x="300" y="300" xlink:href="#p" />
<use x="312" y="300" xlink:href="#p" />
<use x="324" y="300" xlink:href="#p" />
<use x="348" y="300" xlink:href="#p" />
<use x="372" y="300" xlink:href="#p" />
<use x="396" y="300" xlink:href="#p" />
<use x="420" y="300" xlink:href="#p" />
<use x="444" y="300" xlink:href="#p" />
<use x="12" y="312" xlink:href="#p" />
<use x="24" y="312" xlink:href="#p" />
<use x="72" y="312" xlink:href="#p" />
<use x="108" y="312" xlink:href="#p" />
<use x="132" y="312" xlink:href="#p" />
<use x="144" y="312" xlink:href="#p" />
<use x="192" y="312" xlink:href="#p" />
<use x="204" y="312" xlink:href="#p" />
<use x="228" y="312" xlink:href="#p" />
<use x="240" y="312" xlink:href="#p" />
<use x="264" y="312" xlink:href="#p" />
<use x="300" y="312" xlink:href="#p" />
<use x="336" y="312" xlink:href="#p" />
<use x="348" y="312" xlink:href="#p" />
<use x="360" y="312" xlink:href="#p" />
<use x="384" y="312" xlink:href="#p" />
<use x="444" y="312" xlink:href="#p" />
<use x="12" y="324" xlink:href="#p" />
<use x="48" y="324" xlink:href="#p" />
<use x="84" y="324" xlink:href="#p" />
<use x="108" y="324" xlink:href="#p" />
<use x="120" y="324" xlink:href="#p" />
<use x="132" y="324" xlink:href="#p" />
<use x="156" y="324" xlink:href="#p" />
<use x="168" y="324" xlink:href="#p" />
<use x="216" y="324" xlink:href="#p" />
<use x="240" y="324" xlink:href="#p" />
<use x="264" y="324" xlink:href="#p" />
<use x="300" y="324" xlink:href="#p" />
<use x="324" y="324" xlink:href="#p" />
<use x="336" y="324" xlink:href="#p" />
<use x="348" y="324" xlink:href="#p" />
<use x="372" y="324" xlink:href="#p" />
<use x="384" y="324" xlink:href="#p" />
<use x="396" y="324" xlink:href="#p" />
<use x="432" y="324" xlink:href="#p" />
<use x="444" y="324" xlink:href="#p" />
<use x="12" y="336" xlink:href="#p" />
<use x="48" y="336" xlink:href="#p" />
<use x="60" y="336" xlink:href="#p" />
<use x="108" y="336" xlink:href="#p" />
<use x="168" y="336" xlink:href="#p" />
<use x="180" y="336" xlink:href="#p" />
<use x="204" y="336" xlink:href="#p" />
<use x="240" y="336" xlink:href="#p" />
<use x="252" y="336" xlink:href="#p" />
<use x="300" y="336" xlink:href="#p" />
<use x="324" y="336" xlink:href="#p" />
<use x="336" y="336" xlink:href="#p" />
<use x="348" y="336" xlink:href="#p" />
<use x="360" y="336" xlink:href="#p" />
<use x="384" y="336" xlink:href="#p" />
<use x="432" y="336" xlink:href="#p" />
<use x="12" y="348" xlink:href="#p" />
<use x="36" y="348" xlink:href="#p" />
<use x="48" y="348" xlink:href="#p" />
<use x="60" y="348" xlink:href="#p" />
<use x="72" y="348" xlink:href="#p" />
<use x="84" y="348" xlink:href="#p" />
<use x="96" y="348" xlink:href="#p" />
<use x="120" y="348" xlink:href="#p" />
<use x="132" y="348" xlink:href="#p" />
<use x="168" y="348" xlink:href="#p" />
<use x="180" y="348" xlink:href="#p" />
<use x="204" y="348" xlink:href="#p" />
<use x="216" y="348" xlink:href="#p" />
<use x="240" y="348" xlink:href="#p" />
<use x="252" y="348" xlink:href="#p" />
<use x="264" y="348" xlink:href="#p" />
<use x="276" y="348" xlink:href="#p" />
<use x="300" y="348" xlink:href="#p" />
<use x="312" y="348" xlink:href="#p" />
<use x="348" y="348" xlink:href="#p" />
<use x="360" y="348" xlink:href="#p" />
<use x="372" y="348" xlink:href="#p" />
<use x="384" y="348" xlink:href="#p" />
<use x="396" y="348" xlink:href="#p" />
<use x="420" y="348" xlink:href="#p" />
<use x="432" y="348" xlink:href="#p" />
<use x="108" y="360" xlink:href="#p" />
<use x="132" y="360" xlink:href="#p" />
<use x="144" y="360" xlink:href="#p" />
<use x="180" y="360" xlink:href="#p" />
<use x="192" y="360" xlink:href="#p" />
<use x="204" y="360" xlink:href="#p" />
<use x="240" y="360" xlink:href="#p" />
<use x="252" y="360" xlink:href="#p" />
<use x="276" y="360" xlink:href="#p" />
<use x="312" y="360" xlink:href="#p" />
<use x="336" y="360" xlink:href="#p" />
<use x="348" y="360" xlink:href="#p" />
<use x="396" y="360" xlink:href="#p" />
<use x="432" y="360" xlink:href="#p" />
<use x="12" y="372" xlink:href="#p" />
<use x="24" y="372" xlink:href="#p" />
<use x="36" y="372" xlink:href="#p" />
<use x="48" y="372" xlink:href="#p" />
<use x="60" y="372" xlink:href="#p" />
<use x="72" y="372" xlink:href="#p" />
<use x="84" y="372" xlink:href="#p" />
<use x="132" y="372" xlink:href="#p" />
<use x="144" y="372" xlink:href="#p" />
<use x="168" y="372" xlink:href="#p" />
<use x="216" y="372" xlink:href="#p" />
<use x="264" y="372" xlink:href="#p" />
<use x="276" y="372" xlink:href="#p" />
<use x="324" y="372" xlink:href="#p" />
<use x="336" y="372" xlink:href="#p" />
<use x="348" y="372" xlink:href="#p" />
<use x="372" y="372" xlink:href="#p" />
<use x="396" y="372" xlink:href="#p" />
<use x="408" y="372" xlink:href="#p" />
<use x="432" y="372" xlink:href="#p" />
<use x="444" y="372" xlink:href="#p" />
<use x="12" y="384" xlink:href="#p" />
<use x="84" y="384" xlink:href="#p" />
<use x="120" y="384" xlink:href="#p" />
<use x="156" y="384" xlink:href="#p" />
<use x="168" y="384" xlink:href="#p" />
<use x="180" y="384" xlink:href="#p" />
<use x="228" y="384" xlink:href="#p" />
<use x="324" y="384" xlink:href="#p" />
<use x="348" y="384" xlink:href="#p" />
<use x="396" y="384" xlink:href="#p" />
<use x="12" y="396" xlink:href="#p" />
<use x="36" y="396" xlink:href="#p" />
<use x="48" y="396" xlink:href="#p" />
<use x="60" y="396" xlink:href="#p" />
<use x="84" y="396" xlink:href="#p" />
<use x="108" y="396" xlink:href="#p" />
<use x="132" y="396" xlink:href="#p" />
<use x="168" y="396" xlink:href="#p" />
<use x="180" y="396" xlink:href="#p" />
<use x="192" y="396" xlink:href="#p" />
<use x="204" y="396" xlink:href="#p" />
<use x="216" y="396" xlink:href="#p" />
<use x="228" y="396" xlink:href="#p" />
<use x="240" y="396" xlink:href="#p" />
<use x="252" y="396" xlink:href="#p" />
<use x="264" y="396" xlink:href="#p" />
<use x="276" y="396" xlink:href="#p" />
<use x="300" y="396" xlink:href="#p" />
<use x="312" y="396" xlink:href="#p" />
<use x="336" y="396" xlink:href="#p" />
<use x="348" y="396" xlink:href="#p" />
<use x="360" y="396" xlink:href="#p" />
<use x="372" y="396" xlink:href="#p" />
<use x="384" y="396" xlink:href="#p" />
<use x="396" y="396" xlink:href="#p" />
<use x="420" y="396" xlink:href="#p" />
<use x="432" y="396" xlink:href="#p" />
<use x="12" y="408" xlink:href="#p" />
<use x="36" y="408" xlink:href="#p" />
<use x="48" y="408" xlink:href="#p" />
<use x="60" y="408" xlink:href="#p" />
<use x="84" y="408" xlink:href="#p" />
<use x="108" y="408" xlink:href="#p" />
<use x="180" y="408" xlink:href="#p" />
<use x="252" y="408" xlink:href="#p" />
<use x="264" y="408" xlink:href="#p" />
<use x="300" y="408" xlink:href="#p" />
<use x="312" y="408" xlink:href="#p" />
<use x="372" y="408" xlink:href="#p" />
<use x="396" y="408" xlink:href="#p" />
<use x="408" y="408" xlink:href="#p" />
<use x="12" y="420" xlink:href="#p" />
<use x="36" y="420" xlink:href="#p" />
<use x="48" y="420" xlink:href="#p" />
<use x="60" y="420" xlink:href="#p" />
<use x="84" y="420" xlink:href="#p" />
<use x="108" y="420" xlink:href="#p" />
<use x="120" y="420" xlink:href="#p" />
<use x="168" y="420" xlink:href="#p" />
<use x="180" y="420" xlink:href="#p" />
<use x="204" y="420" xlink:href="#p" />
<use x="228" y="420" xlink:href="#p" />
<use x="264" y="420" xlink:href="#p" />
<use x="276" y="420" xlink:href="#p" />
<use x="300" y="420" xlink:href="#p" />
<use x="324" y="420" xlink:href="#p" />
<use x="336" y="420" xlink:href="#p" />
<use x="348" y="420" xlink:href="#p" />
<use x="360" y="420" xlink:href="#p" />
<use x="384" y="420" xlink:href="#p" />
<use x="420" y="420" xlink:href="#p" />
<use x="444" y="420" xlink:href="#p" />
<use x="12" y="432" xlink:href="#p" />
<use x="84" y="432" xlink:href="#p" />
<use x="144" y="432" xlink:href="#p" />
<use x="168" y="432" xlink:href="#p" />
<use x="180" y="432" xlink:href="#p" />
<use x="192" y="432" xlink:href="#p" />
<use x="228" y="432" xlink:href="#p" />
<use x="240" y="432" xlink:href="#p" />
<use x="252" y="432" xlink:href="#p" />
<use x="276" y="432" xlink:href="#p" />
<use x="300" y="432" xlink:href="#p" />
<use x="336" y="432" xlink:href="#p" />
<use x="348" y="432" xlink:href="#p" />
<use x="360" y="432" xlink:href="#p" />
<use x="372" y="432" xlink:href="#p" />
<use x="444" y="432" xlink:href="#p" />
<use x="12" y="444" xlink:href="#p" />
<use x="24" y="444" xlink:href="#p" />
<use x="36" y="444" xlink:href="#p" />
<use x="48" y="444" xlink:href="#p" />
<use x="60" y="444" xlink:href="#p" />
<use x="72" y="444" xlink:href="#p" />
<use x="84" y="444" xlink:href="#p" />
<use x="132" y="444" xlink:href="#p" />
<use x="168" y="444" xlink:href="#p" />
<use x="204" y="444" xlink:href="#p" />
<use x="216" y="444" xlink:href="#p" />
<use x="240" y="444" xlink:href="#p" />
<use x="252" y="444" xlink:href="#p" />
<use x="276" y="444" xlink:href="#p" />
<use x="288" y="444" xlink:href="#p" />
<use x="300" y="444" xlink:href="#p" />
<use x="324" y="444" xlink:href="#p" />
<use x="336" y="444" xlink:href="#p" />
<use x="372" y="444" xlink:href="#p" />
<use x="384" y="444" xlink:href="#p" />
<use x="408" y="444" xlink:href="#p" />
<use x="420" y="444" xlink:href="#p" />
<use x="432" y="444" xlink:href="#p" />
<use x="444" y="444" xlink:href="#p" />
</g>
</svg>
\ No newline at end of file
<template>
<div>
<el-button type="primary" @click="dialogVisible = true" plain round>
<d2-icon name="question-circle-o" class="d2-mr-5"/>
需要帮助吗
</el-button>
<el-dialog
title="帮助"
width="600px"
:visible.sync="dialogVisible"
:append-to-body="true">
<div style="margin-top: -25px;">
<h2 class="d2-mt-0">
这里有一些参考资料
</h2>
<el-button-group>
<el-button @click="$open('https://d2.pub/zh/doc/d2-admin')">
<d2-icon name="book" class="d2-mr-5"/>
文档
</el-button>
<el-button @click="$open('https://github.com/d2-projects/d2-admin/issues?q=is%3Aissue+is%3Aclosed')">
<d2-icon name="question" class="d2-mr-5"/>
历史提问
</el-button>
<el-button @click="$open('https://github.com/d2-projects/d2-admin/issues/new/choose')">
<d2-icon name="plus" class="d2-mr-5"/>
提交问题
</el-button>
</el-button-group>
<h2>询问其它使用者或作者</h2>
<el-row :gutter="20">
<el-col :span="12">
<img src="./image/qq.svg" style="width: 100%;">
<div class="d2-help--qr-info">
请使用手机 QQ 扫面上方二维码<br/>
1群 806395827 (满) | 2群 592981556
</div>
</el-col>
<el-col :span="12">
<img src="./image/we.svg" style="width: 100%;">
<div class="d2-help--qr-info">
请使用手机微信扫面上方二维码<br/>
添加作者微信好友,邀请加入微信群
</div>
</el-col>
</el-row>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data () {
return {
dialogVisible: false
}
}
}
</script>
<style lang="scss" scoped>
.d2-help--qr-info {
background-color: #f4f4f5;
color: #909399;
width: 100%;
padding: 8px 16px;
margin: 0;
box-sizing: border-box;
border-radius: 4px;
position: relative;
overflow: hidden;
opacity: 1;
display: flex;
align-items: center;
transition: opacity .2s;
}
</style>
<template>
<div class="d2-page-cover">
<div class="d2-page-cover__logo">
<slot/>
</div>
<p class="d2-page-cover__title">D2 Admin {{$version}}</p>
<p class="d2-page-cover__sub-title">优雅的中后台集成方案</p>
<p class="d2-page-cover__build-time">FINAL BUILD TIME {{$buildTime}}</p>
<slot name="footer"/>
<a target="blank" href="https://github.com/d2-projects/d2-admin">
<img
style="position: absolute; top: 0; right: 0; border: 0; width: 150px;"
src="./image/darkblue@2x.png"
alt="Fork me on GitHub">
</a>
</div>
</template>
<style lang="scss" scoped>
.d2-page-cover {
@extend %full;
@extend %unable-select;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
.d2-page-cover__logo {
img {
width: 200px;
}
}
.d2-page-cover__title {
margin: 0px;
margin-bottom: 20px;
font-weight: bold;
color: $color-text-main;
}
.d2-page-cover__sub-title {
margin: 0px;
margin-bottom: 5px;
color: $color-text-normal;
}
.d2-page-cover__build-time {
margin: 0px;
margin-bottom: 10px;
font-size: 12px;
color: $color-text-placehoder;
}
}
</style>
import page from './page'
export default page
<template>
<d2-container class="page">
<d2-page-cover>
<d2-icon-svg class="logo" name="d2-admin"/>
<template slot="footer">
<div class="btn-group">
<span class="btn-group__btn" @click="$open('https://github.com/d2-projects')">开源组织</span> |
<span class="btn-group__btn" @click="$open('https://d2.pub/zh/doc/d2-admin')">文档</span> |
<span class="btn-group__btn" @click="$open('https://github.com/d2-projects/d2-admin-start-kit')">简化版</span> |
<span class="btn-group__btn" @click="$open('https://juejin.im/user/57a48b632e958a006691b946/posts')">掘金</span> |
<el-popover :width="172" trigger="hover">
<p class="d2-mt-0 d2-mb-10">今日前端</p>
<img src="./image/qr@2x.png" style="width: 172px;">
<span slot="reference" class="btn-group__btn btn-group__btn--link">
<d2-icon name="weixin"/>
微信公众号
</span>
<p style="font-size: 12px; margin-top: 0px; margin-bottom: 0px;">
官方公众号,主要推送前端技术类文章、框架资源、学习教程,以及 D2 系列项目更新信息
</p>
</el-popover>
</div>
<d2-badge/>
<d2-help/>
</template>
</d2-page-cover>
</d2-container>
</template>
<script>
import D2Badge from './components/d2-badge'
import D2Help from './components/d2-help'
import D2PageCover from './components/d2-page-cover'
export default {
components: {
D2Badge,
D2Help,
D2PageCover
}
}
</script>
<style lang="scss" scoped>
.page {
.logo {
width: 120px;
}
.btn-group {
color: $color-text-placehoder;
font-size: 12px;
line-height: 12px;
margin-top: 0px;
margin-bottom: 20px;
.btn-group__btn {
color: $color-text-sub;
&:hover {
color: $color-text-main;
}
&.btn-group__btn--link {
color: $color-primary;
}
}
}
}
</style>
<template>
<d2-container>
<el-table
:data="log"
size="mini"
style="width: 100%"
empty-text="暂无日志信息"
stripe>
<!-- 时间 -->
<el-table-column
prop="time"
label="Time"
width="140">
</el-table-column>
<!-- 信息 -->
<el-table-column
prop="message"
label="Message">
</el-table-column>
<!-- 触发页面 -->
<el-table-column
label="Url"
align="center"
min-width="200">
<template slot-scope="scope">
{{get(scope.row, 'meta.url')}}
</template>
</el-table-column>
<!-- 触发组件 -->
<el-table-column
label="Tag"
align="center"
min-width="120">
<template slot-scope="scope">
<el-tag
v-if="get(scope.row, 'meta.instance.$vnode.componentOptions.tag')"
type="info"
size="mini">
&#60;{{get(scope.row, 'meta.instance.$vnode.componentOptions.tag')}}&gt;
</el-tag>
</template>
</el-table-column>
<!-- 查看详情 -->
<el-table-column
fixed="right"
align="center"
label="More"
width="100">
<template slot-scope="scope">
<el-button
type="primary"
size="mini"
@click="handleShowMore(scope.row)">
<d2-icon name="eye"/>
</el-button>
</template>
</el-table-column>
</el-table>
<el-button
slot="footer"
type="primary"
size="mini"
:loading="uploading"
@click="handleUpload">
<d2-icon name="cloud-upload"/>
Upload {{log.length}} log data
</el-button>
</d2-container>
</template>
<script>
import { mapState } from 'vuex'
import { get } from 'lodash'
export default {
data () {
return {
uploading: false
}
},
computed: {
...mapState('d2admin/log', [
'log'
])
},
methods: {
get,
handleShowMore (log) {
// 打印一条日志的所有信息到控制台
this.$notify({
type: 'info',
title: '日志详情',
message: '完整的日志内容已经打印到控制台'
})
this.$log.capsule('D2Admin', 'handleShowMore', 'primary')
console.group(log.message)
console.log('time: ', log.time)
console.log('type: ', log.type)
console.log(log.meta)
console.groupEnd()
},
// 日志上传
handleUpload () {
this.uploading = true
this.$notify({
type: 'info',
title: '日志上传',
message: `开始上传${this.log.length}条日志`
})
setTimeout(() => {
this.uploading = false
this.$notify({
type: 'success',
title: '日志上传',
message: '上传成功'
})
}, 3000)
}
}
}
</script>
import page from './page'
export default page
<template>
<div class="page-login">
<div class="page-login--layer page-login--layer-area"></div>
<div class="page-login--layer">
<div
class="page-login--content"
flex="dir:top main:justify cross:stretch box:justify"
>
<div class="page-login--content-header"></div>
<div
class="page-login--content-main"
flex="dir:top main:center cross:center"
>
<h1 class="title">电动车后台管理</h1>
<!-- form -->
<div class="page-login--form">
<el-card shadow="never">
<el-form
ref="loginForm"
label-position="top"
:model="formLogin"
size="default"
@keyup.native.enter="submitDebounce"
>
<el-form-item prop="loginIsAdmin">
<el-radio-group
v-model="formLogin.loginIsAdmin"
@change="changeLogin"
>
<el-radio :label="false">用户登录</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="username">
<el-input
type="text"
v-model="formLogin.username"
placeholder="用户名"
>
<i slot="prepend" class="fa fa-user-circle-o"></i>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
v-model="formLogin.password"
placeholder="密码"
>
<i slot="prepend" class="fa fa-keyboard-o"></i>
</el-input>
</el-form-item>
<el-button
size="default"
@click="submitDebounce"
@keyup.enter="submitDebounce"
type="primary"
:loading="isLoading"
class="button-login"
>
登录
</el-button>
</el-form>
</el-card>
</div>
</div>
<div class="page-login--content-footer">
<p class="page-login--content-footer-options"></p>
</div>
</div>
</div>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { mapActions } from 'vuex'
import localeMixin from '@/locales/mixin.js'
import { debounce } from '../../../utils/tool'
export default {
mixins: [localeMixin],
data() {
return {
// 是否正在提交
isLoading: false,
timeInterval: null,
time: dayjs().format('HH:mm:ss'),
// 快速选择用户
dialogVisible: false,
users: [
{
name: 'Admin',
username: '',
password: ''
},
{
name: 'user',
username: '',
password: ''
}
],
// 表单
formLogin: {
username: '',
password: '',
loginIsAdmin: false,
code: ''
// platform: '106'
// code: 'v9am'
},
// 表单校验
rules: {
username: [
{
required: true,
message: '请输入用户名',
trigger: 'blur'
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
}
],
tenantId: this.$route.params.id,
code: [
{
required: true,
message: '请输入验证码',
trigger: 'blur'
}
]
}
}
},
watch: {
$route(to, from) {
this.tenantId = this.$route.params.id
}
},
mounted() {
// 刷新时间
this.timeInterval = setInterval(() => {
this.refreshTime()
}, 1000)
},
beforeDestroy() {
clearInterval(this.timeInterval)
},
methods: {
...mapActions('d2admin/account', ['login']),
refreshTime() {
this.time = dayjs().format('HH:mm:ss')
},
/**
* @description 接收选择一个用户快速登录的事件
* @param {Object} user 用户信息
*/
handleUserBtnClick(user) {
// this.formLogin.username = user.username
// this.formLogin.password = user.password
// this.formLogin.platform = user.platform
// this.submit()
},
changeLogin() {
this.formLogin.username = ''
this.formLogin.password = ''
this.$refs.loginForm.clearValidate()
},
submitDebounce: debounce(async function () {
await this.submit()
}, 600),
/**
* @description 提交表单
*/
// 提交登录信息
async submit() {
console.log('我执行了')
let isValid
this.$refs.loginForm.validate((valid) => {
isValid = valid
})
if (isValid) {
this.isLoading = true
try {
await this.login({
username: this.formLogin.username,
password: this.formLogin.password,
loginIsAdmin: this.formLogin.loginIsAdmin,
tenantId: this.$route.params.id
})
this.isLoading = false
sessionStorage.removeItem('referer', null)
// 重定向对象不存在则返回顶层路径
this.$router.replace(this.$route.query.redirect || '/')
} catch (err) {
this.isLoading = false
return err
}
} else {
// 登录表单校验失败
// this.$message.error('表单校验失败,请检查')
}
}
}
}
</script>
<style lang="scss">
.page-login {
@extend %unable-select;
background-image: url("../../../../public/image/background.jpg");
background-size: 100% 100%;
/* $backgroundColor: #f0f2f5; */
// ---
/* background-color: $backgroundColor; */
height: 100%;
position: relative;
// 层
.page-login--layer {
@extend %full;
overflow: auto;
}
.page-login--layer-area {
overflow: hidden;
}
// 时间
.page-login--layer-time {
font-size: 24em;
font-weight: bold;
color: rgba(0, 0, 0, 0.03);
overflow: hidden;
}
// 登陆页面控件的容器
.page-login--content {
height: 100%;
min-height: 500px;
}
.page-login--content-main {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0% 0% 10% 60%;
.title {
color: #fff;
}
}
// header
.page-login--content-header {
padding: 1em 0;
.page-login--content-header-motto {
margin: 0px;
padding: 0px;
color: $color-text-normal;
text-align: center;
font-size: 12px;
}
}
// main
.page-login--logo {
width: 240px;
margin-bottom: 2em;
margin-top: -2em;
}
// 登录表单
.page-login--form {
width: 280px;
// 卡片
.el-card {
margin-bottom: 5px;
}
// 登录按钮
.button-login {
width: 100%;
}
// 输入框左边的图表区域缩窄
.el-input-group__prepend {
padding: 0px 14px;
}
.login-code {
height: 40px - 2px;
display: block;
margin: 0px -20px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
// 登陆选项
.page-login--options {
font-size: 14px;
color: $color-primary;
// margin-bottom: 15px;
text-align: center;
font-weight: bold;
}
.page-login--quick {
width: 100%;
}
}
// 快速选择用户面板
.page-login--quick-user {
@extend %flex-center-col;
padding: 10px 0px;
border-radius: 4px;
&:hover {
background-color: $color-bg;
i {
color: $color-text-normal;
}
span {
color: $color-text-normal;
}
}
i {
font-size: 36px;
color: $color-text-sub;
}
span {
font-size: 12px;
margin-top: 10px;
color: $color-text-sub;
}
}
// footer
.page-login--content-footer {
padding: 1em 0;
.page-login--content-footer-locales {
padding: 0px;
margin: 0px;
margin-bottom: 15px;
font-size: 12px;
line-height: 12px;
text-align: center;
color: $color-text-normal;
a {
color: $color-text-normal;
margin: 0 0.5em;
&:hover {
color: $color-text-main;
}
}
}
.page-login--content-footer-copyright {
padding: 0px;
margin: 0px;
margin-bottom: 10px;
font-size: 12px;
line-height: 12px;
text-align: center;
color: $color-text-normal;
a {
color: $color-text-normal;
}
}
.page-login--content-footer-options {
padding: 0px;
margin: 0px;
font-size: 12px;
line-height: 12px;
text-align: center;
a {
color: $color-text-normal;
margin: 0 1em;
}
}
}
// 背景
.circles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
margin: 0px;
padding: 0px;
li {
position: absolute;
display: block;
list-style: none;
width: 20px;
height: 20px;
background: #fff;
animation: animate 25s linear infinite;
bottom: -200px;
@keyframes animate {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
border-radius: 0;
}
100% {
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
border-radius: 50%;
}
}
&:nth-child(1) {
left: 15%;
width: 80px;
height: 80px;
animation-delay: 0s;
}
&:nth-child(2) {
left: 5%;
width: 20px;
height: 20px;
animation-delay: 2s;
animation-duration: 12s;
}
&:nth-child(3) {
left: 70%;
width: 20px;
height: 20px;
animation-delay: 4s;
}
&:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
animation-delay: 0s;
animation-duration: 18s;
}
&:nth-child(5) {
left: 65%;
width: 20px;
height: 20px;
animation-delay: 0s;
}
&:nth-child(6) {
left: 75%;
width: 150px;
height: 150px;
animation-delay: 3s;
}
&:nth-child(7) {
left: 35%;
width: 200px;
height: 200px;
animation-delay: 7s;
}
&:nth-child(8) {
left: 50%;
width: 25px;
height: 25px;
animation-delay: 15s;
animation-duration: 45s;
}
&:nth-child(9) {
left: 20%;
width: 15px;
height: 15px;
animation-delay: 2s;
animation-duration: 35s;
}
&:nth-child(10) {
left: 85%;
width: 150px;
height: 150px;
animation-delay: 0s;
animation-duration: 11s;
}
}
}
}
</style>
@head = combo.admin.isonnhe.com
@token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.Aw4SDXtSvULbjSVS0GPuXaFkU5FJEVa0bbiXe2Uj5uo
@constantHead = combo.admin.isonnhe.com
###
POST http://{{head}}/admin/api/login HTTP/1.1
content-type: application/json
{
"password": "sonnhe3910",
"username": "admin"
}
###
# 添加 商户
POST http://{{head}}/admin/api/company/insert/supplier HTTP/1.1
token: {{token}}
content-type: application/json
{
"companyName": "ceshiE",
"provinceId": "1",
"cityId": "72",
"countyId": "2799",
"townId": "0",
"contactName": "121r",
"contactPhone": "15673701001",
"loginName": "zzx123",
"loginPwd": "123456z",
"merchantId": "1659484214",
"merchantName": "大学城餐厅1231q1",
"nature": "1",
"businessType": "1",
"validUntil": "2025-01-15 00:00:00",
"payRate": "3",
"serviceRate": "3"
}
###
# 获取 商户 列表
GET http://{{head}}/admin/api/company/get/supplier/all/page
?companyNameLike=
&nature=
&businessType=
&status=
&provinceId=
&cityId=
&contactNameLike=
&contactPhoneLike=
&currentPage=1
&pageSize=10
token: {{token}}
Content-Type: application/json
###
# 获取 供应商 性质
GET http://{{constantHead}}/basic/api/constant/get/supplier/nature HTTP/1.1
###
# 获取 供应商 类型
GET http://{{constantHead}}/basic/api/constant/get/supplier/businessType HTTP/1.1
module.exports = {
env: {
jest: true
},
rules: {
'import/no-extraneous-dependencies': 'off'
}
}
\ No newline at end of file
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const VueFilenameInjector = require('@d2-projects/vue-filename-injector')
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const forElementUI = require('webpack-theme-color-replacer/forElementUI')
const cdnDependencies = require('./dependencies-cdn')
const { chain, set, each } = require('lodash')
// 拼接路径
const resolve = dir => require('path').join(__dirname, dir)
// 增加环境变量
process.env.VUE_APP_VERSION = require('./package.json').version
process.env.VUE_APP_BUILD_TIME = require('dayjs')().format('YYYY-M-D HH:mm:ss')
// 基础路径 注意发布之前要先修改这里
const publicPath = process.env.VUE_APP_PUBLIC_PATH || '/'
// 设置不参与构建的库
const externals = {}
cdnDependencies.forEach(pkg => { externals[pkg.name] = pkg.library })
// 引入文件的 cdn 链接
const cdn = {
css: cdnDependencies.map(e => e.css).filter(e => e),
js: cdnDependencies.map(e => e.js).filter(e => e)
}
// 多页配置,默认未开启,如需要请参考 https://cli.vuejs.org/zh/config/#pages
const pages = undefined
// const pages = {
// index: './src/main.js',
// subpage: './src/subpage.js'
// }
module.exports = {
// 根据你的实际情况更改这里
publicPath,
lintOnSave: true,
devServer: {
proxy: {
// 四级地址的请求地址
'^/jdAddress': {
target: 'http://jd.address.service.isonnhe.com',
ws: true,
changeOrigin: true,
},
'^/api': {
target: 'http://bicycle.admin.isonnhe.com/car/admin',// 测试地址
// target: 'https://food.sonnhe.com/middleman',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
},
'^/constant': {
target: 'http://bicycle.isonnhe.com/car/basic/api',// 测试地址
// target: 'https://food.sonnhe.com/middleman',
ws: true,
changeOrigin: true,
},
},
publicPath, // 和 publicPath 保持一致
disableHostCheck: process.env.NODE_ENV === 'development' // 关闭 host check,方便使用 ngrok 之类的内网转发工具
},
css: {
loaderOptions: {
// 设置 scss 公用变量文件
sass: {
prependData: '@import \'~@/assets/style/public.scss\';'
}
}
},
pages,
configureWebpack: config => {
const configNew = {}
if (process.env.NODE_ENV === 'production') {
configNew.externals = externals
configNew.plugins = [
// gzip
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false
})
]
}
return configNew
},
// 默认设置: https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-service/lib/config/base.js
chainWebpack: config => {
/**
* 添加 CDN 参数到 htmlWebpackPlugin 配置中
* 已适配多页
*/
const htmlPluginNames = chain(pages).keys().map(page => 'html-' + page).value()
const targetHtmlPluginNames = htmlPluginNames.length ? htmlPluginNames : ['html']
each(targetHtmlPluginNames, name => {
config.plugin(name).tap(options => {
set(options, '[0].cdn', process.env.NODE_ENV === 'production' ? cdn : [])
return options
})
})
/**
* 删除懒加载模块的 prefetch preload,降低带宽压力
* https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
* https://cli.vuejs.org/zh/guide/html-and-static-assets.html#preload
* 而且预渲染时生成的 prefetch 标签是 modern 版本的,低版本浏览器是不需要的
*/
config.plugins
.delete('prefetch')
.delete('preload')
// 解决 cli3 热更新失效 https://github.com/vuejs/vue-cli/issues/1559
config.resolve
.symlinks(true)
config
.plugin('theme-color-replacer')
.use(ThemeColorReplacer, [{
fileName: 'css/theme-colors.[contenthash:8].css',
matchColors: [
...forElementUI.getElementUISeries(process.env.VUE_APP_ELEMENT_COLOR) // Element-ui主色系列
],
externalCssFiles: ['./node_modules/element-ui/lib/theme-chalk/index.css'], // optional, String or string array. Set external css files (such as cdn css) to extract colors.
changeSelector: forElementUI.changeSelector
}])
config
// 开发环境 sourcemap 不包含列信息
.when(process.env.NODE_ENV === 'development',
config => config.devtool('cheap-source-map')
)
// 预览环境构建 vue-loader 添加 filename
.when(
process.env.VUE_APP_SCOURCE_LINK === 'TRUE',
config => VueFilenameInjector(config, {
propName: process.env.VUE_APP_SOURCE_VIEWER_PROP_NAME
})
)
// markdown
config.module
.rule('md')
.test(/\.md$/)
.use('text-loader')
.loader('text-loader')
.end()
// svg
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule
.include
.add(resolve('src/assets/svg-icons/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'd2-[name]'
})
.end()
// image exclude
const imagesRule = config.module.rule('images')
imagesRule
.test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/)
.exclude
.add(resolve('src/assets/svg-icons/icons'))
.end()
// 重新设置 alias
config.resolve.alias
.set('@api', resolve('src/api'))
// 分析工具
if (process.env.npm_config_report) {
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
}
},
// 不输出 map 文件
productionSourceMap: false,
// i18n
pluginOptions: {
i18n: {
locale: 'zh-chs',
fallbackLocale: 'en',
localeDir: 'locales',
enableInSFC: true
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment