Compare commits
88 Commits
20221023_v
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
10ac766489 | |
|
|
d05ac59983 | |
|
|
0793f9fb58 | |
|
|
7103c121f3 | |
|
|
3c1001dd76 | |
|
|
17f45c1c99 | |
|
|
3d80604f11 | |
|
|
66c0644019 | |
|
|
c9f6fe57fb | |
|
|
8113b0b11e | |
|
|
bab5825081 | |
|
|
7c11af66a8 | |
|
|
a0de373666 | |
|
|
39de7fe1ac | |
|
|
3b0a68c739 | |
|
|
8fa9c3c4a2 | |
|
|
1d57dd78b8 | |
|
|
2925c5d832 | |
|
|
5ab9b4ec08 | |
|
|
3baf83c09b | |
|
|
fda5a40864 | |
|
|
e7459bc460 | |
|
|
0563748bf6 | |
|
|
b5684d1d59 | |
|
|
321931f6de | |
|
|
b45c0f4b08 | |
|
|
c53834bcaa | |
|
|
5f181fae03 | |
|
|
66a3c584df | |
|
|
8ac3bc681a | |
|
|
f5f1d28435 | |
|
|
6c87ac2508 | |
|
|
bdfd41c4d6 | |
|
|
c037e12157 | |
|
|
5d8383c519 | |
|
|
1361525f95 | |
|
|
795ece2225 | |
|
|
2768c33f7f | |
|
|
9361d9ccad | |
|
|
a0ac6f9424 | |
|
|
1caf9733e2 | |
|
|
22cd6da835 | |
|
|
014eee5584 | |
|
|
3921bd2780 | |
|
|
ea52fb0fec | |
|
|
03a104ac52 | |
|
|
1cf1c0364a | |
|
|
eeb54d1726 | |
|
|
2fa87b524a | |
|
|
09206b884e | |
|
|
cc467db879 | |
|
|
092fbe5bd5 | |
|
|
4049c625b6 | |
|
|
949fc6f25b | |
|
|
8a26b162f9 | |
|
|
dcbcc775b6 | |
|
|
accd569fba | |
|
|
afe261072d | |
|
|
e41942b55b | |
|
|
ffc8856c96 | |
|
|
da47b1eeaf | |
|
|
543b221a72 | |
|
|
6d8c9278e5 | |
|
|
418a8e77d8 | |
|
|
4699093e6d | |
|
|
a981538a69 | |
|
|
fab8d0df4e | |
|
|
97275bf98d | |
|
|
7ff2dc0108 | |
|
|
8baf570b8b | |
|
|
ea18a476e1 | |
|
|
1943e2339a | |
|
|
78e38f9e92 | |
|
|
294f2458e4 | |
|
|
bb848895f7 | |
|
|
1e4d2633bc | |
|
|
b6c0f49f01 | |
|
|
00acb21c8f | |
|
|
55bba0f121 | |
|
|
940d9af6fb | |
|
|
f7dac95732 | |
|
|
6fbf1d3514 | |
|
|
00ea15f56b | |
|
|
870792f6ec | |
|
|
3da2172bce | |
|
|
8f00712c43 | |
|
|
ba685d1753 | |
|
|
af9cb48659 |
|
|
@ -2,4 +2,4 @@
|
|||
NODE_ENV = 'development'
|
||||
|
||||
# 本地环境接口地址
|
||||
VUE_APP_BASE_API = '/api'
|
||||
VITE_APP_BASE_API = '/api'
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
NODE_ENV = "production"
|
||||
|
||||
# 线上环境接口地址
|
||||
VUE_APP_BASE_API = '/api'
|
||||
VITE_APP_BASE_API = '/api'
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ NODE_ENV = "test"
|
|||
|
||||
|
||||
# 测试环境接口地址
|
||||
VUE_APP_BASE_API = '/api'
|
||||
VITE_APP_BASE_API = '/api'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
|
||||
*.sh
|
||||
node_modules
|
||||
*.css
|
||||
*.jpg
|
||||
*.jpeg
|
||||
*.png
|
||||
*.gif
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node":true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:vue/vue3-essential",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"vue",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
globals: {
|
||||
defineProps: 'readonly',
|
||||
defineEmits: 'readonly',
|
||||
},
|
||||
rules: {
|
||||
'no-console': 'off', // 禁止调用console对象的方法。
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-empty-function': ['off'], // 关闭空函数警告
|
||||
'@typescript-eslint/no-inferrable-types': 'off', // 可以轻松推断的显式类型可能会增加不必要的冗长
|
||||
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间
|
||||
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
|
||||
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
|
||||
'vue/no-v-html': 'off', // 禁止使用 v-html
|
||||
'no-undef': 'off',
|
||||
'no-redeclare': 'off',
|
||||
'no-self-assign': 'off',
|
||||
'no-sparse-arrays': 'off',
|
||||
'vue/valid-v-for': 'off',
|
||||
'vue/no-unused-vars': 'off',
|
||||
'vue/require-v-for-key': 'off',
|
||||
'no-useless-escape': 'off',
|
||||
'vue/require-explicit-emits': 'off',
|
||||
'no-case-declarations': 'off', // 不允许在 case 子句中使用词法声明
|
||||
//禁止非空断言非空断言是在变量后面添加一个感叹号(!),表示该变量一定存在,不会为 null 或 undefined
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'vue/require-prop-types': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
'no-async-promise-executor': 'off',
|
||||
'vue/no-template-shadow': 'off',
|
||||
'vue/require-default-prop': 'off', // 此规则要求为每个 prop 为必填时,必须提供默认值
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
// // 禁止定义未使用的变量
|
||||
'off',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ pnpm-debug.log*
|
|||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
dist
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
module.exports = {
|
||||
// 一行的字符数,如果超过会进行换行,默认为80
|
||||
printWidth: 100,
|
||||
printWidth: 140,
|
||||
// 缩进制表符宽度 | 空格数
|
||||
tabWidth: 2,
|
||||
// 行位是否使用分号,默认为true
|
||||
semi: false,
|
||||
vueIndentScriptAndStyle: true,
|
||||
45
README.md
|
|
@ -1,39 +1,43 @@
|
|||
## 简介
|
||||
vue-element-perfect 是一个后台前端解决方案, 基于Vue3.0+TS+Element-plus实现。它使用了最新的前端技术栈、动态路由,权限验证,并且有着丰富的组件。
|
||||
|
||||
vue-element-perfect 是一个后台前端解决方案,它使用了最新的前端技术栈、动态路由,权限验证,并且有着丰富的组件,企业级中后台解决方案,可免费商用,同时支持PC、平板、手机
|
||||
本项目也参考了很多开源的项目、
|
||||
### 在线预览
|
||||
- link —— [http://182.61.5.190:8889/ ](http://182.61.5.190:8889/)
|
||||
- gitee国内访问地址:https://yuanzbz.gitee.io/vue-admin-perfect/#/home
|
||||
- github site : https://zouzhibin.github.io/vue-admin-perfect/
|
||||
|
||||
### git仓库
|
||||
- 基础功能版本预览:https://yuanzbz.gitee.io/vue-admin-simple
|
||||
|
||||
|
||||
|
||||
### git仓库(欢迎 Star⭐)
|
||||
- Gitee: —— [https://gitee.com/yuanzbz/vue-admin-perfect](https://gitee.com/yuanzbz/vue-admin-perfect)
|
||||
- GitHub: —— [https://github.com/zouzhibin/vue-admin-perfect](https://github.com/zouzhibin/vue-admin-perfect)
|
||||
- 基础功能版本: —— [https://gitee.com/yuanzbz/vue-admin-simple](https://gitee.com/yuanzbz/vue-admin-simple)
|
||||
|
||||
## 项目功能
|
||||
- 使用Vue3.0开发,单文件组件采用<script setup>
|
||||
- 采用 Vite3 作为项目开发、打包工具(配置了 Gzip 打包、TSX 语法、跨域代理)
|
||||
- 整个项目集成了 TypeScript
|
||||
- 登录逻辑,使用vue-router进行路由权限拦截,判断,路由懒加载
|
||||
- 使用 keep-alive 对整个页面进行缓存,支持多级嵌套页面
|
||||
- 侧边栏导航动态的显示
|
||||
- 集成各种打印功能,图片打印、表格打印、普通打印
|
||||
- 侧边栏导航菜单栏动态的显示
|
||||
- 各种可视化地图组件
|
||||
- 头像裁剪、图片上传
|
||||
- 使用 Pinia替代 Vuex,轻量、简单、易用
|
||||
- 导出excel,自定义样式导出excel、多表头导出
|
||||
- 表单、表格
|
||||
- 水印
|
||||
- 多标签显示
|
||||
- 表单、表格、水印、多标签显示、打印功能,图片打印、表格打印、普通打印、二维码、拖拽、markdown、头像裁剪、图片上传...
|
||||
- 使用 Prettier 统一格式化代码,集成 Eslint、代码校验规范
|
||||
- 其他各组件
|
||||
|
||||
## 安装
|
||||
- Clone
|
||||
```
|
||||
# 克隆项目
|
||||
# GitHub
|
||||
git clone https://github.com/zouzhibin/vue-admin-perfect.git
|
||||
# Gitee
|
||||
git clone https://gitee.com/yuanzbz/vue-admin-perfect.git
|
||||
```
|
||||
|
||||
## 分支管理
|
||||
- master 技术采用 vite + vue3.0 + Typescript + pinia
|
||||
```
|
||||
注意:使用 Vite 构建工具,需要 Node.js 版本 >= 12.0.0 查看 Node.js 版本 node-v
|
||||
```
|
||||
- vue-admin-simple 简易版本
|
||||
- vite-vuex vite + vue3.0 + Typescript + vuex
|
||||
- vue-i18n 语言切换版本
|
||||
- webpack 技术采用 webpack + vue3.0 + Typescript + vuex
|
||||
|
|
@ -48,9 +52,12 @@ vue-element-perfect 是一个后台前端解决方案, 基于Vue3.0+TS+Element-p
|
|||
npm install
|
||||
cnpm install
|
||||
yarn
|
||||
# npm install 安装失败,请升级 nodejs 到 16 以上,或尝试使用以下命令:
|
||||
npm install --registry=https://registry.npm.taobao.org
|
||||
```
|
||||
## 运行
|
||||
## 运行打包
|
||||
```
|
||||
npm run dev
|
||||
npm run build
|
||||
```
|
||||
## eslint+prettier
|
||||
|
|
@ -98,6 +105,10 @@ vue-admin-perfect
|
|||
└─ vite.config.ts # vite 配置
|
||||
```
|
||||
|
||||
### 微信交流群
|
||||
| 微信二维码 |
|
||||
| :----------------------------------------------------------------------------------: |
|
||||
| <img src="http://182.61.5.190:8889/we.png" width=170/> |
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// 文档参考:https://cz-git.qbb.sh/zh/config/
|
||||
// cz.config.js kk
|
||||
/** @type {import('cz-git').CommitizenGitOptions} */
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes('init')],
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
// alias: { fd: 'docs: fix typos' },
|
||||
// messages: {
|
||||
// type: 'Select the type of change that you\'re committing:',
|
||||
// scope: 'Denote the SCOPE of this change (optional):',
|
||||
// customScope: 'Denote the SCOPE of this chang e:',
|
||||
// subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
|
||||
// body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
|
||||
// breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
|
||||
// footerPrefixsSelect: 'Select the ISSUES type of changeList by this change (optional):',
|
||||
// customFooterPrefixs: 'Input ISSUES prefix:',
|
||||
// footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
|
||||
// confirmCommit: 'Are you sure you want to proceed with the commit above?'
|
||||
// },
|
||||
prompt: {
|
||||
// 中英文对照版
|
||||
messages: {
|
||||
type: '选择你要提交的类型 :',
|
||||
scope: '选择一个提交范围(可选):',
|
||||
customScope: '请输入自定义的提交范围 :',
|
||||
subject: '填写简短精炼的变更描述 :\n',
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
footerPrefixesSelect: '选择关联issue前缀(可选):',
|
||||
customFooterPrefix: '输入自定义issue前缀 :',
|
||||
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
confirmCommit: '是否提交或修改commit ?',
|
||||
},
|
||||
types: [
|
||||
{ value: '特性', name: '特性: 新增功能' },
|
||||
{ value: '修复', name: '修复: 修复缺陷' },
|
||||
{ value: '文档', name: '文档: 文档变更' },
|
||||
{ value: '格式', name: '格式: 代码格式(不影响功能,例如空格、分号等格式修正)' },
|
||||
{ value: '重构', name: '重构: 代码重构(不包括 bug 修复、功能新增)' },
|
||||
{ value: '性能', name: '性能: 性能优化' },
|
||||
{ value: '测试', name: '测试: 添加疏漏测试或已有测试改动' },
|
||||
{
|
||||
value: '构建',
|
||||
name: '构建: 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)',
|
||||
},
|
||||
{ value: '集成', name: '集成: 修改 CI 配置、脚本' },
|
||||
{ value: '回退', name: '回退: 回滚 commit' },
|
||||
{ value: '其他', name: '其他: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
|
||||
],
|
||||
// emptyScopesAlias: 'empty: 不填写',
|
||||
// customScopesAlias: 'custom: 自定义',
|
||||
|
||||
useEmoji: true,
|
||||
// emojiAlign: 'center',
|
||||
themeColorCode: '',
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
markBreakingChangeMode: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixs: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
|
||||
customIssuePrefixsAlign: 'top',
|
||||
emptyIssuePrefixsAlias: 'skip',
|
||||
customIssuePrefixsAlias: 'custom',
|
||||
allowCustomIssuePrefixs: true,
|
||||
allowEmptyIssuePrefixs: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: '',
|
||||
},
|
||||
}
|
||||
134
index.html
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<html lang="en" >
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
|
|
@ -9,60 +9,112 @@
|
|||
<body>
|
||||
<div id="app">
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.first-loading-wrp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 90vh;
|
||||
min-height: 90vh;
|
||||
}
|
||||
.init-loading-wrap{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.first-loading-wrp > h1 {
|
||||
font-size: 30px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.first-loading-wrp .loading-wrp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 98px;
|
||||
}
|
||||
.init-loading-inner {
|
||||
width: 20px;
|
||||
height: 50px;
|
||||
background: #1890ff;
|
||||
float: left;
|
||||
margin: 0 3px;
|
||||
animation: init-loading-inner linear 1s infinite;
|
||||
-webkit-animation: init-loading-inner linear 1s infinite;
|
||||
.dot {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
font-size: 64px;
|
||||
transform: rotate(45deg);
|
||||
animation: antRotate 1.2s infinite linear;
|
||||
}
|
||||
.init-loading-inner:nth-child(1){
|
||||
animation-delay:0s;
|
||||
|
||||
.dot i {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 100%;
|
||||
opacity: 0.3;
|
||||
transform: scale(0.75);
|
||||
transform-origin: 50% 50%;
|
||||
animation: antSpinMove 1s infinite linear alternate;
|
||||
}
|
||||
.init-loading-inner:nth-child(2){
|
||||
animation-delay:0.15s;
|
||||
|
||||
.dot i:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.init-loading-inner:nth-child(3){
|
||||
animation-delay:0.3s;
|
||||
|
||||
.dot i:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
.init-loading-inner:nth-child(4){
|
||||
animation-delay:0.45s;
|
||||
|
||||
.dot i:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-animation-delay: 0.8s;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
.init-loading-inner:nth-child(5){
|
||||
animation-delay:0.6s;
|
||||
|
||||
.dot i:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
-webkit-animation-delay: 1.2s;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
@keyframes init-loading-inner{
|
||||
0%,60%,100% {transform: scale(1);}
|
||||
30% {transform: scaleY(3);}
|
||||
@keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes init-loading-inner{
|
||||
0%,60%,100% {transform: scale(1);}
|
||||
30% {transform: scaleY(3);}
|
||||
|
||||
@-webkit-keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="init-loading-wrap">
|
||||
<div class="init-loading-inner"></div>
|
||||
<div class="init-loading-inner"></div>
|
||||
<div class="init-loading-inner"></div>
|
||||
<div class="init-loading-inner"></div>
|
||||
<div class="init-loading-inner"></div>
|
||||
<div id="vue-admin-perfect">
|
||||
<div class="first-loading-wrp">
|
||||
<div class="loading-wrp">
|
||||
<span class="dot dot-spin">
|
||||
<i></i>
|
||||
<i></i>
|
||||
<i></i>
|
||||
<i></i>
|
||||
</span>
|
||||
</div>
|
||||
<h1>vue-admin-perfect</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
|
|
|||
43
package.json
|
|
@ -2,17 +2,19 @@
|
|||
"name": "vue-admin-perfect",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:test": "vite build --mode test",
|
||||
"build:prod": "vite build --mode production",
|
||||
"commit": "git add -A && czg && git push",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build:ts": "vue-tsc --noEmit --skipLibCheck && vite build",
|
||||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix",
|
||||
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
|
||||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx",
|
||||
"lint:fix": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix",
|
||||
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-scroll/core": "^2.4.2",
|
||||
|
|
@ -21,12 +23,11 @@
|
|||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^0.27.2",
|
||||
"clipboard": "^2.0.10",
|
||||
"codemirror": "^5.65.9",
|
||||
"core-js": "^3.6.5",
|
||||
"dayjs": "^1.11.4",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"echarts": "^5.3.1",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.2.16",
|
||||
"element-plus": "^2.2.28",
|
||||
"exceljs": "^4.3.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"fuse.js": "^6.6.2",
|
||||
|
|
@ -37,9 +38,10 @@
|
|||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.21",
|
||||
"pinia-persistedstate-plugin": "^0.1.0",
|
||||
"pinia-plugin-persistedstate": "^2.1.1",
|
||||
"print-js": "^1.6.0",
|
||||
"raf": "^3.4.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sass": "^1.54.0",
|
||||
"splitpanes": "^3.1.1",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
|
|
@ -49,25 +51,34 @@
|
|||
"vue-fuse": "^4.1.1",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-qr": "^4.0.6",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-splitpane": "^1.0.6",
|
||||
"vue3-text-clamp": "^0.1.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuex": "^4.0.0-0",
|
||||
"vxe-table": "^4.3.5",
|
||||
"xe-utils": "^3.5.6",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.3.0",
|
||||
"@commitlint/config-conventional": "^17.3.0",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.32.0",
|
||||
"@typescript-eslint/parser": "^5.32.0",
|
||||
"@vitejs/plugin-vue": "^3.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"commitizen": "^4.2.5",
|
||||
"consola": "^2.15.3",
|
||||
"cz-git": "^1.3.12",
|
||||
"czg": "^1.3.12",
|
||||
"dart-sass": "^1.25.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"fast-glob": "^3.2.11",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.3.4",
|
||||
"mrm": "^3.0.10",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.6.4",
|
||||
"unplugin-auto-import": "^0.10.3",
|
||||
|
|
@ -79,5 +90,17 @@
|
|||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vue-tsc": "^0.38.4"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{vue,js,ts,jsx,tsx}": [
|
||||
"npm run lint:prettier",
|
||||
"npm run lint:fix",
|
||||
"git add ."
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
|
@ -5,14 +5,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed} from "vue";
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import { computed } from 'vue'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
// 配置element中文
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
const SettingStore = useSettingStore()
|
||||
// 配置全局组件大小
|
||||
const globalComSize = computed(():string=>SettingStore.themeConfig.globalComSize)
|
||||
const globalComSize = computed((): string => SettingStore.themeConfig.globalComSize)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
export const errorCodeType = function (code: string): string {
|
||||
let errMessage = '未知错误'
|
||||
switch (code) {
|
||||
case 400:
|
||||
errMessage = '请求失败!请您稍后重试'
|
||||
break
|
||||
case 401:
|
||||
errMessage = '未授权,请重新登录'
|
||||
break
|
||||
case 403:
|
||||
errMessage = '当前账号无权限访问!'
|
||||
break
|
||||
case 404:
|
||||
errMessage = '你所访问的资源不存在!'
|
||||
break
|
||||
case 405:
|
||||
errMessage = '请求方式错误!请您稍后重试'
|
||||
break
|
||||
case 408:
|
||||
errMessage = '请求超时!请您稍后重试'
|
||||
break
|
||||
case 500:
|
||||
errMessage = '服务器端出错'
|
||||
break
|
||||
case 501:
|
||||
errMessage = '网络未实现'
|
||||
break
|
||||
case 502:
|
||||
errMessage = '网络错误'
|
||||
break
|
||||
case 503:
|
||||
errMessage = '服务不可用'
|
||||
break
|
||||
case 504:
|
||||
errMessage = '网络超时'
|
||||
break
|
||||
case 505:
|
||||
errMessage = 'http版本不支持该请求'
|
||||
break
|
||||
default:
|
||||
errMessage = `其他连接错误 --${code}`
|
||||
}
|
||||
return errMessage
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
export const errorCodeType = function(code:string):string{
|
||||
let errMessage:string = "未知错误"
|
||||
switch (code) {
|
||||
case 400:
|
||||
errMessage = '请求失败!请您稍后重试'
|
||||
break
|
||||
case 401:
|
||||
errMessage = '未授权,请重新登录'
|
||||
break
|
||||
case 403:
|
||||
errMessage = '当前账号无权限访问!'
|
||||
break
|
||||
case 404:
|
||||
errMessage = '你所访问的资源不存在!'
|
||||
break
|
||||
case 405:
|
||||
errMessage = '请求方式错误!请您稍后重试'
|
||||
break
|
||||
case 408:
|
||||
errMessage = '请求超时!请您稍后重试'
|
||||
break
|
||||
case 500:
|
||||
errMessage = '服务器端出错'
|
||||
break
|
||||
case 501:
|
||||
errMessage = '网络未实现'
|
||||
break
|
||||
case 502:
|
||||
errMessage = '网络错误'
|
||||
break
|
||||
case 503:
|
||||
errMessage = '服务不可用'
|
||||
break
|
||||
case 504:
|
||||
errMessage = '网络超时'
|
||||
break
|
||||
case 505:
|
||||
errMessage = 'http版本不支持该请求'
|
||||
break
|
||||
default:
|
||||
errMessage = `其他连接错误 --${code}`
|
||||
}
|
||||
return errMessage
|
||||
}
|
||||
|
|
@ -1,57 +1,62 @@
|
|||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { ElMessage } from "element-plus";
|
||||
import {useUserStore} from "@/store/modules/user"
|
||||
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
// 创建axios实例 进行基本参数配置
|
||||
const service = axios.create({
|
||||
// 默认请求地址,根据环境的不同可在.env 文件中进行修改
|
||||
baseURL: import.meta.env.VUE_APP_BASE_API,
|
||||
// 设置接口访问超时时间
|
||||
timeout: 3000000, // request timeout,
|
||||
// 跨域时候允许携带凭证
|
||||
withCredentials: true
|
||||
// 默认请求地址,根据环境的不同可在.env 文件中进行修改
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
// 设置接口访问超时时间
|
||||
timeout: 3000000, // request timeout,
|
||||
// 跨域时候允许携带凭证
|
||||
withCredentials: true,
|
||||
})
|
||||
|
||||
// request interceptor 接口请求拦截
|
||||
service.interceptors.request.use((config:AxiosRequestConfig)=>{
|
||||
service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
/**
|
||||
* 用户登录之后获取服务端返回的token,后面每次请求都在请求头中带上token进行JWT校验
|
||||
* token 存储在本地储存中(storage)、vuex、pinia
|
||||
*/
|
||||
const userStore = useUserStore();
|
||||
const token: string = userStore.token;
|
||||
const userStore = useUserStore()
|
||||
const token: string = userStore.token
|
||||
// 自定义请求头
|
||||
if(token){ config.headers['Authorization'] = token}
|
||||
if (token) {
|
||||
config.headers['Authorization'] = token
|
||||
}
|
||||
return config
|
||||
},(error: AxiosError) => {
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
// 请求错误,这里可以用全局提示框进行提示
|
||||
return Promise.reject(error);
|
||||
})
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
// response interceptor 接口响应拦截
|
||||
service.interceptors.response.use((response: AxiosResponse) =>{
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
// 直接返回res,当然你也可以只返回res.data
|
||||
// 系统如果有自定义code也可以在这里处理
|
||||
return response
|
||||
|
||||
|
||||
},(error: AxiosError) => {
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
return Promise.reject(error)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* @description 显示错误消息
|
||||
* opt 传入参数
|
||||
* err 错误信息
|
||||
* type 消息类型
|
||||
* duration 消息持续时间
|
||||
*/
|
||||
function showErrMessage (opt, err, type:any= 'error', duration:number = 5000){
|
||||
ElMessage({
|
||||
message: err.msg,
|
||||
type:type,
|
||||
duration: duration
|
||||
})
|
||||
}
|
||||
// /**
|
||||
// * @description 显示错误消息
|
||||
// * opt 传入参数
|
||||
// * err 错误信息
|
||||
// * type 消息类型
|
||||
// * duration 消息持续时间
|
||||
// */
|
||||
// function showErrMessage(opt, err, type: any = 'error', duration = 5000) {
|
||||
// ElMessage({
|
||||
// message: err.msg,
|
||||
// type: type,
|
||||
// duration: duration,
|
||||
// });
|
||||
// }
|
||||
|
||||
export default service
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import request from './request'
|
||||
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/vue-element-perfect/user/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return request({
|
||||
url: '/vue-element-perfect/user/login',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
|
@ -1,539 +0,0 @@
|
|||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
|
@ -1,763 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>iconfont Demo</title>
|
||||
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
|
||||
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967FF, #B500FE);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
|
||||
|
||||
</a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=3641732" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">阴</div>
|
||||
<div class="code-name">&#xe6a0;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">大暴雨</div>
|
||||
<div class="code-name">&#xe6a7;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">刷新</div>
|
||||
<div class="code-name">&#xe627;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">刷新</div>
|
||||
<div class="code-name">&#xe629;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">多云-2-copy</div>
|
||||
<div class="code-name">&#xe68d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">多云-1</div>
|
||||
<div class="code-name">&#xe679;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">大雪</div>
|
||||
<div class="code-name">&#xe67a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">大雨</div>
|
||||
<div class="code-name">&#xe67b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">风</div>
|
||||
<div class="code-name">&#xe67c;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">多云-3</div>
|
||||
<div class="code-name">&#xe67d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">多云-2</div>
|
||||
<div class="code-name">&#xe67e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">雷雨交加</div>
|
||||
<div class="code-name">&#xe67f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">多云</div>
|
||||
<div class="code-name">&#xe680;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">中雨</div>
|
||||
<div class="code-name">&#xe681;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">雾</div>
|
||||
<div class="code-name">&#xe682;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">小雨</div>
|
||||
<div class="code-name">&#xe683;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">小雪</div>
|
||||
<div class="code-name">&#xe684;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">闪电</div>
|
||||
<div class="code-name">&#xe685;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">雪</div>
|
||||
<div class="code-name">&#xe686;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">中雪</div>
|
||||
<div class="code-name">&#xe687;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">扬尘</div>
|
||||
<div class="code-name">&#xe688;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">月亮</div>
|
||||
<div class="code-name">&#xe689;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">雨夹雪</div>
|
||||
<div class="code-name">&#xe68a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">晴</div>
|
||||
<div class="code-name">&#xe68b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">霾</div>
|
||||
<div class="code-name">&#xe68c;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1663216428515') format('woff2'),
|
||||
url('iconfont.woff?t=1663216428515') format('woff'),
|
||||
url('iconfont.ttf?t=1663216428515') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yin"></span>
|
||||
<div class="name">
|
||||
阴
|
||||
</div>
|
||||
<div class="code-name">.icon-yin
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-dabaoyu"></span>
|
||||
<div class="name">
|
||||
大暴雨
|
||||
</div>
|
||||
<div class="code-name">.icon-dabaoyu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shuaxin1"></span>
|
||||
<div class="name">
|
||||
刷新
|
||||
</div>
|
||||
<div class="code-name">.icon-shuaxin1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shuaxin"></span>
|
||||
<div class="name">
|
||||
刷新
|
||||
</div>
|
||||
<div class="code-name">.icon-shuaxin
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-duoyun-2-copy"></span>
|
||||
<div class="name">
|
||||
多云-2-copy
|
||||
</div>
|
||||
<div class="code-name">.icon-duoyun-2-copy
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-duoyun-1"></span>
|
||||
<div class="name">
|
||||
多云-1
|
||||
</div>
|
||||
<div class="code-name">.icon-duoyun-1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-daxue"></span>
|
||||
<div class="name">
|
||||
大雪
|
||||
</div>
|
||||
<div class="code-name">.icon-daxue
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-dayu"></span>
|
||||
<div class="name">
|
||||
大雨
|
||||
</div>
|
||||
<div class="code-name">.icon-dayu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-feng"></span>
|
||||
<div class="name">
|
||||
风
|
||||
</div>
|
||||
<div class="code-name">.icon-feng
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-duoyun-3"></span>
|
||||
<div class="name">
|
||||
多云-3
|
||||
</div>
|
||||
<div class="code-name">.icon-duoyun-3
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-duoyun-2"></span>
|
||||
<div class="name">
|
||||
多云-2
|
||||
</div>
|
||||
<div class="code-name">.icon-duoyun-2
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-leiyujiaojia"></span>
|
||||
<div class="name">
|
||||
雷雨交加
|
||||
</div>
|
||||
<div class="code-name">.icon-leiyujiaojia
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-duoyun"></span>
|
||||
<div class="name">
|
||||
多云
|
||||
</div>
|
||||
<div class="code-name">.icon-duoyun
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zhongyu"></span>
|
||||
<div class="name">
|
||||
中雨
|
||||
</div>
|
||||
<div class="code-name">.icon-zhongyu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-wu"></span>
|
||||
<div class="name">
|
||||
雾
|
||||
</div>
|
||||
<div class="code-name">.icon-wu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiaoyu"></span>
|
||||
<div class="name">
|
||||
小雨
|
||||
</div>
|
||||
<div class="code-name">.icon-xiaoyu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiaoxue"></span>
|
||||
<div class="name">
|
||||
小雪
|
||||
</div>
|
||||
<div class="code-name">.icon-xiaoxue
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shandian"></span>
|
||||
<div class="name">
|
||||
闪电
|
||||
</div>
|
||||
<div class="code-name">.icon-shandian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xue"></span>
|
||||
<div class="name">
|
||||
雪
|
||||
</div>
|
||||
<div class="code-name">.icon-xue
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zhongxue"></span>
|
||||
<div class="name">
|
||||
中雪
|
||||
</div>
|
||||
<div class="code-name">.icon-zhongxue
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yangchen"></span>
|
||||
<div class="name">
|
||||
扬尘
|
||||
</div>
|
||||
<div class="code-name">.icon-yangchen
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yueliang"></span>
|
||||
<div class="name">
|
||||
月亮
|
||||
</div>
|
||||
<div class="code-name">.icon-yueliang
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yujiaxue"></span>
|
||||
<div class="name">
|
||||
雨夹雪
|
||||
</div>
|
||||
<div class="code-name">.icon-yujiaxue
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qing"></span>
|
||||
<div class="name">
|
||||
晴
|
||||
</div>
|
||||
<div class="code-name">.icon-qing
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-mai"></span>
|
||||
<div class="name">
|
||||
霾
|
||||
</div>
|
||||
<div class="code-name">.icon-mai
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yin"></use>
|
||||
</svg>
|
||||
<div class="name">阴</div>
|
||||
<div class="code-name">#icon-yin</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-dabaoyu"></use>
|
||||
</svg>
|
||||
<div class="name">大暴雨</div>
|
||||
<div class="code-name">#icon-dabaoyu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shuaxin1"></use>
|
||||
</svg>
|
||||
<div class="name">刷新</div>
|
||||
<div class="code-name">#icon-shuaxin1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shuaxin"></use>
|
||||
</svg>
|
||||
<div class="name">刷新</div>
|
||||
<div class="code-name">#icon-shuaxin</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-duoyun-2-copy"></use>
|
||||
</svg>
|
||||
<div class="name">多云-2-copy</div>
|
||||
<div class="code-name">#icon-duoyun-2-copy</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-duoyun-1"></use>
|
||||
</svg>
|
||||
<div class="name">多云-1</div>
|
||||
<div class="code-name">#icon-duoyun-1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-daxue"></use>
|
||||
</svg>
|
||||
<div class="name">大雪</div>
|
||||
<div class="code-name">#icon-daxue</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-dayu"></use>
|
||||
</svg>
|
||||
<div class="name">大雨</div>
|
||||
<div class="code-name">#icon-dayu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-feng"></use>
|
||||
</svg>
|
||||
<div class="name">风</div>
|
||||
<div class="code-name">#icon-feng</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-duoyun-3"></use>
|
||||
</svg>
|
||||
<div class="name">多云-3</div>
|
||||
<div class="code-name">#icon-duoyun-3</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-duoyun-2"></use>
|
||||
</svg>
|
||||
<div class="name">多云-2</div>
|
||||
<div class="code-name">#icon-duoyun-2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-leiyujiaojia"></use>
|
||||
</svg>
|
||||
<div class="name">雷雨交加</div>
|
||||
<div class="code-name">#icon-leiyujiaojia</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-duoyun"></use>
|
||||
</svg>
|
||||
<div class="name">多云</div>
|
||||
<div class="code-name">#icon-duoyun</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zhongyu"></use>
|
||||
</svg>
|
||||
<div class="name">中雨</div>
|
||||
<div class="code-name">#icon-zhongyu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-wu"></use>
|
||||
</svg>
|
||||
<div class="name">雾</div>
|
||||
<div class="code-name">#icon-wu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaoyu"></use>
|
||||
</svg>
|
||||
<div class="name">小雨</div>
|
||||
<div class="code-name">#icon-xiaoyu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaoxue"></use>
|
||||
</svg>
|
||||
<div class="name">小雪</div>
|
||||
<div class="code-name">#icon-xiaoxue</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shandian"></use>
|
||||
</svg>
|
||||
<div class="name">闪电</div>
|
||||
<div class="code-name">#icon-shandian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xue"></use>
|
||||
</svg>
|
||||
<div class="name">雪</div>
|
||||
<div class="code-name">#icon-xue</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zhongxue"></use>
|
||||
</svg>
|
||||
<div class="name">中雪</div>
|
||||
<div class="code-name">#icon-zhongxue</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yangchen"></use>
|
||||
</svg>
|
||||
<div class="name">扬尘</div>
|
||||
<div class="code-name">#icon-yangchen</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yueliang"></use>
|
||||
</svg>
|
||||
<div class="name">月亮</div>
|
||||
<div class="code-name">#icon-yueliang</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yujiaxue"></use>
|
||||
</svg>
|
||||
<div class="name">雨夹雪</div>
|
||||
<div class="code-name">#icon-yujiaxue</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qing"></use>
|
||||
</svg>
|
||||
<div class="name">晴</div>
|
||||
<div class="code-name">#icon-qing</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-mai"></use>
|
||||
</svg>
|
||||
<div class="name">霾</div>
|
||||
<div class="code-name">#icon-mai</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3641732 */
|
||||
src: url('./iconfont.woff2?t=1663216428515') format('woff2'),
|
||||
url('./iconfont.woff?t=1663216428515') format('woff'),
|
||||
url('./iconfont.ttf?t=1663216428515') format('truetype');
|
||||
font-family: 'iconfont'; /* Project id 3641732 */
|
||||
src: url('./iconfont.woff2?t=1663216428515') format('woff2'), url('./iconfont.woff?t=1663216428515') format('woff'),
|
||||
url('./iconfont.ttf?t=1663216428515') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-family: 'iconfont' !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
|
@ -14,102 +13,101 @@
|
|||
}
|
||||
|
||||
.icon-yin:before {
|
||||
content: "\e6a0";
|
||||
content: '\e6a0';
|
||||
}
|
||||
|
||||
.icon-dabaoyu:before {
|
||||
content: "\e6a7";
|
||||
content: '\e6a7';
|
||||
}
|
||||
|
||||
.icon-shuaxin1:before {
|
||||
content: "\e627";
|
||||
content: '\e627';
|
||||
}
|
||||
|
||||
.icon-shuaxin:before {
|
||||
content: "\e629";
|
||||
content: '\e629';
|
||||
}
|
||||
|
||||
.icon-duoyun-2-copy:before {
|
||||
content: "\e68d";
|
||||
content: '\e68d';
|
||||
}
|
||||
|
||||
.icon-duoyun-1:before {
|
||||
content: "\e679";
|
||||
content: '\e679';
|
||||
}
|
||||
|
||||
.icon-daxue:before {
|
||||
content: "\e67a";
|
||||
content: '\e67a';
|
||||
}
|
||||
|
||||
.icon-dayu:before {
|
||||
content: "\e67b";
|
||||
content: '\e67b';
|
||||
}
|
||||
|
||||
.icon-feng:before {
|
||||
content: "\e67c";
|
||||
content: '\e67c';
|
||||
}
|
||||
|
||||
.icon-duoyun-3:before {
|
||||
content: "\e67d";
|
||||
content: '\e67d';
|
||||
}
|
||||
|
||||
.icon-duoyun-2:before {
|
||||
content: "\e67e";
|
||||
content: '\e67e';
|
||||
}
|
||||
|
||||
.icon-leiyujiaojia:before {
|
||||
content: "\e67f";
|
||||
content: '\e67f';
|
||||
}
|
||||
|
||||
.icon-duoyun:before {
|
||||
content: "\e680";
|
||||
content: '\e680';
|
||||
}
|
||||
|
||||
.icon-zhongyu:before {
|
||||
content: "\e681";
|
||||
content: '\e681';
|
||||
}
|
||||
|
||||
.icon-wu:before {
|
||||
content: "\e682";
|
||||
content: '\e682';
|
||||
}
|
||||
|
||||
.icon-xiaoyu:before {
|
||||
content: "\e683";
|
||||
content: '\e683';
|
||||
}
|
||||
|
||||
.icon-xiaoxue:before {
|
||||
content: "\e684";
|
||||
content: '\e684';
|
||||
}
|
||||
|
||||
.icon-shandian:before {
|
||||
content: "\e685";
|
||||
content: '\e685';
|
||||
}
|
||||
|
||||
.icon-xue:before {
|
||||
content: "\e686";
|
||||
content: '\e686';
|
||||
}
|
||||
|
||||
.icon-zhongxue:before {
|
||||
content: "\e687";
|
||||
content: '\e687';
|
||||
}
|
||||
|
||||
.icon-yangchen:before {
|
||||
content: "\e688";
|
||||
content: '\e688';
|
||||
}
|
||||
|
||||
.icon-yueliang:before {
|
||||
content: "\e689";
|
||||
content: '\e689';
|
||||
}
|
||||
|
||||
.icon-yujiaxue:before {
|
||||
content: "\e68a";
|
||||
content: '\e68a';
|
||||
}
|
||||
|
||||
.icon-qing:before {
|
||||
content: "\e68b";
|
||||
content: '\e68b';
|
||||
}
|
||||
|
||||
.icon-mai:before {
|
||||
content: "\e68c";
|
||||
content: '\e68c';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 653 B |
|
After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 496 B |
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="avatarUrl"
|
||||
:output-size="defaultOptions.outputSize"
|
||||
:output-type="defaultOptions.outputType"
|
||||
:info="defaultOptions.info"
|
||||
:full="defaultOptions.full"
|
||||
:fixed="defaultOptions.fixed"
|
||||
:auto-crop-width="defaultOptions.autoCropWidth"
|
||||
:auto-crop-height="defaultOptions.autoCropHeight"
|
||||
:fixed-box="defaultOptions.fixedBox"
|
||||
:auto-crop="defaultOptions.autoCrop"
|
||||
:center-box="defaultOptions.centerBox"
|
||||
@real-time="realTime"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineExpose, reactive, ref } from 'vue'
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
import 'vue-cropper/dist/index.css'
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
let props = defineProps({
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
|
||||
let cropper = ref()
|
||||
|
||||
const defaultOptions = reactive({
|
||||
outputSize: 0.8, // 裁剪生成图片的质量
|
||||
outputType: 'png', // 生成图片的格式
|
||||
info: true, // 裁剪框的大小信息
|
||||
fixed: true, // 是否开启截图框宽高固定比例
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
anMoveBox: true, // 截图框能否拖动
|
||||
original: false, // 上传图片按照原始比例渲染
|
||||
autoCropWidth: 300, // 默认生成截图框宽度
|
||||
autoCropHeight: 300, // 默认生成截图框高度
|
||||
// 只有自动截图开启 宽度高度才生效
|
||||
centerBox: false, // 截图框是否被限制在图片里面
|
||||
high: true, // 是否按照dpr设备比例图片
|
||||
fixedBox: false, // 固定截图框大小 不允许改变
|
||||
full: false, // 是否输出原图比例的截图
|
||||
...props.options,
|
||||
})
|
||||
|
||||
const getBase64 = () => {
|
||||
return new Promise((resolve) => {
|
||||
cropper.value.getCropData((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const rotateLeft = () => {
|
||||
cropper.value.rotateLeft()
|
||||
}
|
||||
const rotateRight = () => {
|
||||
cropper.value.rotateRight()
|
||||
}
|
||||
|
||||
const zoom = (percent) => {
|
||||
cropper.value.changeScale(percent)
|
||||
}
|
||||
|
||||
// 实时预览图片
|
||||
const realTime = (data) => {
|
||||
emit('change', data)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getBase64,
|
||||
rotateLeft,
|
||||
rotateRight,
|
||||
zoom,
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
<template>
|
||||
<div :id="id" :class="className" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import echarts from 'echarts'
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById(this.id))
|
||||
|
||||
const xAxisData = []
|
||||
const data = []
|
||||
const data2 = []
|
||||
for (let i = 0; i < 50; i++) {
|
||||
xAxisData.push(i)
|
||||
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)
|
||||
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)
|
||||
}
|
||||
this.chart.setOption({
|
||||
backgroundColor: '#08263a',
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
data: xAxisData,
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
data: xAxisData,
|
||||
},
|
||||
],
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055'],
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#4a657a',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#08263f',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'back',
|
||||
type: 'bar',
|
||||
data: data2,
|
||||
z: 1,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0.4,
|
||||
barBorderRadius: 5,
|
||||
shadowBlur: 3,
|
||||
shadowColor: '#111',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Simulate Shadow',
|
||||
type: 'line',
|
||||
data,
|
||||
z: 2,
|
||||
showSymbol: false,
|
||||
animationDelay: 0,
|
||||
animationEasing: 'linear',
|
||||
animationDuration: 1200,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: 'transparent',
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: '#08263a',
|
||||
shadowBlur: 50,
|
||||
shadowColor: '#000',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'front',
|
||||
type: 'bar',
|
||||
data,
|
||||
xAxisIndex: 1,
|
||||
z: 3,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
barBorderRadius: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
animationEasing: 'elasticOut',
|
||||
animationEasingUpdate: 'elasticOut',
|
||||
animationDelay(idx) {
|
||||
return idx * 20
|
||||
},
|
||||
animationDelayUpdate(idx) {
|
||||
return idx * 20
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
<template>
|
||||
<div :id="id" :class="className" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as echarts from 'echarts'
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
let props = defineProps({
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
})
|
||||
const isCollapse = computed(() => {
|
||||
return store.state.app.isCollapse
|
||||
})
|
||||
|
||||
let chart: EChartsType
|
||||
|
||||
const initChart = () => {
|
||||
let chart = echarts.init(document.getElementById(props.id))
|
||||
chart.setOption({
|
||||
backgroundColor: '#394056',
|
||||
title: {
|
||||
top: 20,
|
||||
text: 'Requests',
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
fontSize: 16,
|
||||
color: '#F1F1F3',
|
||||
},
|
||||
left: '1%',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#57617B',
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
top: 20,
|
||||
icon: 'rect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 5,
|
||||
itemGap: 13,
|
||||
data: ['CMCC', 'CTCC', 'CUCC'],
|
||||
right: '4%',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#F1F1F3',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 100,
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B',
|
||||
},
|
||||
},
|
||||
data: [
|
||||
'13:00',
|
||||
'13:05',
|
||||
'13:10',
|
||||
'13:15',
|
||||
'13:20',
|
||||
'13:25',
|
||||
'13:30',
|
||||
'13:35',
|
||||
'13:40',
|
||||
'13:45',
|
||||
'13:50',
|
||||
'13:55',
|
||||
],
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '(%)',
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 10,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'CMCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(137, 189, 27, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: 'rgba(137, 189, 27, 0)',
|
||||
},
|
||||
],
|
||||
false,
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(137,189,27)',
|
||||
borderColor: 'rgba(137,189,2,0.27)',
|
||||
borderWidth: 12,
|
||||
},
|
||||
},
|
||||
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122],
|
||||
},
|
||||
{
|
||||
name: 'CTCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 136, 212, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: 'rgba(0, 136, 212, 0)',
|
||||
},
|
||||
],
|
||||
false,
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(0,136,212)',
|
||||
borderColor: 'rgba(0,136,212,0.2)',
|
||||
borderWidth: 12,
|
||||
},
|
||||
},
|
||||
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150],
|
||||
},
|
||||
{
|
||||
name: 'CUCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(219, 50, 51, 0.3)',
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: 'rgba(219, 50, 51, 0)',
|
||||
},
|
||||
],
|
||||
false,
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(219,50,51)',
|
||||
borderColor: 'rgba(219,50,51,0.2)',
|
||||
borderWidth: 12,
|
||||
},
|
||||
},
|
||||
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122],
|
||||
},
|
||||
],
|
||||
})
|
||||
return chart
|
||||
}
|
||||
|
||||
watch(isCollapse, () => {
|
||||
setTimeout(() => {
|
||||
chart && chart.resize()
|
||||
}, 300)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
chart && chart.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
<template>
|
||||
<div :id="id" :class="className" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import echarts from 'echarts'
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById(this.id))
|
||||
const xData = (function () {
|
||||
const data = []
|
||||
for (let i = 1; i < 13; i++) {
|
||||
data.push(i + 'month')
|
||||
}
|
||||
return data
|
||||
})()
|
||||
this.chart.setOption({
|
||||
backgroundColor: '#344b58',
|
||||
title: {
|
||||
text: 'statistics',
|
||||
x: '20',
|
||||
top: '20',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: '22',
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#90979c',
|
||||
fontSize: '16',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
borderWidth: 0,
|
||||
top: 150,
|
||||
bottom: 95,
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
x: '5%',
|
||||
top: '10%',
|
||||
textStyle: {
|
||||
color: '#90979c',
|
||||
},
|
||||
data: ['female', 'male', 'average'],
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#90979c',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
},
|
||||
data: xData,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#90979c',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
},
|
||||
splitArea: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
show: true,
|
||||
height: 30,
|
||||
xAxisIndex: [0],
|
||||
bottom: 30,
|
||||
start: 10,
|
||||
end: 80,
|
||||
handleIcon:
|
||||
'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
|
||||
handleSize: '110%',
|
||||
handleStyle: {
|
||||
color: '#d3dee5',
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
borderColor: '#90979c',
|
||||
},
|
||||
{
|
||||
type: 'inside',
|
||||
show: true,
|
||||
height: 15,
|
||||
start: 1,
|
||||
end: 35,
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'female',
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
barMaxWidth: 35,
|
||||
barGap: '10%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(255,144,128,1)',
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
position: 'insideTop',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [709, 1917, 2455, 2610, 1719, 1433, 1544, 3285, 5208, 3372, 2484, 4078],
|
||||
},
|
||||
|
||||
{
|
||||
name: 'male',
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(0,191,183,1)',
|
||||
barBorderRadius: 0,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [327, 1776, 507, 1200, 800, 482, 204, 1390, 1001, 951, 381, 220],
|
||||
},
|
||||
{
|
||||
name: 'average',
|
||||
type: 'line',
|
||||
stack: 'total',
|
||||
symbolSize: 10,
|
||||
symbol: 'circle',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgba(252,230,48,1)',
|
||||
barBorderRadius: 0,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter(p) {
|
||||
return p.value > 0 ? p.value : ''
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [1036, 3693, 2962, 3810, 2519, 1915, 1748, 4675, 6209, 4323, 2865, 4298],
|
||||
},
|
||||
],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<textarea ref="codeEditor" placeholder="请输入..."></textarea>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onBeforeUnmount, onMounted, ref, toRefs, watch } from 'vue'
|
||||
// codemirror基础资源引入
|
||||
import _CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// language
|
||||
import 'codemirror/mode/javascript/javascript.js'
|
||||
|
||||
// theme css
|
||||
import 'codemirror/theme/monokai.css'
|
||||
// 折叠资源引入:开始
|
||||
import 'codemirror/addon/fold/foldgutter.css'
|
||||
import 'codemirror/addon/fold/foldcode.js'
|
||||
import 'codemirror/addon/fold/brace-fold.js'
|
||||
import 'codemirror/addon/fold/comment-fold.js'
|
||||
import 'codemirror/addon/fold/indent-fold.js'
|
||||
import 'codemirror/addon/fold/foldgutter.js'
|
||||
// 折叠资源引入:结束
|
||||
|
||||
// 搜索资源引入:开始
|
||||
import 'codemirror/addon/scroll/annotatescrollbar.js'
|
||||
import 'codemirror/addon/search/matchesonscrollbar.js'
|
||||
import 'codemirror/addon/search/match-highlighter.js'
|
||||
import 'codemirror/addon/search/jump-to-line.js'
|
||||
|
||||
import 'codemirror/addon/dialog/dialog.js'
|
||||
import 'codemirror/addon/dialog/dialog.css'
|
||||
import 'codemirror/addon/search/searchcursor.js'
|
||||
import 'codemirror/addon/search/search.js'
|
||||
// 搜索资源引入:结束
|
||||
|
||||
// 启用placeholder
|
||||
import 'codemirror/addon/display/placeholder.js'
|
||||
|
||||
import 'codemirror/addon/selection/active-line.js' //光标行背景高亮,配置里面也需要styleActiveLine设置为true
|
||||
|
||||
const CodeMirror = window.CodeMirror || _CodeMirror
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: String,
|
||||
defaultValue: String,
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { modelValue, defaultValue, readOnly } = toRefs(props)
|
||||
const codeEditor = ref()
|
||||
let editor
|
||||
watch(modelValue, () => {
|
||||
if (null != editor && modelValue.value && modelValue.value !== editor.getValue()) {
|
||||
// 触发v-model的双向绑定
|
||||
editor.setValue(modelValue.value)
|
||||
}
|
||||
})
|
||||
watch(readOnly, () => {
|
||||
if (null != editor) {
|
||||
editor.setOption('readOnly', readOnly.value)
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
editor = CodeMirror.fromTextArea(codeEditor.value, {
|
||||
value: modelValue.value,
|
||||
// mime: "text/javascript",
|
||||
mode: 'application/json',
|
||||
indentWithTabs: false, // 在缩进时,是否需要把 n*tab宽度个空格替换成n个tab字符,默认为false
|
||||
smartIndent: true, // 自动缩进,设置是否根据上下文自动缩进(和上一行相同的缩进量)。默认为true
|
||||
lineNumbers: true, // 是否在编辑器左侧显示行号
|
||||
matchBrackets: true, // 括号匹配
|
||||
readOnly: readOnly.value,
|
||||
// 启用代码折叠相关功能:开始
|
||||
foldGutter: true,
|
||||
lineWrapping: true, //是否自动换行
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
|
||||
// 启用代码折叠相关功能:结束
|
||||
styleActiveLine: false, // 光标行高亮
|
||||
})
|
||||
// 监听编辑器的change事件
|
||||
editor.on('change', () => {
|
||||
// 触发v-model的双向绑定
|
||||
context.emit('update:modelValue', editor.getValue())
|
||||
})
|
||||
if (defaultValue.value) {
|
||||
editor.setValue(defaultValue.value)
|
||||
}
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
if (null !== editor) {
|
||||
editor.toTextArea()
|
||||
editor = null
|
||||
}
|
||||
})
|
||||
return { codeEditor }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style lang="scss">
|
||||
.CodeMirror-wrap {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
min-height: 100%;
|
||||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -103,6 +103,9 @@
|
|||
}
|
||||
this.$emit('mountedCallback')
|
||||
},
|
||||
unmounted() {
|
||||
cancelAnimationFrame(this.rAF)
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
this.localStartVal = this.startVal
|
||||
|
|
@ -142,26 +145,15 @@
|
|||
|
||||
if (this.useEasing) {
|
||||
if (this.countDown) {
|
||||
this.printVal =
|
||||
this.localStartVal -
|
||||
this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration)
|
||||
this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration)
|
||||
} else {
|
||||
this.printVal = this.easingFn(
|
||||
progress,
|
||||
this.localStartVal,
|
||||
this.endVal - this.localStartVal,
|
||||
this.localDuration,
|
||||
)
|
||||
this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration)
|
||||
}
|
||||
} else {
|
||||
if (this.countDown) {
|
||||
this.printVal =
|
||||
this.localStartVal -
|
||||
(this.localStartVal - this.endVal) * (progress / this.localDuration)
|
||||
this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration)
|
||||
} else {
|
||||
this.printVal =
|
||||
this.localStartVal +
|
||||
(this.endVal - this.localStartVal) * (progress / this.localDuration)
|
||||
this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration)
|
||||
}
|
||||
}
|
||||
if (this.countDown) {
|
||||
|
|
@ -195,8 +187,5 @@
|
|||
return this.prefix + x1 + x2 + this.suffix
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
cancelAnimationFrame(this.rAF)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,7 @@ if (isServer) {
|
|||
}
|
||||
prefix = prefixes[i]
|
||||
requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']
|
||||
cancelAnimationFrame =
|
||||
cancelAnimationFrame ||
|
||||
window[prefix + 'CancelAnimationFrame'] ||
|
||||
window[prefix + 'CancelRequestAnimationFrame']
|
||||
cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']
|
||||
}
|
||||
|
||||
// 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
|
||||
|
|
|
|||
|
|
@ -0,0 +1,269 @@
|
|||
<template>
|
||||
<div ref="chartsRef" class="echarts" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as echarts from 'echarts'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
import { onMounted, ref } from 'vue'
|
||||
const chartsRef = ref<HTMLElement | null>()
|
||||
|
||||
let colorList = ['#46ea91', '#2ba0ff', '#ed593b', '#7357ff', '#f2d750']
|
||||
const options = {
|
||||
legend: {
|
||||
icon: 'circle',
|
||||
top: '5%',
|
||||
right: '5%',
|
||||
itemWidth: 6,
|
||||
itemGap: 5,
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
padding: [3, 0, 0, 0],
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
grid: {
|
||||
top: '14%',
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '10%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['1', '2', '3', '4', '5', '6', '7', '8'],
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#33BBFF',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
textStyle: {
|
||||
color: '#5FBBEB',
|
||||
},
|
||||
// 默认x轴字体大小
|
||||
fontSize: 12,
|
||||
// margin:文字到x轴的距离
|
||||
margin: 10,
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
// padding: [11, 5, 7],
|
||||
padding: [0, 0, 0, 0],
|
||||
// 这里的margin和axisLabel的margin要一致!
|
||||
margin: 10,
|
||||
// 移入时的字体大小
|
||||
fontSize: 12,
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
},
|
||||
},
|
||||
boundaryGap: false,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
name: '单位/件',
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#05D5FF',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#5FBBEB',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '咨询',
|
||||
type: 'line',
|
||||
data: [100, 20, 30, 102, 15, 30, 20, 18],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#90ffc6',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#46ea91',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(144, 255, 198, .3)',
|
||||
shadowBlur: 5,
|
||||
shadowOffsetY: 5,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorList[0],
|
||||
borderColor: colorList[0],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '求助',
|
||||
type: 'line',
|
||||
data: [20, 12, 11, 14, 25, 16, 10, 20],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#67bcfc',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#2ba0ff',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(105, 188, 252,.3)',
|
||||
shadowBlur: 5,
|
||||
shadowOffsetY: 5,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorList[1],
|
||||
borderColor: colorList[1],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '无效',
|
||||
type: 'line',
|
||||
data: [150, 120, 170, 140, 100, 160, 110, 110],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#fc937e ',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#ed593b',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgb(252, 147, 126,.3)',
|
||||
shadowBlur: 2,
|
||||
shadowOffsetY: 2,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorList[2],
|
||||
borderColor: colorList[2],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '投诉举报',
|
||||
type: 'line',
|
||||
data: [200, 80, 100, 30, 60, 50, 110, 20],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#a390ff',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#7357ff',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(115, 87, 255, .1)',
|
||||
shadowBlur: 5,
|
||||
shadowOffsetY: 5,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorList[3],
|
||||
borderColor: colorList[3],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '建议',
|
||||
type: 'line',
|
||||
data: [20, 80, 150, 30, 60, 50, 50, 20],
|
||||
symbolSize: 1,
|
||||
symbol: 'circle',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#ffeb86',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#f2d750',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(255, 235, 134, .5)',
|
||||
shadowBlur: 5,
|
||||
shadowOffsetY: 5,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorList[4],
|
||||
borderColor: colorList[4],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let chart: EChartsType
|
||||
const initChart = () => {
|
||||
const chart = echarts.init(chartsRef.value)
|
||||
chart.setOption(options)
|
||||
return chart
|
||||
}
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
chart && chart.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<div class="item-charts">
|
||||
<bar-charts width="100%" height="100%" :config="lineConfig" />
|
||||
</div>
|
||||
<div ref="chartsRef" class="echarts" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import BarCharts from './components/bar.vue'
|
||||
import { reactive } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
import { onMounted, ref, reactive } from 'vue'
|
||||
const chartsRef = ref<HTMLElement | null>()
|
||||
const data = [154, 230, 224, 218, 135, 147, 260]
|
||||
const color = ['#fa796f', '#54c1fb', '#ca6cd4', '#59dcc1', '#09a4ea', '#e98f4d', '#ea8e49']
|
||||
const dataOptions = []
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
dataOptions.push(obj)
|
||||
})
|
||||
|
||||
const lineConfig = reactive({
|
||||
const options = {
|
||||
color,
|
||||
grid: {
|
||||
top: '10%',
|
||||
|
|
@ -44,6 +45,7 @@
|
|||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
// 设置坐标轴的 文字样式
|
||||
axisLabel: {
|
||||
color: '#bbdaff',
|
||||
|
|
@ -61,6 +63,7 @@
|
|||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
|
|
@ -70,14 +73,12 @@
|
|||
color: '#2d5baf',
|
||||
},
|
||||
},
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
axisLabel: {
|
||||
// 设置坐标轴的 文字样式
|
||||
color: '#bbdaff',
|
||||
margin: 20, // 刻度标签与轴线之间的距离。
|
||||
},
|
||||
|
||||
axisTick: {
|
||||
// 取消坐标轴刻度线
|
||||
show: false,
|
||||
|
|
@ -93,10 +94,23 @@
|
|||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let chart: EChartsType
|
||||
const initChart = () => {
|
||||
const chart = echarts.init(chartsRef.value)
|
||||
chart.setOption(options)
|
||||
return chart
|
||||
}
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
chart && chart.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.item-charts {
|
||||
.echarts {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
<template>
|
||||
<div class="item-charts">
|
||||
<line-charts width="100%" height="100%" :config="lineConfig" />
|
||||
</div>
|
||||
<div ref="chartsRef" class="echarts" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import LineCharts from './components/line.vue'
|
||||
import { reactive } from 'vue'
|
||||
import BarCharts from './components/bar.vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
import { onMounted, ref, reactive } from 'vue'
|
||||
const chartsRef = ref<HTMLElement | null>()
|
||||
|
||||
const lineConfig = reactive({
|
||||
const options = {
|
||||
grid: {
|
||||
top: '10%',
|
||||
left: '3%',
|
||||
|
|
@ -87,10 +88,23 @@
|
|||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let chart: EChartsType
|
||||
const initChart = () => {
|
||||
const chart = echarts.init(chartsRef.value)
|
||||
chart.setOption(options)
|
||||
return chart
|
||||
}
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
chart && chart.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.item-charts {
|
||||
.echarts {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -5,33 +5,33 @@ export const cityIconData = [
|
|||
value: [88.999903, 43.607078],
|
||||
},
|
||||
{
|
||||
adcode: 150000,
|
||||
// adcode: 150000,
|
||||
name: '内蒙古自治区',
|
||||
value: [119.24787, 42.205741],
|
||||
},
|
||||
,
|
||||
{
|
||||
adcode: 150000,
|
||||
// adcode: 150000,
|
||||
name: '内蒙古自治区',
|
||||
value: [110.627161, 41.16628],
|
||||
},
|
||||
{
|
||||
adcode: 540000,
|
||||
// adcode: 540000,
|
||||
name: '西藏自治区',
|
||||
value: [89.483714, 30.251951],
|
||||
},
|
||||
{
|
||||
adcode: 630000,
|
||||
// adcode: 630000,
|
||||
name: '青海省',
|
||||
value: [102.064693, 37.084008],
|
||||
},
|
||||
{
|
||||
adcode: 530000,
|
||||
// adcode: 530000,
|
||||
name: '云南省',
|
||||
value: [102.187665, 25.083688],
|
||||
},
|
||||
{
|
||||
adcode: 610000,
|
||||
// adcode: 610000,
|
||||
name: '陕西省',
|
||||
value: [109.20663, 35.018625],
|
||||
},
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
<div :id="id" :class="className" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { geoJson } from './get.js'
|
||||
import { geoJson } from './map.js'
|
||||
import * as echarts from 'echarts'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
import { onMounted } from 'vue'
|
||||
import { cityIconData } from '../js/data.js'
|
||||
import logo from '@/assets/logo.png'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { cityIconData } from './data.js'
|
||||
import logo from '@/assets/image/logo.png'
|
||||
const props = defineProps({
|
||||
className: {
|
||||
type: String,
|
||||
|
|
@ -318,6 +318,11 @@
|
|||
chart.setOption(options)
|
||||
return chart
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
chart && chart.dispose()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div ref="chartsRef" class="echarts" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as echarts from 'echarts'
|
||||
import { EChartsType } from 'echarts/core'
|
||||
import { onMounted, ref } from 'vue'
|
||||
const chartsRef = ref<HTMLElement | null>()
|
||||
var trafficWay = [
|
||||
{
|
||||
name: 'Ⅰ类',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
name: 'Ⅱ类',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
name: 'Ⅲ类',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
name: 'Ⅳ类',
|
||||
value: 20,
|
||||
},
|
||||
{ name: 'Ⅴ类', value: 20 },
|
||||
{ name: '劣Ⅴ类', value: 20 },
|
||||
]
|
||||
|
||||
var data = []
|
||||
var color = ['#fd566a', '#9787ff', '#fdb36a', '#fdd56a', '#6da7ff', '#63e1f2', '#ff3000']
|
||||
for (var i = 0; i < trafficWay.length; i++) {
|
||||
data.push(
|
||||
{
|
||||
value: trafficWay[i].value,
|
||||
name: trafficWay[i].name,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
borderWidth: 5,
|
||||
shadowBlur: 20,
|
||||
borderColor: color[i],
|
||||
shadowColor: color[i],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: '',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
color: 'rgba(0, 0, 0, 0)',
|
||||
borderColor: 'rgba(0, 0, 0, 0)',
|
||||
borderWidth: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
var seriesOption = [
|
||||
{
|
||||
name: '',
|
||||
type: 'pie',
|
||||
clockWise: false,
|
||||
radius: [105, 109],
|
||||
hoverAnimation: false,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
label: {
|
||||
show: true,
|
||||
position: 'outside',
|
||||
|
||||
formatter: function (params) {
|
||||
var percent = 0
|
||||
var total = 0
|
||||
for (var i = 0; i < trafficWay.length; i++) {
|
||||
total += trafficWay[i].value
|
||||
}
|
||||
percent = ((params.value / total) * 100).toFixed(0)
|
||||
if (params.name !== '') {
|
||||
return params.name + '\t' + percent + '%'
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
length: 10,
|
||||
length2: 20,
|
||||
show: true,
|
||||
color: '#00ffff',
|
||||
},
|
||||
},
|
||||
},
|
||||
data: data,
|
||||
},
|
||||
]
|
||||
let options = {
|
||||
color: color,
|
||||
title: [
|
||||
{
|
||||
text: '水质监测',
|
||||
top: '44%',
|
||||
textAlign: 'center',
|
||||
left: '49.50%',
|
||||
backgroundColor: '#163253',
|
||||
borderRadius: 100,
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 20,
|
||||
fontWeight: '400',
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '水环境监测站',
|
||||
top: '49%',
|
||||
textAlign: 'center',
|
||||
left: '49.50%',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 20,
|
||||
fontWeight: '400',
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '9',
|
||||
top: '53%',
|
||||
textAlign: 'center',
|
||||
left: '48%',
|
||||
textStyle: {
|
||||
color: '#f6ea2f',
|
||||
fontSize: 25,
|
||||
fontWeight: '800',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '个',
|
||||
top: '53.5%',
|
||||
textAlign: 'center',
|
||||
left: '50.5%',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 16,
|
||||
fontWeight: '400',
|
||||
},
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
series: seriesOption,
|
||||
}
|
||||
|
||||
let chart: EChartsType
|
||||
const initChart = () => {
|
||||
const chart = echarts.init(chartsRef.value)
|
||||
chart.setOption(options)
|
||||
return chart
|
||||
}
|
||||
onMounted(() => {
|
||||
chart = initChart()
|
||||
window.addEventListener('resize', function () {
|
||||
chart && chart.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
.m-container-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 10px 12px;
|
||||
box-sizing: border-box;
|
||||
.m-container-layout-inner {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
background: white;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div class="m-container-layout">
|
||||
<div class="m-container-layout-inner">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
<template>
|
||||
<div class="g-right-click-menu" :style="style" v-if="isShow" ref="rightMenu">
|
||||
<div
|
||||
v-for="(item, index) in data"
|
||||
:key="index"
|
||||
class="operating"
|
||||
@click.stop="operatingRightAction($event, item)"
|
||||
>{{ item.label }}</div
|
||||
>
|
||||
<div v-if="isShow" ref="rightMenu" class="g-right-click-menu" :style="style">
|
||||
<div v-for="(item, index) in data" :key="index" class="operating" @click.stop="operatingRightAction($event, item)">{{
|
||||
item.label
|
||||
}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
|
@ -37,16 +33,10 @@
|
|||
default: true,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
left: {
|
||||
handler(newName, oldName) {
|
||||
if (newName) {
|
||||
this.isShow = true
|
||||
}
|
||||
},
|
||||
// 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
|
||||
// immediate: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
|
|
@ -59,19 +49,15 @@
|
|||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @func 点击操作
|
||||
* @param val 1、置顶/取消置顶 2、开启/关闭免打扰 3、开启/关闭星标 4、删除会话
|
||||
*/
|
||||
operatingRightAction($event, val) {
|
||||
this.$emit('ok', $event, val)
|
||||
this.isShow = false
|
||||
watch: {
|
||||
left: {
|
||||
handler(newName, oldName) {
|
||||
if (newName) {
|
||||
this.isShow = true
|
||||
}
|
||||
},
|
||||
// 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
|
||||
// immediate: true
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -85,6 +71,16 @@
|
|||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @func 点击操作
|
||||
* @param val 1、置顶/取消置顶 2、开启/关闭免打扰 3、开启/关闭星标 4、删除会话
|
||||
*/
|
||||
operatingRightAction($event, val) {
|
||||
this.$emit('ok', $event, val)
|
||||
this.isShow = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
<template>
|
||||
<div class="advancedForm">
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:inline="true"
|
||||
:label-position="'right'"
|
||||
:model="formInline"
|
||||
class="form-inline">
|
||||
<el-row
|
||||
:class="{
|
||||
'not-show':byHeight&&!isExpand
|
||||
}"
|
||||
:gutter="gutterWidth">
|
||||
<el-col :span="item.span"
|
||||
v-for="item,index in columns"
|
||||
:key="item.name"
|
||||
v-show="byHeight?true:(index< (showRow*3)||isExpand)">
|
||||
<el-form-item :label="item.title" :label-width="labelWidth" v-if="item.type==='input'">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="formInline[item.name]" :placeholder="item.placeholder" />
|
||||
</el-form-item>
|
||||
<template v-else-if="item.type==='date'">
|
||||
<el-form-item :label="item.title" :label-width="labelWidth" >
|
||||
<el-date-picker
|
||||
value-format="YYYY-MM-DD"
|
||||
v-model="formInline[item.name]"
|
||||
type="date"
|
||||
:placeholder="item.placeholder"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="onSubmit">查询</el-button>
|
||||
<el-button @click="resetForm(ruleFormRef)">重置</el-button>
|
||||
<el-button link type="primary" @click="isExpand = !isExpand">{{ isExpand ? '合并' : '展开'}}<el-icon>
|
||||
<arrow-down v-if="!isExpand" />
|
||||
<arrow-up v-else /> </el-icon
|
||||
></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
let props = defineProps({
|
||||
// 宽度
|
||||
labelWidth: {
|
||||
default: "100px",
|
||||
},
|
||||
gutterWidth: {
|
||||
type: Number,
|
||||
default: 24,
|
||||
},
|
||||
showRow:{
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
columns:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
byHeight:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['submit','reset'])
|
||||
// 收缩展开
|
||||
const isExpand = ref(false)
|
||||
|
||||
const formInline = reactive({
|
||||
})
|
||||
|
||||
for(let item of props.columns){
|
||||
formInline[item.name] = null
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
emit('submit',formInline)
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
console.log('formEl',formEl)
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
const keys = Object.keys(formInline);
|
||||
keys.forEach(key => {
|
||||
formInline[key] = null;
|
||||
});
|
||||
emit("reset", formInline);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.advancedForm{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.form-inline{
|
||||
flex: 1;
|
||||
}
|
||||
.el-form--inline .el-form-item{
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.search-btn{
|
||||
margin-left: 40px;
|
||||
}
|
||||
.not-show{
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<el-form-item v-if="config.valueType === 'input'" :label="config?.label" style="width: 100%">
|
||||
<el-input v-model="value" v-bind="$attrs" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="config.valueType === 'select'" :label="config?.label" style="width: 100%">
|
||||
<el-select v-model="value" v-bind="$attrs" style="width: 100%">
|
||||
<el-option v-for="item in config.options" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="config.valueType === 'date-picker'" :label="config?.label" style="width: 100%">
|
||||
<el-date-picker v-model="value" v-bind="$attrs" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="config.valueType === 'cascader'" :label="config?.label" style="width: 100%">
|
||||
<el-cascader v-model="value" v-bind="$attrs" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="config.valueType === 'time-select'" :label="config?.label" style="width: 100%">
|
||||
<el-time-select v-model="value" v-bind="$attrs" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
type ConfigType = {
|
||||
modelValue?: any
|
||||
config?: any
|
||||
}
|
||||
|
||||
const props = defineProps<ConfigType>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', value: any): void
|
||||
}>()
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
return props?.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emits('update:modelValue', value)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<div class="advancedForm">
|
||||
<el-form ref="ruleFormRef" :inline="true" :label-position="'right'" :model="formParams" class="form-inline">
|
||||
<el-row :class="{ 'not-show': byHeight && !isExpand }" :gutter="gutterWidth">
|
||||
<template v-for="(item, index) in columns">
|
||||
<el-col v-if="item.valueType" v-show="byHeight ? true : index < showRow * 3 || isExpand" :span="item.span">
|
||||
<BaseFormItem :key="index" v-bind="item.attrs" v-model="item.value" :config="item" />
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="onSubmit">查询</el-button>
|
||||
<el-button @click="resetForm(ruleFormRef)">重置</el-button>
|
||||
<el-button v-if="columns.length > 3" link type="primary" @click="isExpand = !isExpand">
|
||||
{{ isExpand ? '合并' : '展开' }}
|
||||
<el-icon>
|
||||
<arrow-down v-if="!isExpand" />
|
||||
<arrow-up v-else />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import BaseFormItem from './components/BaseFormItem.vue'
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
let props = defineProps({
|
||||
// 宽度
|
||||
labelWidth: {
|
||||
default: '100px',
|
||||
},
|
||||
gutterWidth: {
|
||||
type: Number,
|
||||
default: 24,
|
||||
},
|
||||
showRow: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
byHeight: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['submit', 'reset'])
|
||||
// 收缩展开
|
||||
const isExpand = ref(false)
|
||||
const formParams = reactive({})
|
||||
|
||||
const initFormParams = () => {
|
||||
for (let item of props.columns) {
|
||||
formParams[item.name] = item?.value
|
||||
}
|
||||
}
|
||||
|
||||
// 进行遍历获取参数
|
||||
const getFormParams = () => {
|
||||
let searchParams = {}
|
||||
for (let item of props.columns) {
|
||||
searchParams[item.name] = item?.value
|
||||
}
|
||||
return searchParams
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFormParams()
|
||||
})
|
||||
|
||||
// 获取参数进行组装为 obj
|
||||
const onSubmit = () => {
|
||||
let searchParams = getFormParams()
|
||||
emit('submit', searchParams)
|
||||
}
|
||||
|
||||
// 重置参数
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
const keys = Object.keys(formParams)
|
||||
keys.forEach((key) => {
|
||||
let itemColums = props.columns.find((item) => item.name === key)
|
||||
itemColums.value = formParams[key]
|
||||
})
|
||||
let searchParams = getFormParams()
|
||||
emit('reset', searchParams)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.advancedForm {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.form-inline {
|
||||
flex: 1;
|
||||
}
|
||||
.el-form--inline .el-form-item {
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.search-btn {
|
||||
margin-left: 40px;
|
||||
}
|
||||
.not-show {
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<script lang="ts">
|
||||
import { computed } from 'vue'
|
||||
export default {
|
||||
name: 'baseSvgIcon',
|
||||
name: 'BaseSvgIcon',
|
||||
props: {
|
||||
iconClass: { type: String },
|
||||
className: { type: String },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<el-switch v-model="themeConfig.isDark" inline-prompt :active-icon="Sunny" :inactive-icon="Moon" @change="switchDark" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="switchDark">
|
||||
import { Sunny, Moon } from '@element-plus/icons-vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
|
||||
const SettingStore = useSettingStore()
|
||||
// 设置信息
|
||||
const themeConfig = computed(() => SettingStore.themeConfig)
|
||||
|
||||
// 切换暗黑模式
|
||||
const switchDark = () => {
|
||||
const body = document.documentElement as HTMLElement
|
||||
if (themeConfig.value.isDark) body.setAttribute('class', 'dark')
|
||||
else body.setAttribute('class', '')
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="m-edit-table">
|
||||
<div style="margin-top: 15px; margin-bottom: 15px" v-if="mode !== 'hide' && mode !== 'bottom'">
|
||||
<div v-if="mode !== 'hide' && mode !== 'bottom'" style="margin-top: 15px; margin-bottom: 15px">
|
||||
<el-button style="width: 100%" @click="add">
|
||||
<el-icon style="margin-right: 4px"><plus /></el-icon> 添加一行数据</el-button
|
||||
>
|
||||
|
|
@ -8,61 +8,39 @@
|
|||
<el-table :data="transData" style="width: 100%" row-key="id" border>
|
||||
<template v-for="item in columns">
|
||||
<el-table-column
|
||||
v-if="item.type"
|
||||
:type="item.type"
|
||||
:width="item.width"
|
||||
:align="item.align"
|
||||
:fixed="item.fixed"
|
||||
:label="item.label"
|
||||
v-if="item.type"
|
||||
:type="item.type"
|
||||
:width="item.width"
|
||||
:align="item.align"
|
||||
:fixed="item.fixed"
|
||||
:label="item.label"
|
||||
/>
|
||||
<el-table-column
|
||||
v-else
|
||||
:prop="item.name"
|
||||
:width="item.width"
|
||||
:align="item.align"
|
||||
:fixed="item.fixed"
|
||||
:label="item.label"
|
||||
>
|
||||
<el-table-column v-else :prop="item.name" :width="item.width" :align="item.align" :fixed="item.fixed" :label="item.label">
|
||||
<template #default="scope">
|
||||
<template v-if="!item.slot">
|
||||
<template v-if="item.readonly">
|
||||
{{ scope.row[item.name] }}
|
||||
</template>
|
||||
<template v-else-if="item.valueType === 'select'">
|
||||
<el-select
|
||||
clearable
|
||||
:placeholder="`请选择`"
|
||||
v-model="scope.row[item.name]"
|
||||
v-if="scope.row.edit"
|
||||
>
|
||||
<el-option
|
||||
v-for="ite in item.options"
|
||||
:key="ite.value"
|
||||
:label="ite.label"
|
||||
:value="ite.value"
|
||||
/>
|
||||
<el-select v-if="scope.row.edit" v-model="scope.row[item.name]" clearable :placeholder="`请选择`">
|
||||
<el-option v-for="ite in item.options" :key="ite.value" :label="ite.label" :value="ite.value" />
|
||||
</el-select>
|
||||
<span v-else>{{ filterOption(item, scope) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.valueType === 'date'">
|
||||
<el-date-picker
|
||||
v-model="scope.row[item.name]"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
v-if="scope.row.edit"
|
||||
v-if="scope.row.edit"
|
||||
v-model="scope.row[item.name]"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<span v-else>{{ scope.row[item.name] || '--' }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入"
|
||||
v-model="scope.row[item.name]"
|
||||
v-if="scope.row.edit"
|
||||
></el-input>
|
||||
<el-input v-if="scope.row.edit" v-model="scope.row[item.name]" clearable placeholder="请输入"></el-input>
|
||||
<span v-else>{{ scope.row[item.name] || '--' }}</span>
|
||||
</template>
|
||||
</template>
|
||||
|
|
@ -72,59 +50,28 @@
|
|||
</template>
|
||||
<el-table-column prop="operator" label="操作" width="250px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.edit"
|
||||
type="success"
|
||||
size="small"
|
||||
icon="CircleCheckFilled"
|
||||
@click="confirmEdit(scope.row)"
|
||||
>
|
||||
<el-button v-if="scope.row.edit" type="primary" size="small" icon="CircleCheckFilled" @click="confirmEdit(scope.row)">
|
||||
保存
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="Edit"
|
||||
@click="scope.row.edit = !scope.row.edit"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popover
|
||||
trigger="click"
|
||||
v-model:visible="scope.row.visible"
|
||||
placement="top"
|
||||
:width="160"
|
||||
>
|
||||
<el-button v-else type="primary" size="small" icon="Edit" @click="scope.row.edit = !scope.row.edit"> 编辑 </el-button>
|
||||
<el-popover v-model:visible="scope.row.visible" trigger="click" placement="top" :width="160">
|
||||
<p style="display: flex; align-items: center; margin-bottom: 10px">
|
||||
<el-icon color="#faad14" style="margin-right: 10px"><warning-filled /></el-icon>
|
||||
删除此行?</p
|
||||
>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="small" @click="scope.row.visible = false">取消</el-button>
|
||||
<el-button size="small" type="primary" @click="deleteAction(scope.row)"
|
||||
>确定</el-button
|
||||
>
|
||||
<el-button size="small" type="primary" @click="deleteAction(scope.row)">确定</el-button>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button icon="Delete" @click="deleteCurrent(scope.row)" type="danger" size="small"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button icon="Delete" type="danger" size="small" @click="deleteCurrent(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
v-if="scope.row.edit"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="Edit"
|
||||
@click="cancelEdit(scope.row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button v-if="scope.row.edit" type="primary" size="small" icon="Edit" @click="cancelEdit(scope.row)"> 取消 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="margin-top: 15px" v-if="mode !== 'hide' && mode !== 'top'">
|
||||
<div v-if="mode !== 'hide' && mode !== 'top'" style="margin-top: 15px">
|
||||
<el-button style="width: 100%" @click="add">
|
||||
<el-icon style="margin-right: 4px"><plus /></el-icon> 添加一行数据</el-button
|
||||
>
|
||||
|
|
@ -132,72 +79,72 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { deepObjClone } from '@/utils/index'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { reactive } from 'vue'
|
||||
const emit = defineEmits(['del', 'add', 'onChange'])
|
||||
let transData = ref([])
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { deepObjClone } from '@/utils/index'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { reactive } from 'vue'
|
||||
const emit = defineEmits(['del', 'add', 'onChange'])
|
||||
let transData = ref([])
|
||||
|
||||
let props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
editableKeys: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
})
|
||||
let props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
editableKeys: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
})
|
||||
|
||||
// 删除当前此行
|
||||
const deleteCurrent = (row)=>{
|
||||
// console.log('----------',row)
|
||||
// row.visible = true
|
||||
}
|
||||
// 删除当前此行
|
||||
const deleteCurrent = (row) => {
|
||||
// console.log('----------',row)
|
||||
// row.visible = true
|
||||
}
|
||||
|
||||
const getData = () => {
|
||||
let arr = deepObjClone(transData.value)
|
||||
for (let item of arr) {
|
||||
for (let attr in item) {
|
||||
if (attr.includes('te__mp')) {
|
||||
delete item[attr]
|
||||
const getData = () => {
|
||||
let arr = deepObjClone(transData.value)
|
||||
for (let item of arr) {
|
||||
for (let attr in item) {
|
||||
if (attr.includes('te__mp')) {
|
||||
delete item[attr]
|
||||
}
|
||||
}
|
||||
}
|
||||
emit('onChange', arr)
|
||||
}
|
||||
emit('onChange', arr)
|
||||
}
|
||||
|
||||
let obj = {}
|
||||
for (let item of props.columns) {
|
||||
props.data.forEach((it) => {
|
||||
if (!obj[item.name]) {
|
||||
obj[item.name] = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重置数据
|
||||
const reset = () => {
|
||||
transData.value = props.data
|
||||
for (let item of transData.value) {
|
||||
if (props.editableKeys.includes(item.id)) {
|
||||
item.edit = true
|
||||
}
|
||||
let obj = {}
|
||||
for (let item of props.columns) {
|
||||
props.data.forEach((it) => {
|
||||
if (!obj[item.name]) {
|
||||
obj[item.name] = null
|
||||
}
|
||||
})
|
||||
}
|
||||
getData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
// 重置数据
|
||||
const reset = () => {
|
||||
transData.value = props.data
|
||||
for (let item of transData.value) {
|
||||
if (props.editableKeys.includes(item.id)) {
|
||||
item.edit = true
|
||||
}
|
||||
}
|
||||
getData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => props.data,
|
||||
(val) => {
|
||||
// // 转换数据
|
||||
|
|
@ -217,88 +164,88 @@ onMounted(() => {
|
|||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const visible = ref(false)
|
||||
const visible = ref(false)
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
console.log(`${val} items per page`)
|
||||
}
|
||||
|
||||
const listLoading = ref(false)
|
||||
|
||||
// 保存
|
||||
const confirmEdit = (row) => {
|
||||
row.edit = false
|
||||
for (let attr in row) {
|
||||
if (attr.includes('te__mp')) {
|
||||
row[attr] = row[attr.replace('te__mp', '')]
|
||||
}
|
||||
const handleSizeChange = (val: number) => {
|
||||
console.log(`${val} items per page`)
|
||||
}
|
||||
getData()
|
||||
}
|
||||
// 取消
|
||||
const cancelEdit = (row) => {
|
||||
row.edit = !row.edit
|
||||
for (let attr in row) {
|
||||
if (attr !== 'edit') {
|
||||
if (!attr.includes('te__mp')) {
|
||||
row[attr] = row[attr + 'te__mp']
|
||||
|
||||
const listLoading = ref(false)
|
||||
|
||||
// 保存
|
||||
const confirmEdit = (row) => {
|
||||
row.edit = false
|
||||
for (let attr in row) {
|
||||
if (attr.includes('te__mp')) {
|
||||
row[attr] = row[attr.replace('te__mp', '')]
|
||||
}
|
||||
}
|
||||
getData()
|
||||
}
|
||||
// 取消
|
||||
const cancelEdit = (row) => {
|
||||
row.edit = !row.edit
|
||||
for (let attr in row) {
|
||||
if (attr !== 'edit') {
|
||||
if (!attr.includes('te__mp')) {
|
||||
row[attr] = row[attr + 'te__mp']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteAction = (row) => {
|
||||
row.visible = false
|
||||
transData.value = transData.value.filter((item) => item.id !== row.id)
|
||||
emit('del', row)
|
||||
}
|
||||
const deleteAction = (row) => {
|
||||
row.visible = false
|
||||
transData.value = transData.value.filter((item) => item.id !== row.id)
|
||||
emit('del', row)
|
||||
}
|
||||
|
||||
// 添加
|
||||
const add = () => {
|
||||
let id = ~~(Math.random() * 1000000).toFixed(0)
|
||||
let obj1 = Object.assign({}, obj, {
|
||||
id: id,
|
||||
edit: true,
|
||||
visible: false,
|
||||
// 添加
|
||||
const add = () => {
|
||||
let id = ~~(Math.random() * 1000000).toFixed(0)
|
||||
let obj1 = Object.assign({}, obj, {
|
||||
id: id,
|
||||
edit: true,
|
||||
visible: false,
|
||||
})
|
||||
for (let attr in obj1) {
|
||||
let temp = `${attr}te__mp`
|
||||
obj1[temp] = obj1[attr]
|
||||
}
|
||||
|
||||
if (props.mode === 'bottom') {
|
||||
transData.value.push(obj1)
|
||||
}
|
||||
if (props.mode === 'top') {
|
||||
transData.value.unshift(obj1)
|
||||
}
|
||||
}
|
||||
|
||||
const filterOption = (item, scope) => {
|
||||
let obj = item.options.find((ite) => ite.value === scope.row[item.name])
|
||||
if (obj) {
|
||||
return obj.label
|
||||
}
|
||||
return '--'
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
})
|
||||
for (let attr in obj1) {
|
||||
let temp = `${attr}te__mp`
|
||||
obj1[temp] = obj1[attr]
|
||||
}
|
||||
|
||||
if (props.mode === 'bottom') {
|
||||
transData.value.push(obj1)
|
||||
}
|
||||
if (props.mode === 'top') {
|
||||
transData.value.unshift(obj1)
|
||||
}
|
||||
}
|
||||
|
||||
const filterOption = (item, scope) => {
|
||||
let obj = item.options.find((ite) => ite.value === scope.row[item.name])
|
||||
if (obj) {
|
||||
return obj.label
|
||||
}
|
||||
return '--'
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
.inline-edit-table {
|
||||
width: 100%;
|
||||
}
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
.inline-edit-table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,240 +1,206 @@
|
|||
<template>
|
||||
<div class="zb-pro-table">
|
||||
<div class="header">
|
||||
<el-form :inline="true" :model="formInline" class="demo-form-inline search-form" ref="ruleFormRef" >
|
||||
<template v-for="(item, index) in formSearchData" :key="index">
|
||||
<el-form-item :label="item.label" v-show="isExpand ? isExpand : index < 2">
|
||||
<template v-if="item.valueType === 'input'">
|
||||
<el-input v-model="formInline[item.name]" :placeholder="`请输入${item.label}`" />
|
||||
</template>
|
||||
<template v-if="item.valueType === 'select'">
|
||||
<el-select v-model="formInline[item.name]" :placeholder="`请选择${item.label}`">
|
||||
<el-option
|
||||
v-for="ite in item.options"
|
||||
:key="ite.value"
|
||||
:label="ite.label"
|
||||
:value="ite.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
<div class="search">
|
||||
<el-button type="primary" @click="onSubmit" :icon="Search">查询</el-button>
|
||||
<el-button @click="reset(ruleFormRef)">重置</el-button>
|
||||
<el-button link type="primary" @click="isExpand = !isExpand">{{ isExpand ? '合并' : '展开'}}<el-icon>
|
||||
<arrow-down v-if="!isExpand" />
|
||||
<arrow-up v-else /> </el-icon
|
||||
></el-button>
|
||||
</div>
|
||||
<SearchForm :columns="baseFormColumns" @submit="onSubmit" />
|
||||
</div>
|
||||
|
||||
<!----------底部---------------------->
|
||||
<div class="footer">
|
||||
<!-----------工具栏操作工具----------------->
|
||||
<div class="operator">
|
||||
<slot name="btn"></slot>
|
||||
</div>
|
||||
|
||||
<!-- ------------表格--------------->
|
||||
<div class="table">
|
||||
<el-table
|
||||
class="zb-table"
|
||||
v-loading="loading"
|
||||
@selection-change="(val) => emit('selection-change', val)"
|
||||
:data="list"
|
||||
:border="true"
|
||||
v-loading="loading"
|
||||
class="zb-table"
|
||||
:data="list"
|
||||
:border="true"
|
||||
@selection-change="(val) => emit('selection-change', val)"
|
||||
>
|
||||
<template v-for="item in columns">
|
||||
<el-table-column
|
||||
v-if="item.type"
|
||||
:type="item.type"
|
||||
:width="item.width"
|
||||
:align="item.align!=null?item.align:'center'"
|
||||
:fixed="item.fixed"
|
||||
:label="item.label"
|
||||
/>
|
||||
<el-table-column
|
||||
v-else
|
||||
:prop="item.name"
|
||||
:width="item.width"
|
||||
:align="item.align!=null?item.align:'center'"
|
||||
:fixed="item.fixed"
|
||||
:label="item.label"
|
||||
>
|
||||
<el-table-column v-if="item.slot" v-bind="{ ...item, ...{ prop: item.name } }">
|
||||
<template #default="scope">
|
||||
<span v-if="!item.slot">{{ scope.row[item.name] }}</span>
|
||||
<slot v-else :name="item.name" :item="item" :row="scope.row"></slot>
|
||||
<slot :name="item.name" :item="item" :row="scope.row"></slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else v-bind="{ ...item, ...{ prop: item.name } }" />
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
<!-- ------------分页--------------->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:currentPage="currentPage1"
|
||||
:page-size="10"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="data.length"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
v-model:currentPage="pagination.currentPage"
|
||||
:page-size="10"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="data.length"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import {Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
const emit = defineEmits(['reset', 'onSubmit', 'selection-change'])
|
||||
let props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const currentPage1 = ref(1)
|
||||
// 收缩展开
|
||||
const isExpand = ref(false)
|
||||
const handleSizeChange = (val: number) => {
|
||||
console.log(`${val} items per page`)
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
console.log(`current page: ${val}`)
|
||||
currentPage1.value = val
|
||||
}
|
||||
|
||||
const list = computed(() => {
|
||||
let arr = JSON.parse(JSON.stringify(props.data))
|
||||
return arr.splice((currentPage1.value - 1) * 10, 10)
|
||||
})
|
||||
|
||||
const listLoading = ref(false)
|
||||
const confirmEdit = (row) => {
|
||||
row.edit = false
|
||||
}
|
||||
const cancelEdit = (row) => {
|
||||
row.edit = false
|
||||
}
|
||||
|
||||
import { reactive } from 'vue'
|
||||
|
||||
let obj = {}
|
||||
let search = []
|
||||
for (let item of props.columns) {
|
||||
if (item.inSearch) {
|
||||
obj[item.name] = null
|
||||
}
|
||||
if (item.inSearch) {
|
||||
search.push(item)
|
||||
}
|
||||
}
|
||||
const formSearchData = ref(search)
|
||||
const formInline = reactive(obj)
|
||||
|
||||
const onSubmit = () => {
|
||||
console.log('submit!', formInline)
|
||||
emit('onSubmit', formInline)
|
||||
}
|
||||
|
||||
const reset = (formEl: FormInstance | undefined) => {
|
||||
formSearchData.value.forEach((item) => {
|
||||
formInline[item.name] = null
|
||||
import { computed, ref } from 'vue'
|
||||
import SearchForm from '@/components/SearchForm/index.vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
const emit = defineEmits(['reset', 'onSubmit', 'selection-change'])
|
||||
let props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
emit('reset')
|
||||
}
|
||||
const deleteAction = (row) => {
|
||||
ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
draggable: true,
|
||||
|
||||
// 过滤调需要进行搜索选择的
|
||||
const baseFormColumns = computed(() => {
|
||||
return props.columns.filter((item) => item.valueType && item.search)
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
const currentPage = ref(1)
|
||||
// 收缩展开
|
||||
const isExpand = ref(false)
|
||||
const handleSizeChange = (val: number) => {
|
||||
console.log(`${val} items per page`)
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
console.log(`current page: ${val}`)
|
||||
pagination.currentPage = val
|
||||
}
|
||||
|
||||
const list = computed(() => {
|
||||
let arr = JSON.parse(JSON.stringify(props.data))
|
||||
return arr.splice((pagination.currentPage - 1) * 10, 10)
|
||||
})
|
||||
|
||||
const listLoading = ref(false)
|
||||
const confirmEdit = (row) => {
|
||||
row.edit = false
|
||||
}
|
||||
const cancelEdit = (row) => {
|
||||
row.edit = false
|
||||
}
|
||||
|
||||
import { reactive } from 'vue'
|
||||
|
||||
let obj = {}
|
||||
let search = []
|
||||
for (let item of props.columns) {
|
||||
if (item.inSearch) {
|
||||
obj[item.name] = null
|
||||
}
|
||||
if (item.inSearch) {
|
||||
search.push(item)
|
||||
}
|
||||
}
|
||||
const formSearchData = ref(search)
|
||||
const formInline = reactive(obj)
|
||||
|
||||
const onSubmit = () => {
|
||||
emit('onSubmit', formInline)
|
||||
}
|
||||
|
||||
const reset = (formEl: FormInstance | undefined) => {
|
||||
formSearchData.value.forEach((item) => {
|
||||
formInline[item.name] = null
|
||||
})
|
||||
emit('reset')
|
||||
}
|
||||
const deleteAction = (row) => {
|
||||
ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
draggable: true,
|
||||
})
|
||||
.then(() => {
|
||||
list.value = list.value.filter((item) => item.id !== row.id)
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
.zb-pro-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
|
||||
.header{
|
||||
display: flex;
|
||||
padding: 16px 16px 0 16px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
|
||||
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.footer{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
|
||||
.operator{
|
||||
margin-bottom: 15px
|
||||
}
|
||||
.table{
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
.zb-table{
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
.search-form{
|
||||
flex: 1;
|
||||
}
|
||||
.search{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
::v-deep{
|
||||
.el-table__header th{
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #252525;
|
||||
}
|
||||
}
|
||||
.pagination{
|
||||
.zb-pro-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
padding: 16px 16px 0 16px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
|
||||
:deep(.advancedForm) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
|
||||
min-height: 300px;
|
||||
.operator {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.table {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
.zb-table {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
::v-deep {
|
||||
.el-table__header th {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #252525;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<template>
|
||||
<el-switch @change="(val) => changeSwitch(val)" v-model="isDark"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="switchDark">
|
||||
import {computed, ref} from "vue";
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {toggleDark} from '../../../hooks/dark'
|
||||
const SettingStore = useSettingStore()
|
||||
// 横向
|
||||
const isDark = ref(SettingStore.themeConfig.isDark)
|
||||
|
||||
const changeSwitch = ()=>{
|
||||
toggleDark()
|
||||
}
|
||||
</script>
|
||||
|
|
@ -12,18 +12,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-drawer
|
||||
v-model="drawer" title="主题配置" size="300px">
|
||||
<el-drawer v-model="drawer" title="主题配置" size="300px">
|
||||
<div class="theme-item">
|
||||
<label>导航栏布局</label>
|
||||
<el-select
|
||||
v-model="layout"
|
||||
placeholder="请选择"
|
||||
style="width: 150px"
|
||||
@change="(val) => changeSwitch('mode',val)"
|
||||
>
|
||||
<el-select v-model="layout" placeholder="请选择" style="width: 150px" @change="(val) => changeSwitch('mode', val)">
|
||||
<el-option label="纵向" value="vertical"></el-option>
|
||||
<el-option label="横向" value="horizontal"></el-option>
|
||||
<el-option label="分栏" value="columns"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
|
|
@ -36,39 +31,40 @@
|
|||
</div>
|
||||
<div class="theme-item">
|
||||
<label>灰色模式</label>
|
||||
<el-switch v-model="gray" @change="(val) => changeGrayWeak('gray',val)" />
|
||||
<el-switch v-model="gray" @change="(val) => changeGrayWeak('gray', val)" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<label>色弱模式</label>
|
||||
<el-switch v-model="weak" @change="(val) => changeGrayWeak('weak',val)" />
|
||||
<el-switch v-model="weak" @change="(val) => changeGrayWeak('weak', val)" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<label>标签栏</label>
|
||||
<el-switch v-model="showTag" @change="(val) => changeSwitch('showTag',val)" />
|
||||
<el-switch v-model="showTag" @change="(val) => changeSwitch('showTag', val)" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<label>侧边栏 Logo</label>
|
||||
<el-switch v-model="showLogo" @change="(val) => changeSwitch('showLogo',val)" />
|
||||
<el-switch v-model="showLogo" @change="(val) => changeSwitch('showLogo', val)" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<label>保持一个子菜单的展开</label>
|
||||
<el-switch v-model="uniqueOpened" @change="(val) => changeSwitch('uniqueOpened',val)" />
|
||||
<el-switch v-model="uniqueOpened" @change="(val) => changeSwitch('uniqueOpened', val)" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<label>固定header</label>
|
||||
<el-switch v-model="fixedHeader" @change="(val) => changeSwitch('fixedHeader',val)" />
|
||||
<el-switch v-model="fixedHeader" @change="(val) => changeSwitch('fixedHeader', val)" />
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, ref} from 'vue'
|
||||
import SwitchDark from './components/switchDark.vue'
|
||||
import {ElMessage} from "element-plus";
|
||||
import {PRIMARY_COLOR} from "@/config/index";
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {getDarkColor,getLightColor} from '@/utils/index'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { openLoading, closeLoading } from '@/utils/element'
|
||||
import SwitchDark from '@/components/SwitchDark/index.vue'
|
||||
import { PRIMARY_COLOR } from '@/config/index'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
|
||||
const SettingStore = useSettingStore()
|
||||
const layout = ref(SettingStore.themeConfig.mode)
|
||||
const showTag = ref(SettingStore.themeConfig.showTag)
|
||||
|
|
@ -81,17 +77,15 @@
|
|||
|
||||
const drawer = computed({
|
||||
get() {
|
||||
return SettingStore.themeConfig.showSetting;
|
||||
return SettingStore.themeConfig.showSetting
|
||||
},
|
||||
set() {
|
||||
changeSwitch('showSetting',!SettingStore.themeConfig.showSetting)
|
||||
}
|
||||
changeSwitch('showSetting', !SettingStore.themeConfig.showSetting)
|
||||
},
|
||||
})
|
||||
|
||||
// 预定义主题颜色
|
||||
const predefineColor = [
|
||||
'#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d'
|
||||
];
|
||||
const predefineColor = ['#409EFF', '#1890ff', '#304156', '#212121', '#11a983', '#13c2c2', '#6959CD', '#f5222d']
|
||||
|
||||
const operator = (type) => {
|
||||
switch (type) {
|
||||
|
|
@ -103,45 +97,54 @@
|
|||
return
|
||||
}
|
||||
}
|
||||
const changeSwitch = (key,val) => {
|
||||
SettingStore.setThemeConfig({key, val})
|
||||
|
||||
// 进行配置
|
||||
const changeSwitch = (key, val) => {
|
||||
SettingStore.setThemeConfig({ key, val })
|
||||
if (key === 'mode') {
|
||||
openLoading()
|
||||
setTimeout(() => {
|
||||
closeLoading()
|
||||
}, 600)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听布局变化
|
||||
watch(
|
||||
() => layout.value,
|
||||
() => {
|
||||
const body = document.body as HTMLElement
|
||||
body.setAttribute('class', `layout-${layout.value}`)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 修改主题颜色
|
||||
const changePrimary = (val)=>{
|
||||
const changePrimary = (val) => {
|
||||
if (!val) {
|
||||
primary.value = val = PRIMARY_COLOR;
|
||||
ElMessage({ type: "success", message: `主题颜色已重置为 ${PRIMARY_COLOR}` });
|
||||
primary.value = val = PRIMARY_COLOR
|
||||
ElMessage({ type: 'success', message: `主题颜色已重置为 ${PRIMARY_COLOR}` })
|
||||
}
|
||||
// 颜色加深
|
||||
document.documentElement.style.setProperty("--el-color-primary-dark-2", `${getDarkColor(val, 0.1)}`);
|
||||
document.documentElement.style.setProperty("--el-color-primary", val);
|
||||
// 颜色变浅
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
document.documentElement.style.setProperty(
|
||||
`--el-color-primary-light-${i}`,
|
||||
`${getLightColor(val, i / 10)}`
|
||||
);
|
||||
}
|
||||
changeSwitch('primary',val)
|
||||
document.documentElement.style.setProperty('--el-color-primary', val)
|
||||
changeSwitch('primary', val)
|
||||
}
|
||||
|
||||
// 修改灰色模式
|
||||
const changeGrayWeak = (type,val)=>{
|
||||
const body = document.documentElement as HTMLElement;
|
||||
if (!val) return body.setAttribute("style", "");
|
||||
if (type === "gray") body.setAttribute("style", "filter: grayscale(1)");
|
||||
if (type === "weak") body.setAttribute("style", "filter: invert(80%)");
|
||||
changeSwitch(type,val)
|
||||
const changeGrayWeak = (type, val) => {
|
||||
const body = document.documentElement as HTMLElement
|
||||
if (!val) return body.setAttribute('style', '')
|
||||
if (type === 'gray') body.setAttribute('style', 'filter: grayscale(1)')
|
||||
if (type === 'weak') body.setAttribute('style', 'filter: invert(80%)')
|
||||
changeSwitch(type, val)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep(.el-drawer__header){
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px 14px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
::v-deep(.el-drawer__header) {
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px 14px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.m-setting-fix {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
|
|
@ -181,19 +184,17 @@
|
|||
border-radius: 5.5px;
|
||||
font-size: 12px;
|
||||
background: #ebf5ff;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
.item-child2 {
|
||||
margin-top: 10px;
|
||||
color: #b37feb;
|
||||
background: #f7f2fd;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-drawer__title) {
|
||||
:deep(.el-drawer__title) {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
|
|
@ -205,6 +206,5 @@
|
|||
font-size: 14px;
|
||||
color: black;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,21 @@
|
|||
<template>
|
||||
<div class="m-wangEditor">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
:mode="mode"
|
||||
/>
|
||||
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :default-config="toolbarConfig" :mode="mode" />
|
||||
<Editor
|
||||
class="editor-content'"
|
||||
style="height: 300px; overflow-y: hidden;"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
v-model="valueHtml"
|
||||
class="editor-content'"
|
||||
style="height: 300px; overflow-y: hidden"
|
||||
:default-config="editorConfig"
|
||||
:mode="mode"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// 引入 wangEditor
|
||||
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||
import {onBeforeUnmount, onMounted, watch, shallowRef, ref, computed} from 'vue'
|
||||
import { onBeforeUnmount, onMounted, watch, shallowRef, ref, computed } from 'vue'
|
||||
let editors = null
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef()
|
||||
|
|
@ -45,18 +40,18 @@
|
|||
}
|
||||
|
||||
const valueHtml = computed({
|
||||
get(){
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(val){
|
||||
set(val) {
|
||||
// 防止富文本内容为空时,校验失败
|
||||
if (editorRef.value.isEmpty()) val = "";
|
||||
if (editorRef.value.isEmpty()) val = ''
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(()=>{
|
||||
onBeforeUnmount(() => {
|
||||
// 调用销毁 API 对当前编辑器实例进行销毁
|
||||
const editor = editorRef.value
|
||||
if (editor == null) {
|
||||
|
|
@ -66,15 +61,15 @@
|
|||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.m-wangEditor{
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
border: 1px solid #cccccc;
|
||||
.editor-toolbar {
|
||||
border-bottom: 1px solid #cccccc;
|
||||
.m-wangEditor {
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
border: 1px solid #cccccc;
|
||||
.editor-toolbar {
|
||||
border-bottom: 1px solid #cccccc;
|
||||
}
|
||||
.editor-content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
.editor-content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
<template>
|
||||
<div class="zb-pipeline-start-wrapper">
|
||||
<div
|
||||
class="zb-pipeline-start"
|
||||
v-bind:class="control === value ? 'active' : ''"
|
||||
v-on:click="handleClick"
|
||||
>
|
||||
<div class="zb-pipeline-start" :class="control === value ? 'active' : ''" @click="handleClick">
|
||||
<div class="zb-pipeline-start-header">
|
||||
<zb-icon type="play-filled" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// * 默认主题颜色
|
||||
export const PRIMARY_COLOR: string = "#409eff";
|
||||
export const PRIMARY_COLOR = '#409eff'
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import { useDark, useToggle } from '@vueuse/core'
|
||||
|
||||
export const isDark = useDark()
|
||||
export const toggleDark = useToggle(isDark)
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* @description 方法用来切换元素 进入全屏
|
||||
* @vueuse/core 提供了 useFullscreen 方法,因此项目中采用 useFullscreen,实现效果功能是一样得,目的是为了减少代码量
|
||||
* 它原理原理实现如下:
|
||||
*/
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
export const useFullscreen = () => {
|
||||
/**
|
||||
* @description: 是否支持全屏+判断浏览器前缀
|
||||
*/
|
||||
const isFullscreen = () => {
|
||||
let prefixName = '' // 浏览器前缀
|
||||
let fullscreenEnabled = false
|
||||
// 判断浏览器前缀
|
||||
if (document.fullscreenEnabled) {
|
||||
fullscreenEnabled = document.fullscreenEnabled
|
||||
// webkit
|
||||
} else if (document.webkitFullscreenEnabled) {
|
||||
fullscreenEnabled = document.webkitFullscreenEnabled
|
||||
prefixName = 'webkit'
|
||||
// moz
|
||||
} else if (document.mozFullScreenEnabled) {
|
||||
fullscreenEnabled = document.mozFullScreenEnabled
|
||||
prefixName = 'moz'
|
||||
// ms
|
||||
} else if (document.msFullscreenEnabled) {
|
||||
fullscreenEnabled = document.msFullscreenEnabled
|
||||
prefixName = 'ms'
|
||||
}
|
||||
return {
|
||||
fullscreenEnabled,
|
||||
prefixName,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 检测有没有元素处于全屏状态
|
||||
* @return 布尔值
|
||||
*/
|
||||
const isElementFullScreen = () => {
|
||||
const fullscreenElement =
|
||||
document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement
|
||||
if (fullscreenElement === null) {
|
||||
return false // 当前没有元素在全屏状态
|
||||
} else {
|
||||
return true // 有元素在全屏状态
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 将传进来的元素全屏
|
||||
* @param {String} domName 要全屏的dom名称
|
||||
*/
|
||||
const Fullscreen = (target) => {
|
||||
const targetRef = target || (document == null ? void 0 : document.querySelector('html'))
|
||||
const { prefixName } = isFullscreen()
|
||||
const methodName = prefixName === '' ? 'requestFullscreen' : `${prefixName}RequestFullScreen`
|
||||
targetRef[methodName]()
|
||||
}
|
||||
|
||||
// 退出全屏
|
||||
const exitFullscreen = () => {
|
||||
const { prefixName } = isFullscreen()
|
||||
const methodName = prefixName === '' ? 'exitFullscreen' : `${prefixName}ExitFullscreen`
|
||||
document[methodName]()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 浏览器无法进入全屏时触发,可能是技术原因,也可能是用户拒绝:比如全屏请求不是在事件处理函数中调用,会在这里拦截到错误
|
||||
* @param {Function} enterErrorFn 回调
|
||||
*/
|
||||
const screenError = () => {
|
||||
const { prefixName } = isFullscreen()
|
||||
const methodName = `on${prefixName}fullscreenerror`
|
||||
document[methodName] = (e) => {
|
||||
ElMessage.error('进入全屏失败')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 监听进入/离开全屏
|
||||
* @param {Function} enter 进入全屏的回调
|
||||
* @param {Function} quit 离开全屏的回调
|
||||
*/
|
||||
const screenChange = (enter, quit) => {
|
||||
const { fullscreenEnabled, prefixName } = isFullscreen()
|
||||
if (!fullscreenEnabled) return
|
||||
const methodName = `on${prefixName}fullscreenchange`
|
||||
document[methodName] = (e) => {
|
||||
if (isElementFullScreen()) {
|
||||
enter && enter(e) // 进入全屏回调
|
||||
} else {
|
||||
quit && quit(e) // 离开全屏的回调
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
screenError()
|
||||
})
|
||||
|
||||
return {
|
||||
isFullscreen,
|
||||
isElementFullScreen,
|
||||
Fullscreen,
|
||||
exitFullscreen,
|
||||
screenChange,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import ResizeObserver from 'resize-observer-polyfill'
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
import requestAnimationFrameThrottle from '@/utils/requestAnimationFrameThrottle'
|
||||
export const useResizeElement = (chart, chartsRef) => {
|
||||
let observer = null
|
||||
let widthW = 0
|
||||
let heightW = 0
|
||||
const handleResize = (entries) => {
|
||||
const { contentRect } = entries[0]
|
||||
let { width, height } = contentRect
|
||||
width = Math.floor(width)
|
||||
height = Math.floor(height)
|
||||
if (widthW !== width || heightW !== height) {
|
||||
widthW = width
|
||||
heightW = height
|
||||
chart && chart.resize()
|
||||
}
|
||||
}
|
||||
const addObserver = () => {
|
||||
observer = new ResizeObserver(requestAnimationFrameThrottle(handleResize))
|
||||
observer.observe(chartsRef)
|
||||
}
|
||||
|
||||
const removeObserver = () => {
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
observer = null
|
||||
}
|
||||
chart && chart.dispose()
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeObserver()
|
||||
})
|
||||
|
||||
return {
|
||||
addObserver,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { useSettingStore } from '@/store/modules/setting'
|
||||
import { computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const { body } = document
|
||||
|
||||
const WIDTH = 800 // refer to Bootstrap's responsive design
|
||||
const MAX_WIDTH = 1200
|
||||
|
||||
export const useResizeHandler = () => {
|
||||
const SettingStore = useSettingStore()
|
||||
const route = useRoute()
|
||||
const device = computed(() => {
|
||||
return SettingStore.device
|
||||
})
|
||||
function $_isMobile() {
|
||||
const rect = body.getBoundingClientRect()
|
||||
return rect.width - 1 < WIDTH
|
||||
}
|
||||
|
||||
function collapse() {
|
||||
const rect = body.getBoundingClientRect()
|
||||
if (rect.width - 1 > MAX_WIDTH) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function $_resizeHandler() {
|
||||
if (!document.hidden) {
|
||||
// bool型,表示页面是否处于隐藏状态。页面隐藏包括页面在后台标签页或者浏览器最小化
|
||||
const isMobile = $_isMobile()
|
||||
const isCollapse = collapse()
|
||||
SettingStore.toggleDevice(isMobile ? 'mobile' : 'desktop')
|
||||
|
||||
if (isMobile) {
|
||||
SettingStore.closeSideBar({ withoutAnimation: true })
|
||||
}
|
||||
|
||||
if (!isMobile) {
|
||||
SettingStore.setCollapse(isCollapse)
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
const isMobile = $_isMobile()
|
||||
if (isMobile) {
|
||||
SettingStore.toggleDevice('mobile')
|
||||
SettingStore.closeSideBar({ withoutAnimation: true })
|
||||
}
|
||||
window.addEventListener('resize', $_resizeHandler)
|
||||
|
||||
watch(route, () => {
|
||||
if (device.value === 'mobile' && SettingStore.isCollapse) {
|
||||
SettingStore.closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', $_resizeHandler)
|
||||
})
|
||||
|
||||
return { device }
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 自定义name的壳的集合
|
||||
import { h } from 'vue'
|
||||
|
||||
const wrapperMap = new Map()
|
||||
|
||||
export const useWrapComponents = (Component, route) => {
|
||||
let wrapper
|
||||
if (Component) {
|
||||
const wrapperName = route.name
|
||||
if (wrapperMap.has(wrapperName)) {
|
||||
wrapper = wrapperMap.get(wrapperName)
|
||||
} else {
|
||||
wrapper = {
|
||||
name: wrapperName,
|
||||
render() {
|
||||
return h('div', { className: 'app-main-inner' }, Component)
|
||||
},
|
||||
}
|
||||
wrapperMap.set(wrapperName, wrapper)
|
||||
}
|
||||
return h(wrapper)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon' // svg component
|
||||
|
||||
|
||||
// const req = require.context('./svg', false, /\.svg$/)
|
||||
const req = import.meta.globEager('./svg/*.svg')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
<template>
|
||||
<div class="hamburger-container" @click="handleCollapse">
|
||||
<el-icon class="icon" v-if="isCollapse" ><expand /></el-icon>
|
||||
<el-icon class="icon" v-else><fold /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {computed} from "_vue@3.2.40@vue";
|
||||
const SettingStore = useSettingStore()
|
||||
const isCollapse = computed(() =>!SettingStore.isCollapse)
|
||||
const handleCollapse = () => {
|
||||
SettingStore.setCollapse(isCollapse.value)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.hamburger-container{
|
||||
padding: 0px 15px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
}
|
||||
.icon {
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
|
||||
<template>
|
||||
<div class="m-headerSearch">
|
||||
<el-tooltip effect="dark" content="菜单搜索" placement="bottom">
|
||||
<el-icon class="bell" style="font-size: 22px;" @click="handleSearch"><Search /></el-icon>
|
||||
</el-tooltip>
|
||||
<el-dialog v-model="isShowSearch" width="600px" destroy-on-close :show-close="false">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
ref="headerSearchSelect"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="菜单搜索 :支持菜单名称、路径"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.item.path" :value="item.item.path" :label="item&&item.item.title&&item.item.title.length&&item.item.title.join(' > ') " >
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, ref, watch} from 'vue'
|
||||
import path from 'path-browserify'
|
||||
import Fuse from 'fuse.js'
|
||||
import { useVueFuse } from 'vue-fuse'
|
||||
import {useRouter} from "vue-router";
|
||||
const router = useRouter()
|
||||
const isShowSearch = ref(false);
|
||||
const options = ref([]);
|
||||
const searchPool = ref([]);
|
||||
const search = ref('');
|
||||
const fuse = ref(null);
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
const PermissionStore = usePermissionStore()
|
||||
const routes = computed(() => PermissionStore.routes)
|
||||
|
||||
const handleSearch = () => {
|
||||
isShowSearch.value = true
|
||||
}
|
||||
|
||||
const initFuse = (list)=> {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
watch(searchPool,(list)=>{
|
||||
initFuse(list)
|
||||
})
|
||||
|
||||
// 筛选出可以在侧栏中显示的路线 生成标题
|
||||
const generateRoutes = (routes, basePath = '/', prefixTitle = [])=>{
|
||||
let res = []
|
||||
|
||||
for (const router of routes) {
|
||||
// 忽略隐藏的路由
|
||||
if (router.hidden) { continue }
|
||||
|
||||
const data = {
|
||||
path: path.resolve(basePath, router.path),
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
if (router.meta && router.meta.title) {
|
||||
data.title = [...data.title, router.meta.title]
|
||||
|
||||
if (router.redirect !== 'noRedirect') {
|
||||
// 仅推送带有标题的路由
|
||||
// 特殊情况:需要排除无重定向的父路由器
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
// 递归子路由
|
||||
if (router.children) {
|
||||
const tempRoutes = generateRoutes(router.children, data.path, data.title)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const change=(val)=> {
|
||||
if(val){
|
||||
router.push({
|
||||
path: val,
|
||||
})
|
||||
}
|
||||
options.value = []
|
||||
search.value = ''
|
||||
isShowSearch.value = false
|
||||
}
|
||||
onMounted(()=>{
|
||||
searchPool.value = generateRoutes(JSON.parse(JSON.stringify(routes.value)))
|
||||
})
|
||||
|
||||
const querySearch=(query)=> {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.m-headerSearch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
.item-info-pop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.bell{
|
||||
color: black;
|
||||
}
|
||||
.item-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.transverseMenu {
|
||||
.bell {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.header-search-select{
|
||||
|
||||
}
|
||||
/* 菜单搜索样式 */
|
||||
.m-headerSearch {
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__header {
|
||||
display: none;
|
||||
}
|
||||
.el-dialog__body{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.header-search-select{
|
||||
height: 50px;
|
||||
:deep(.el-input__wrapper){
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<template>
|
||||
<div class="m-screenful">
|
||||
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="toggle" class="full-screen"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFullscreen } from "@vueuse/core";
|
||||
const { toggle, isFullscreen } = useFullscreen();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.m-screenful {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 0;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.transverseMenu {
|
||||
.full-screen {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
<template>
|
||||
<div class="m-setting">
|
||||
<el-tooltip effect="dark" content="主题设置" placement="bottom">
|
||||
<el-icon style="font-size: 20px;" class="bell"><Setting @click="changeSwitch('showSetting',true)"/></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
const SettingStore = useSettingStore()
|
||||
const changeSwitch = (key,val) => {
|
||||
SettingStore.setThemeConfig({key, val})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.m-setting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
.item-info-pop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.bell{
|
||||
color: black;
|
||||
}
|
||||
.item-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.transverseMenu {
|
||||
.bell {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<template>
|
||||
<el-dropdown trigger="click" @command="setAssemblySize">
|
||||
<svg-icon class-name="size-icon" icon-class="size" style="font-size: 20px;"/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="item in assemblySizeList" :key="item" :disabled="globalComSize === item" :command="item">
|
||||
{{ assemblySizeListCh[item] }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from "vue";
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
const SettingStore = useSettingStore()
|
||||
|
||||
const globalComSize = computed(():string=>SettingStore.themeConfig.globalComSize)
|
||||
|
||||
const assemblySizeListCh = reactive<{ [key: string]: any }>({
|
||||
default: "默认",
|
||||
large: "大型",
|
||||
small: "小型"
|
||||
});
|
||||
|
||||
const assemblySizeList = reactive<string[]>(["default", "large", "small"]);
|
||||
|
||||
const setAssemblySize = (item: string) => {
|
||||
if (globalComSize.value === item) return;
|
||||
SettingStore.setThemeConfig({key:'globalComSize', val:item})
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.transverseMenu {
|
||||
.size-icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
<template>
|
||||
<div
|
||||
class="m-layout-header"
|
||||
:class="[
|
||||
SettingStore.themeConfig.fixedHeader?'zb-fixed-header':'zb-no-fixed-header',
|
||||
mode === 'horizontal'?'':isCollapse?'fixed-header-collapse':'fixed-header-no-collapse'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="header"
|
||||
:class="{
|
||||
transverseMenu: mode === 'horizontal',
|
||||
}"
|
||||
>
|
||||
<UMenu v-if="mode === 'horizontal'" />
|
||||
<div class="left" v-if="mode === 'vertical'">
|
||||
<CollapseIcon/>
|
||||
<Hamburger />
|
||||
</div>
|
||||
<div class="right">
|
||||
<GlobalComSize class="right-item-menu"/>
|
||||
|
||||
<HeaderSearch class="right-item-menu"/>
|
||||
|
||||
<Remind class="right-item-menu"/>
|
||||
|
||||
<ScreenFull class="right-item-menu"/>
|
||||
|
||||
<Setting class="right-item-menu"/>
|
||||
|
||||
<el-dropdown @command="commandAction">
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar :icon="UserFilled" :size="30" style="margin-right: 6px" />{{
|
||||
userInfo.username
|
||||
}}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="2" >
|
||||
<el-icon><Edit /></el-icon>修改密码</el-dropdown-item>
|
||||
<el-dropdown-item :command="1" divided>
|
||||
<el-icon><SwitchButton /></el-icon>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<tag-views v-if="showTag" />
|
||||
<personal ref="person" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { UserFilled } from '@element-plus/icons-vue'
|
||||
import Personal from './components/Personal.vue'
|
||||
import TagViews from '../TagsView/index.vue'
|
||||
import GlobalComSize from './components/globalComSize.vue'
|
||||
import Hamburger from '@/components/Hamburger/index.vue'
|
||||
import Setting from './components/Setting.vue'
|
||||
import ScreenFull from './components/ScreenFull.vue'
|
||||
import Remind from './components/Remind'
|
||||
import HeaderSearch from './components/HeaderSearch'
|
||||
import CollapseIcon from './components/CollapseIcon'
|
||||
import UMenu from '../Sidebar/components/Menu.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {useUserStore} from "@/store/modules/user"
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
import {useTagsViewStore} from "@/store/modules/tagsView"
|
||||
|
||||
const person = ref()
|
||||
const router = useRouter()
|
||||
const SettingStore = useSettingStore()
|
||||
const UserStore = useUserStore()
|
||||
const PermissionStore = usePermissionStore()
|
||||
const TagsViewStore = useTagsViewStore()
|
||||
|
||||
const isCollapse = computed(() =>!SettingStore.isCollapse)
|
||||
// menu 布局
|
||||
const mode = computed(() => SettingStore.themeConfig.mode)
|
||||
// 显示 tag
|
||||
const showTag = computed(() =>SettingStore.themeConfig.showTag)
|
||||
// 用户信息
|
||||
const userInfo = computed(() => UserStore.userInfo)
|
||||
|
||||
const logOut = async () => {
|
||||
ElMessageBox.confirm('确定退出登录吗?', '退出登录', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
router.push({ path: '/login' })
|
||||
await UserStore.logout()
|
||||
PermissionStore.clearRoutes()
|
||||
TagsViewStore.clearVisitedView()
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "退出登录成功!"
|
||||
});
|
||||
} catch (e) {}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const commandAction = (key: number) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
logOut()
|
||||
break
|
||||
case 2:
|
||||
person.value.show()
|
||||
break
|
||||
}
|
||||
}
|
||||
const handleCollapse = () => {
|
||||
SettingStore.setCollapse(isCollapse.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mobile {
|
||||
.m-layout-header {
|
||||
left: 0 !important;
|
||||
width: 100%!important;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.right-item-menu{
|
||||
margin-right: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.zb-fixed-header{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.zb-no-fixed-header{
|
||||
width: 100%!important;;
|
||||
}
|
||||
|
||||
.m-layout-header {
|
||||
width: 100%;
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
}
|
||||
.fixed-header-collapse{
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
.fixed-header-no-collapse{
|
||||
width: calc(100% - 210px);
|
||||
}
|
||||
.el-dropdown {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.transverseMenu{
|
||||
display: flex;
|
||||
.el-menu{
|
||||
overflow: hidden;
|
||||
}
|
||||
.right{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width:300px ;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
<template>
|
||||
<div class="main-columns">
|
||||
<div class="layout-columns-aside">
|
||||
<div class="logo flex-center">
|
||||
<img src="@/assets/image/logo.png" alt="logo" />
|
||||
</div>
|
||||
<el-scrollbar>
|
||||
<div class="menu-wrap">
|
||||
<div
|
||||
v-for="item in menusRoutes"
|
||||
:key="item.path"
|
||||
class="item-menu-wrap"
|
||||
:class="{
|
||||
'active-menu': activeCurrentMenu === item.path,
|
||||
}"
|
||||
@click="handleChangeMenu(item)"
|
||||
>
|
||||
<el-icon :size="20">
|
||||
<component :is="item?.meta?.icon"></component>
|
||||
</el-icon>
|
||||
<span class="title">{{ item?.meta?.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="layout-columns-sub" :style="{ width: isCollapse ? '60px' : '210px' }">
|
||||
<div class="logo flex-center">
|
||||
<span v-show="subMenus.length">{{ isCollapse ? 'Vue' : 'Vue Admin Perfect' }}</span>
|
||||
</div>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:collapse="isCollapse"
|
||||
:router="false"
|
||||
:default-active="activeMenu"
|
||||
:unique-opened="SettingStore.themeConfig.uniqueOpened"
|
||||
:collapse-transition="false"
|
||||
class="menu-columns"
|
||||
>
|
||||
<SubMenu :menu-list="subMenus" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="layout-header">
|
||||
<div class="header-tool">
|
||||
<HeaderToolLeft />
|
||||
<HeaderToolRight />
|
||||
</div>
|
||||
<TagsView v-if="themeConfig.showTag" />
|
||||
</div>
|
||||
<Main />
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
import Footer from '../components/Footer/index.vue'
|
||||
import SubMenu from '../components/SubMenu/SubMenu.vue'
|
||||
import TagsView from '../components/TagsView/index.vue'
|
||||
const PermissionStore = usePermissionStore()
|
||||
const SettingStore = useSettingStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import HeaderToolRight from '../components/Header/ToolRight.vue'
|
||||
import HeaderToolLeft from '../components/Header/ToolLeft.vue'
|
||||
import Main from '../components/Main/index.vue'
|
||||
// 获取路由
|
||||
const permission_routes = computed(() => PermissionStore.permission_routes)
|
||||
|
||||
// 获取路由
|
||||
const menusRoutes = computed(() => {
|
||||
return PermissionStore.permission_routes.filter((item) => !item.hidden)
|
||||
})
|
||||
|
||||
const activeCurrentMenu = ref('')
|
||||
// 主题配置
|
||||
const themeConfig = computed(() => SettingStore.themeConfig)
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse)
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route
|
||||
return path
|
||||
})
|
||||
const basePath = ref<string>('/')
|
||||
const subMenus = ref([])
|
||||
|
||||
watch(
|
||||
() => [route],
|
||||
() => {
|
||||
if (!menusRoutes.value.length) return
|
||||
const [firstMenu] = route.matched
|
||||
activeCurrentMenu.value = firstMenu.path
|
||||
let menuItem = menusRoutes.value.find((item) => firstMenu.path === item.path)
|
||||
if (menuItem && menuItem.children?.length) {
|
||||
subMenus.value = menuItem.children
|
||||
} else {
|
||||
subMenus.value = []
|
||||
}
|
||||
basePath.value = firstMenu.path
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
const handleChangeMenu = (item) => {
|
||||
router.push(item.path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-columns {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.layout-columns-aside {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
background-color: #304156;
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
.logo {
|
||||
box-sizing: border-box;
|
||||
height: 50px;
|
||||
img {
|
||||
width: 32px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
.menu-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.item-menu-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.active-menu {
|
||||
background: $primaryColor;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.title {
|
||||
color: #e5eaf3;
|
||||
}
|
||||
.el-icon {
|
||||
color: #e5eaf3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-columns-sub {
|
||||
flex-shrink: 0;
|
||||
width: 200px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
border-right: 1px solid #eee;
|
||||
.el-scrollbar {
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
.logo {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 50px;
|
||||
border-bottom: 1px solid #eee;
|
||||
span {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
::v-deep(.menu-columns) {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.layout-header {
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
.header-tool {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
.m-layout-header {
|
||||
width: 100%;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
.header-inner {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
background-color: $menuBg;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.menu-horizontal {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<!--纵向布局-->
|
||||
<Height />
|
||||
<div
|
||||
class="m-layout-header"
|
||||
:class="{
|
||||
'fixed-header': themeConfig.fixedHeader,
|
||||
}"
|
||||
>
|
||||
<div class="header-inner">
|
||||
<el-menu
|
||||
mode="horizontal"
|
||||
:default-active="activeMenu"
|
||||
background-color="#304156"
|
||||
text-color="#bfcbd9"
|
||||
:unique-opened="SettingStore.themeConfig.uniqueOpened"
|
||||
:collapse-transition="false"
|
||||
class="menu-horizontal"
|
||||
>
|
||||
<SubItem v-for="route in permission_routes" :key="route.path" :item="route" />
|
||||
</el-menu>
|
||||
<HeaderToolRight />
|
||||
</div>
|
||||
<TagsView v-if="themeConfig.showTag" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 引入组件
|
||||
import Height from '../../components/Header/components/Height.vue'
|
||||
import HeaderToolRight from '../../components/Header/ToolRight.vue'
|
||||
import TagsView from '../../components/TagsView/index.vue'
|
||||
import SubItem from '../../components/SubMenu/SubItem.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
const PermissionStore = usePermissionStore()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
// 获取路由
|
||||
const permission_routes = computed(() => PermissionStore.permission_routes)
|
||||
import { computed } from 'vue'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
const SettingStore = useSettingStore()
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
})
|
||||
|
||||
// 主题配置
|
||||
const themeConfig = computed(() => SettingStore.themeConfig)
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<div class="main-container">
|
||||
<UHeader />
|
||||
<Main />
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Sidebar from '../components/Sidebar/index.vue'
|
||||
import UHeader from './HeaderHorizontal/index.vue'
|
||||
import Main from '../components/Main/index.vue'
|
||||
import Footer from '../components/Footer/index.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
.mobile {
|
||||
.m-layout-header {
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.show-tag {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.zb-no-fixed-header {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.m-layout-header {
|
||||
width: 100%;
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
|
||||
.header-inner {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.collapse {
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
.no-collapse {
|
||||
width: calc(100% - 210px);
|
||||
}
|
||||
|
||||
.el-dropdown {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.transverseMenu {
|
||||
display: flex;
|
||||
.el-menu {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.el-menu-item) {
|
||||
height: 100% !important;
|
||||
}
|
||||
.tool-bar-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width: 300px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<!--纵向布局-->
|
||||
<Height />
|
||||
<div
|
||||
class="m-layout-header"
|
||||
:class="{
|
||||
'fixed-header': themeConfig.fixedHeader,
|
||||
collapse: themeConfig.fixedHeader && isCollapse,
|
||||
'no-collapse': themeConfig.fixedHeader && !isCollapse,
|
||||
}"
|
||||
>
|
||||
<div class="header-inner">
|
||||
<HeaderToolLeft />
|
||||
<HeaderToolRight />
|
||||
</div>
|
||||
<TagsView v-if="themeConfig.showTag" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 引入组件
|
||||
import Height from '../../components/Header/components/Height.vue'
|
||||
import HeaderToolRight from '../../components/Header/ToolRight.vue'
|
||||
import HeaderToolLeft from '../../components/Header/ToolLeft.vue'
|
||||
import TagsView from '../../components/TagsView/index.vue'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
const SettingStore = useSettingStore()
|
||||
|
||||
// 主题配置
|
||||
const themeConfig = computed(() => SettingStore.themeConfig)
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<!--纵向布局-->
|
||||
<Sidebar />
|
||||
<div class="main-container">
|
||||
<HeaderVertical />
|
||||
<Main />
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Sidebar from '../components/Sidebar/index.vue'
|
||||
import HeaderVertical from './HeaderVertical/index.vue'
|
||||
import Main from '../components/Main/index.vue'
|
||||
import Footer from '../components/Footer/index.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g-container-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
}
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 90;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<template>
|
||||
<div class="app-main" v-if="isReload">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-slide" mode="out-in" appear>
|
||||
<keep-alive :include="cacheRoutes">
|
||||
<component :is="Component" :key="route.path" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
<Theme />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Theme from '@/components/Theme/index.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
const SettingStore = useSettingStore()
|
||||
const PermissionStor = usePermissionStore()
|
||||
|
||||
const cacheRoutes = computed(() =>PermissionStor.getCacheRoutes())
|
||||
const isReload = computed(() => SettingStore.isReload)
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
<template>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
background-color="#304156"
|
||||
text-color="#bfcbd9"
|
||||
:mode="mode"
|
||||
:unique-opened="SettingStore.themeConfig.uniqueOpened"
|
||||
:collapse-transition="false"
|
||||
class="el-menu-vertical-demo"
|
||||
:collapse="isCollapse"
|
||||
>
|
||||
<sub-item
|
||||
v-for="route in permission_routes"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
/>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import SubItem from './SubItem.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import { computed } from 'vue'
|
||||
|
||||
// 在setup中获取store
|
||||
const route = useRoute()
|
||||
const PermissionStore = usePermissionStore()
|
||||
const SettingStore = useSettingStore()
|
||||
|
||||
// 获取路由
|
||||
const permission_routes = computed(() => PermissionStore.permission_routes)
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
})
|
||||
|
||||
// 是否折叠
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse)
|
||||
|
||||
// 横向
|
||||
const mode = computed(() => SettingStore.themeConfig.mode)
|
||||
</script>
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
<template>
|
||||
<template v-if="!item.hidden">
|
||||
<template v-if="!item.alwaysShow && hasOneShowingChild(item.children, item)">
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)">
|
||||
<el-icon :size="20">
|
||||
<component :is="onlyOneChild?.meta.icon"></component>
|
||||
</el-icon>
|
||||
<template #title>{{ onlyOneChild.meta && onlyOneChild.meta.title }}</template>
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
<el-sub-menu :index="resolvePath(item.path)" v-else popper-append-to-body>
|
||||
<template #title>
|
||||
<el-icon :size="20"> <component :is="item.meta?.icon"></component></el-icon>
|
||||
<span>{{ item.meta && item.meta.title }}</span>
|
||||
</template>
|
||||
<sub-item
|
||||
v-for="child in item.children"
|
||||
:key="child.path"
|
||||
:item="child"
|
||||
:base-path="resolvePath(child.path)"
|
||||
/>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { isExternal } from '@/utils/validate.js'
|
||||
import AppLink from './Link.vue'
|
||||
import path from 'path-browserify'
|
||||
import { ref, computed } from 'vue'
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const onlyOneChild = ref(null)
|
||||
const hasOneShowingChild = (children = [], parent) => {
|
||||
const showingChildren = children.filter((item) => {
|
||||
// 过滤掉需要隐藏的菜单
|
||||
if (item.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// 临时设置(如果只有一个显示子项,则将使用)
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// 当只有一个子路由器时,默认情况下会显示该子路由器
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
// 如果没有要显示的子路由器,则显示父路由器
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const resolvePath = (routePath) => {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath
|
||||
}
|
||||
return path.resolve(props.basePath, routePath)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<template>
|
||||
<div :class="{ 'has-logo': themeConfig.showLogo }">
|
||||
<logo :isCollapse="isCollapse" v-if="themeConfig.showLogo"/>
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<u-menu/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import UMenu from './components/Menu.vue'
|
||||
import logo from './components/Logo.vue'
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import { computed } from 'vue'
|
||||
|
||||
const SettingStore = useSettingStore()
|
||||
// 是否折叠
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse)
|
||||
// 设置
|
||||
const themeConfig = computed(() =>SettingStore.themeConfig )
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.el-menu-vertical-demo:not(.el-menu--collapse) {
|
||||
//width: 200px;
|
||||
height: 100%;
|
||||
}
|
||||
.crollbar-wrapper {
|
||||
height: 100%;
|
||||
.el-scrollbar__view {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<template>
|
||||
<div ref="bsWrap" class="tags-scroll-wrap">
|
||||
<div ref="bsContent" class="tags-scroll">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import BScroll from '@better-scroll/core'
|
||||
import type { Options } from '@better-scroll/core'
|
||||
|
||||
interface Props {
|
||||
/** better-scroll的配置: https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html */
|
||||
options: Options
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const bsWrap = ref<HTMLElement>()
|
||||
const instance = ref<BScroll>()
|
||||
const bsContent = ref<HTMLElement>()
|
||||
|
||||
function initBetterScroll() {
|
||||
nextTick(() => {
|
||||
instance.value = new BScroll(bsWrap.value, props.options)
|
||||
})
|
||||
}
|
||||
|
||||
// 滚动元素发生变化,刷新BS
|
||||
const { width: wrapWidth } = useElementSize(bsWrap)
|
||||
|
||||
const { width, height } = useElementSize(bsContent)
|
||||
watch([() => wrapWidth.value, () => width.value, () => height.value], () => {
|
||||
if (instance.value) {
|
||||
instance.value.refresh()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
initBetterScroll()
|
||||
})
|
||||
|
||||
defineExpose({ instance })
|
||||
</script>
|
||||
<style scoped>
|
||||
.tags-scroll-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
.tags-scroll-inner {
|
||||
/*display: flex;*/
|
||||
}
|
||||
.tags-scroll {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
<template>
|
||||
<div class="tags-wrap-container">
|
||||
<div class="tags-view" ref="scrollContainer">
|
||||
<better-scroll :options="{ scrollX: true, scrollY: false }" ref="bsScroll">
|
||||
<div class="tags-scroll-inner">
|
||||
<div
|
||||
v-for="tag in visitedViews"
|
||||
:ref="setTagRef"
|
||||
:path="tag.path"
|
||||
:data-id="tag.path"
|
||||
:fullPath="tag.fullPath"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
:key="tag.path"
|
||||
class="item-tag-wrap"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
@click.stop="routerGo(tag)"
|
||||
>
|
||||
<div class="tags-view-item">{{ tag.title }}</div>
|
||||
<el-icon
|
||||
v-if="!isAffix(tag)"
|
||||
@click.prevent.stop="(e) => closeSelectedTag(e, tag)" class="tag-icon">
|
||||
<circle-close-filled
|
||||
/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</better-scroll>
|
||||
</div>
|
||||
<el-dropdown trigger="click">
|
||||
<div class="item-tag-wrap more">
|
||||
<div class="tags-view-item">更多 <el-icon class="el-icon--right"><arrow-down /></el-icon></div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="refresh">刷新当页</el-dropdown-item>
|
||||
<el-dropdown-item @click="closeCurrentTab">关闭当前</el-dropdown-item>
|
||||
<el-dropdown-item @click="closeOtherTab">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item @click="closeAllTab">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||||
import betterScroll from './betterScroll.vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import {useSettingStore} from "@/store/modules/setting"
|
||||
import {useTagsViewStore} from "@/store/modules/tagsView"
|
||||
import {usePermissionStore} from "@/store/modules/permission"
|
||||
|
||||
import path from 'path-browserify'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const SettingStore = useSettingStore()
|
||||
const TagsViewStore = useTagsViewStore()
|
||||
const PermissionStore = usePermissionStore()
|
||||
|
||||
const refresh = () => {
|
||||
SettingStore.setReload()
|
||||
}
|
||||
|
||||
const routes = computed(() => PermissionStore.routes)
|
||||
|
||||
const visitedViews = computed(() => TagsViewStore.visitedViews)
|
||||
|
||||
const bsScroll = ref<Expose.BetterScroll>()
|
||||
let obj = new WeakMap()
|
||||
|
||||
let affixTags = ref([])
|
||||
const tags = ref(new Map())
|
||||
// 多个ref 添加tag
|
||||
const setTagRef = (el) => {
|
||||
if (el) {
|
||||
if (!obj.get(el)) {
|
||||
tags.value.set(el.dataset['id'], el)
|
||||
}
|
||||
obj.set(el, el)
|
||||
}
|
||||
}
|
||||
|
||||
const rollPane = ref()
|
||||
const scrollContainer = ref()
|
||||
|
||||
function filterAffixTags(routes, basePath = '/') {
|
||||
let tags = []
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path)
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta },
|
||||
})
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = filterAffixTags(route.children, route.path)
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags]
|
||||
}
|
||||
}
|
||||
})
|
||||
return tags
|
||||
}
|
||||
|
||||
const initTags = () => {
|
||||
let routesNew = routes.value
|
||||
let affixTag = (affixTags.value = filterAffixTags(routesNew))
|
||||
for (const tag of affixTag) {
|
||||
if (tag.name) {
|
||||
TagsViewStore.addVisitedView(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isActive = (rou) => {
|
||||
return rou.path === route.path
|
||||
}
|
||||
const isAffix = (tag) => {
|
||||
return tag.meta && tag.meta.affix
|
||||
}
|
||||
const addTags = () => {
|
||||
const { name } = route
|
||||
if (name === 'Login') {
|
||||
return
|
||||
}
|
||||
if (name) {
|
||||
TagsViewStore.addView(route)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function toLastView(visitedViews, view) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
if (latestView) {
|
||||
router.push(latestView.fullPath)
|
||||
} else {
|
||||
if (view.name === 'home') {
|
||||
router.replace({ path: '/redirect' + view.fullPath })
|
||||
} else {
|
||||
router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const closeSelectedTag = async (event, view) => {
|
||||
if (tags.value.get(view.path)) {
|
||||
tags.value.delete(view.path)
|
||||
}
|
||||
let { visitedViews } = await TagsViewStore.delView(view)
|
||||
if (isActive(view)) {
|
||||
toLastView(visitedViews, view)
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭当前
|
||||
const closeCurrentTab = (event)=>{
|
||||
closeSelectedTag(event,route)
|
||||
}
|
||||
|
||||
// 关闭其他
|
||||
const closeOtherTab= async ()=>{
|
||||
const { name } = route
|
||||
for(let item of visitedViews.value){
|
||||
if(item.name!==name){
|
||||
await closeSelectedTag(null,item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭所有 去首页
|
||||
const closeAllTab = async ()=>{
|
||||
let visitedViews = await TagsViewStore.delAllViews()
|
||||
toLastView(visitedViews,route)
|
||||
}
|
||||
|
||||
const routerGo = (tag) => {
|
||||
router.push({
|
||||
path: tag.path,
|
||||
query: tag.query,
|
||||
})
|
||||
}
|
||||
|
||||
function handleScrollAction(currentTag) {
|
||||
const scrollContainerRect = scrollContainer.value.getBoundingClientRect()
|
||||
let { left: currx, width: currentWidth } = currentTag.getBoundingClientRect()
|
||||
const clientX = currx + currentWidth / 2
|
||||
const currentX = clientX - scrollContainerRect.left
|
||||
const deltaX = currentX - scrollContainerRect.width / 2
|
||||
if (bsScroll.value) {
|
||||
const { maxScrollX, x: leftX } = bsScroll.value.instance
|
||||
const rightX = maxScrollX - leftX
|
||||
const update = deltaX > 0 ? Math.max(-deltaX, rightX) : Math.min(-deltaX, -leftX)
|
||||
bsScroll.value?.instance.scrollBy(update, 0, 300)
|
||||
}
|
||||
}
|
||||
|
||||
function moveToCurrentTag() {
|
||||
nextTick(() => {
|
||||
for (const [key, tag] of tags.value) {
|
||||
let path = tag.attributes.path.value
|
||||
if (path === route.path) {
|
||||
let fullPath = tag.attributes.fullPath.value
|
||||
// 移动
|
||||
handleScrollAction(tag, tags.value)
|
||||
if (fullPath !== route.fullPath) {
|
||||
TagsViewStore.updateVisitedView(route)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initTags()
|
||||
addTags()
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
moveToCurrentTag()
|
||||
}, 50)
|
||||
})
|
||||
|
||||
watch(route, () => {
|
||||
addTags()
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
moveToCurrentTag()
|
||||
}, 100)
|
||||
})
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
if (
|
||||
(from.fullPath === '/error/404' || from.fullPath === '/error/401') &&
|
||||
to.fullPath === '/home'
|
||||
) {
|
||||
let whiteList = ['/error/404', '/error/401']
|
||||
await TagsViewStore.removeView(whiteList)
|
||||
}
|
||||
next()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tags-wrap-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
.tags-view {
|
||||
height: 30px;
|
||||
background: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.refresh {
|
||||
//margin-left: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.refresh-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
}
|
||||
//box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
|
||||
}
|
||||
//padding-right: 10px;
|
||||
}
|
||||
.item-tag-wrap {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
border: 1px solid #d8dce5;
|
||||
&.active .tag-icon {
|
||||
display: block;
|
||||
}
|
||||
&.active {
|
||||
background-color: $primaryColor;
|
||||
color: #fff;
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
}
|
||||
.item-tag-wrap:hover {
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
.tags-scroll-inner {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.tag-icon {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.tags-view-item {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
.tags-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.more{
|
||||
background-color: $primaryColor;
|
||||
color: white;
|
||||
.tags-view-item{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div class="footer-layout">
|
||||
<span href="/" target="_blank"> 2022 © VUE-ADMIN-PERFECT By ZB Technology. </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.footer-layout {
|
||||
height: 40px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ffffff;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
flex-shrink: 0;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div class="m-tool-left">
|
||||
<CollapseIcon />
|
||||
<Hamburger />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CollapseIcon from './components/CollapseIcon.vue'
|
||||
import Hamburger from './components/Hamburger.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.m-tool-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||