移除了主体色替换功能

路由过渡动画的配置由`@/config`移动至`@/layout/store/page.js`

使用新的上传组件替换el-upload

引入了file-saver以替换原有的文件下载代码

上传文件时key的拼接加入用户id

修复列表页自适应高度的问题

代码优化

样式修改

部分文件名称修改
master
toesbieya 6 years ago
parent 3e01d306c3
commit c44753a0ce
  1. 4
      vue/full/element-ui-personal/style/message.scss
  2. 14
      vue/full/package-lock.json
  3. 4
      vue/full/package.json
  4. 3
      vue/full/src/api/request.js
  5. 66
      vue/full/src/asset/style/cover/element-ui.scss
  6. 17
      vue/full/src/asset/style/cover/var.scss
  7. 2
      vue/full/src/asset/style/display.scss
  8. 27
      vue/full/src/asset/style/index.scss
  9. 32
      vue/full/src/asset/style/mixin.scss
  10. 2
      vue/full/src/asset/style/nprogress.scss
  11. 51
      vue/full/src/asset/style/var.scss
  12. 6
      vue/full/src/component/Empty/style.scss
  13. 2
      vue/full/src/component/LinerProgress/style.scss
  14. 42
      vue/full/src/component/RegionSelector/Tab/style.scss
  15. 6
      vue/full/src/component/TreeSelect/index.vue
  16. 309
      vue/full/src/component/Upload/PictureCard.vue
  17. 147
      vue/full/src/component/Upload/Qiniu.vue
  18. 135
      vue/full/src/component/Upload/style.scss
  19. 242
      vue/full/src/component/UploadFile/index.vue
  20. 34
      vue/full/src/component/UploadFile/style.scss
  21. 4
      vue/full/src/component/abstract/Dialog/index.vue
  22. 9
      vue/full/src/component/checkbox/ImgCheckbox.vue
  23. 11
      vue/full/src/component/form/Anchor/index.vue
  24. 6
      vue/full/src/component/form/ValidateInfo/style.scss
  25. 4
      vue/full/src/component/menu/ContextMenu/index.vue
  26. 4
      vue/full/src/component/menu/NavMenu/index.vue
  27. 2
      vue/full/src/component/menu/NavMenu/style.scss
  28. 2
      vue/full/src/component/menu/NavMenu/theme-dark.scss
  29. 2
      vue/full/src/component/menu/NavMenu/theme-light.scss
  30. 12
      vue/full/src/config/index.js
  31. 8
      vue/full/src/globalMethod/Guide/style.scss
  32. 4
      vue/full/src/globalMethod/Verify/style.scss
  33. 4
      vue/full/src/layout/component/Aside/component/MenuSearch.vue
  34. 3
      vue/full/src/layout/component/Aside/style.scss
  35. 6
      vue/full/src/layout/component/Aside/theme-dark.scss
  36. 6
      vue/full/src/layout/component/Aside/theme-light.scss
  37. 2
      vue/full/src/layout/component/Logo.vue
  38. 2
      vue/full/src/layout/component/Navbar/style.scss
  39. 2
      vue/full/src/layout/component/Navbar/theme-dark.scss
  40. 4
      vue/full/src/layout/component/Navbar/theme-light.scss
  41. 4
      vue/full/src/layout/component/Page/component/BackToTop.vue
  42. 2
      vue/full/src/layout/component/Page/component/PageFooter.vue
  43. 6
      vue/full/src/layout/component/Page/component/PageHeader.vue
  44. 8
      vue/full/src/layout/component/Page/component/TagsView/style.scss
  45. 42
      vue/full/src/layout/component/Page/index.vue
  46. 23
      vue/full/src/layout/component/Page/style.scss
  47. 34
      vue/full/src/layout/component/SettingDrawer.vue
  48. 13
      vue/full/src/layout/mixin/decideRouterTransition.js
  49. 6
      vue/full/src/layout/mixin/offline.js
  50. 2
      vue/full/src/layout/store/app.js
  51. 17
      vue/full/src/layout/store/page.js
  52. 10
      vue/full/src/layout/store/tagsView.js
  53. 4
      vue/full/src/main.js
  54. 6
      vue/full/src/mixin/docDetailMixin.js
  55. 2
      vue/full/src/plugin/webgl/fluid/index.js
  56. 2
      vue/full/src/plugin/webgl/fluid/obj.js
  57. 5
      vue/full/src/router/guardian/accessControl.js
  58. 7
      vue/full/src/router/module/dev/child/uploadFileDev.js
  59. 9
      vue/full/src/router/module/dev/index.js
  60. 105
      vue/full/src/store/module/resource.js
  61. 41
      vue/full/src/store/module/user.js
  62. 10
      vue/full/src/store/module/websocket.js
  63. 9
      vue/full/src/util/auth.js
  64. 4
      vue/full/src/util/browser.js
  65. 1
      vue/full/src/util/excel/common.js
  66. 23
      vue/full/src/util/file.js
  67. 36
      vue/full/src/view/_app/login/style.scss
  68. 23
      vue/full/src/view/_common/DetailPage/style.scss
  69. 7
      vue/full/src/view/_common/ExceptionPage/style.scss
  70. 32
      vue/full/src/view/_common/ListPage/autoAdaptHeightMixin.js
  71. 2
      vue/full/src/view/_common/SkeletonPage/style.scss
  72. 5
      vue/full/src/view/admin/index/component/PanelGroup/PanelGroupItem.vue
  73. 10
      vue/full/src/view/admin/index/component/PanelGroup/index.vue
  74. 4
      vue/full/src/view/admin/message/user/MessageStream.vue
  75. 2
      vue/full/src/view/admin/message/user/indexPage.vue
  76. 2
      vue/full/src/view/admin/purchase/inbound/detailPage.vue
  77. 2
      vue/full/src/view/admin/purchase/order/detailPage.vue
  78. 2
      vue/full/src/view/admin/sell/order/detailPage.vue
  79. 2
      vue/full/src/view/admin/sell/outbound/detailPage.vue
  80. 2
      vue/full/src/view/admin/system/menu/indexPage.vue
  81. 4
      vue/full/src/view/admin/userCenter/component/UserCard.vue
  82. 2
      vue/full/src/view/dev/index.vue
  83. 4
      vue/full/src/view/example/component/skeletonPage.vue
  84. 15
      vue/full/src/view/example/component/uploadPage.vue
  85. 4
      vue/full/src/view/example/test/develop/component/ImageCompressTest.vue
  86. 8
      vue/full/vue.config.js

@ -7,7 +7,7 @@
position: absolute; position: absolute;
box-shadow: 0 1px 3px rgba(#000, .2), 0 1px 1px rgba(#000, .14), 0 2px 1px -1px rgba(#000, .12); box-shadow: 0 1px 3px rgba(#000, .2), 0 1px 1px rgba(#000, .14), 0 2px 1px -1px rgba(#000, .12);
background-color: $--color-danger; background-color: $--color-danger;
color: #ffffff; color: $--color-white;
border-radius: 4px; border-radius: 4px;
top: -10px; top: -10px;
left: -10px; left: -10px;
@ -23,7 +23,7 @@
left: 10px; left: 10px;
right: 10px; right: 10px;
animation: el-message-progress linear; animation: el-message-progress linear;
background: currentColor; background-color: currentColor;
opacity: .3; opacity: .3;
transform-origin: 0 50%; transform-origin: 0 50%;
transform: scaleX(0); transform: scaleX(0);

@ -7552,6 +7552,11 @@
"schema-utils": "^2.5.0" "schema-utils": "^2.5.0"
} }
}, },
"file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npm.taobao.org/file-saver/download/file-saver-2.0.5.tgz?cache=0&sync_timestamp=1605790845476&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-saver%2Fdownload%2Ffile-saver-2.0.5.tgz",
"integrity": "sha1-1hz+LOBZ9BTYmendbUEH7iVnDDg="
},
"filesize": { "filesize": {
"version": "3.6.1", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@ -14275,15 +14280,6 @@
"source-map": "~0.6.1" "source-map": "~0.6.1"
} }
}, },
"webpack-theme-color-replacer": {
"version": "1.3.14",
"resolved": "https://registry.npmjs.org/webpack-theme-color-replacer/-/webpack-theme-color-replacer-1.3.14.tgz",
"integrity": "sha512-PKT/OD2+iXxGuGDxH/MVkt36EKYtYckowzPlZBdPOgGIVk8MbvLfdRFYOmWz6vhVJpyIGWtpfpoP9ecWZP8qYg==",
"dev": true,
"requires": {
"webpack-sources": "*"
}
},
"websocket-driver": { "websocket-driver": {
"version": "0.6.5", "version": "0.6.5",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",

@ -13,6 +13,7 @@
"core-js": "^3.6.5", "core-js": "^3.6.5",
"decimal.js": "^10.2.0", "decimal.js": "^10.2.0",
"element-ui": "^2.13.2", "element-ui": "^2.13.2",
"file-saver": "^2.0.5",
"js-md5": "^0.7.3", "js-md5": "^0.7.3",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -34,8 +35,7 @@
"svg-sprite-loader": "^4.3.0", "svg-sprite-loader": "^4.3.0",
"svgo": "^1.3.2", "svgo": "^1.3.2",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^3.6.1", "webpack-bundle-analyzer": "^3.6.1"
"webpack-theme-color-replacer": "^1.3.14"
}, },
"postcss": { "postcss": {
"plugins": { "plugins": {

@ -3,6 +3,7 @@ import {apiPrefix} from '@/config'
import {MessageBox, Notification} from 'element-ui' import {MessageBox, Notification} from 'element-ui'
import Message from '@ele/component/Message' import Message from '@ele/component/Message'
import store from '@/store' import store from '@/store'
import {isLogin} from '@/util/auth'
const instance = axios.create({ const instance = axios.create({
baseURL: apiPrefix, baseURL: apiPrefix,
@ -13,7 +14,7 @@ const instance = axios.create({
instance.interceptors.request.use( instance.interceptors.request.use(
config => { config => {
//登录状态下socket断连时,除登出外中断一切请求 //登录状态下socket断连时,除登出外中断一切请求
if (store.state.user.id && !store.state.socket.online && config.url !== '/account/logout') { if (isLogin() && !store.state.websocket.online && config.url !== '/account/logout') {
Message.error('请等待与服务器重新连接') Message.error('请等待与服务器重新连接')
return Promise.reject('') return Promise.reject('')
} }

@ -1,14 +1,6 @@
// cover some element-ui styles // cover some element-ui styles
.el-alert { .el-backtop {
&--info.is-light { border-radius: 0;
color: #606266;
background-color: #f0faff;
border: 1px solid #abdcff;
.el-icon-info {
color: $--color-primary;
}
}
} }
.el-button { .el-button {
@ -57,7 +49,7 @@
min-width: 384px; min-width: 384px;
.el-dialog__header { .el-dialog__header {
border-bottom: 1px solid #EBEEF5; border-bottom: 1px solid $--border-color-lighter;
padding: 17px; padding: 17px;
.el-dialog__title { .el-dialog__title {
@ -72,7 +64,7 @@
} }
.el-dialog__footer { .el-dialog__footer {
border-top: 1px solid #EBEEF5; border-top: 1px solid $--border-color-lighter;
padding: 10px 16px; padding: 10px 16px;
} }
@ -89,7 +81,7 @@
} }
.el-loading-mask { .el-loading-mask {
background: #ffffff; background-color: $--color-white;
color: $--color-primary; color: $--color-primary;
} }
@ -146,24 +138,25 @@
@include max-width-on-mobile; @include max-width-on-mobile;
border-width: 0; border-width: 0;
.el-message__icon, .el-message__content { .el-message__icon,
color: #ffffff !important; .el-message__content {
color: $--color-white !important;
} }
&.el-message--success { &.el-message--success {
background: $--color-success; background-color: $--color-success;
} }
&.el-message--info { &.el-message--info {
background: $--color-primary; background-color: $--color-primary;
} }
&.el-message--warning { &.el-message--warning {
background: $--color-warning; background-color: $--color-warning;
} }
&.el-message--error { &.el-message--error {
background: $--color-danger; background-color: $--color-danger;
} }
} }
@ -175,10 +168,6 @@
.el-notification { .el-notification {
@include deep-shadow; @include deep-shadow;
.el-notification__icon.el-icon-info {
color: $--color-primary;
}
} }
.el-pagination { .el-pagination {
@ -214,37 +203,8 @@
} }
} }
.el-table {
thead {
color: #303133
}
thead > tr > th {
background: #fbfbfb;
}
}
.el-textarea { .el-textarea {
.el-input__count { .el-input__count {
background: transparent; background-color: transparent;
}
}
.el-upload {
$size: 100px;
&-list__item-thumbnail {
object-fit: cover;
}
&.el-upload--picture-card {
height: $size;
width: $size;
line-height: $size;
}
&-list .el-upload-list__item {
height: $size;
width: $size;
} }
} }

@ -0,0 +1,17 @@
/*仅覆盖element-ui中的预设变量,不会被使用*/
//返回顶部的背景色
$--backtop-background-color: $--background-color-base;
//返回顶部hover背景色
$--backtop-hover-background-color: $--border-color-lighter;
//el-menu的hover背景颜色
$--menu-item-hover-fill: none;
//el-table的表头字体颜色
$--table-header-font-color: $--color-text-primary;
//el-table的表头背景颜色
$--table-header-background-color: #fbfbfb;
//引入element的scss时需要覆盖字体路径
$--font-path: '~element-ui/lib/theme-chalk/fonts';

@ -1,4 +1,4 @@
@media only screen and (max-width: 768px) { @media only screen and (max-width: $--sm) {
.hidden-xs { .hidden-xs {
display: none !important display: none !important
} }

@ -1,11 +1,13 @@
@import "~normalize.css/normalize.css"; @import "~normalize.css/normalize.css";
@import "./variables"; @import "./var.scss";
@import "~element-ui/packages/theme-chalk/src/index"; @import "./cover/var.scss";
@import "./mixin.scss";
@import "~element-ui/packages/theme-chalk/src/index.scss";
@import "~@ele/style/index.scss"; @import "~@ele/style/index.scss";
@import "./element-ui"; @import "./cover/element-ui.scss";
@import './display'; @import './display.scss';
@import "./transition"; @import "./transition.scss";
@import "./nprogress"; @import "./nprogress.scss";
html { html {
height: 100%; height: 100%;
@ -59,7 +61,7 @@ a:hover {
background-color: rgba($--color-primary, 0.8); background-color: rgba($--color-primary, 0.8);
&:hover { &:hover {
background-color: rgba($--color-primary, 1); background-color: $--color-primary;
} }
} }
} }
@ -169,10 +171,11 @@ $max-height: '100vh - #{$page-view-margin * 2}';
} }
} }
//仅用于演示用例
.tip-row { .tip-row {
position: relative; position: relative;
z-index: 1; z-index: 1;
background: #fafafa; background-color: #fafafa;
padding: 8px 24px; padding: 8px 24px;
margin-bottom: 20px; margin-bottom: 20px;
border-radius: 2px; border-radius: 2px;
@ -206,18 +209,18 @@ $max-height: '100vh - #{$page-view-margin * 2}';
position: relative; position: relative;
top: -1px; top: -1px;
margin-right: 11px; margin-right: 11px;
background: $--color-info; background-color: $--color-info;
&.success { &.success {
background: $--color-success; background-color: $--color-success;
} }
&.primary { &.primary {
background: $--color-primary; background-color: $--color-primary;
} }
&.error { &.error {
background: $--color-danger; background-color: $--color-danger;
} }
} }

@ -0,0 +1,32 @@
@mixin clearfix {
&::after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
@mixin deep-shadow {
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
}
@mixin mobile {
@media (max-width: $max-mobile-width) {
@content
}
}
@mixin pc {
@media (min-width: $max-mobile-width) {
@content
}
}
@mixin max-width-on-mobile {
@include mobile {
max-width: 384px;
}
}

@ -2,7 +2,7 @@
#nprogress { #nprogress {
.bar { .bar {
background: $--color-primary; background-color: $--color-primary;
.peg { .peg {
box-shadow: 0 0 10px $--color-primary, 0 0 5px $--color-primary;; box-shadow: 0 0 10px $--color-primary, 0 0 5px $--color-primary;;

@ -1,52 +1,15 @@
@import "~element-ui/packages/theme-chalk/src/common/var";
//设定移动端的最大宽度只能是number类型 //设定移动端的最大宽度只能是number类型
$max-mobile-width: 500px; $max-mobile-width: 500px;
//覆盖element颜色 //覆盖element颜色
$--color-primary: #1890ff; //这里如果有改动需要同时修改vue.config.js中ThemeColorReplacer的配置 $--color-primary: #1890ff;
$--color-success: #21BA45; $--color-success: #21BA45;
$--color-warning: #FF5722; $--color-warning: #FF5722;
$--color-danger: #F44336; $--color-danger: #F44336;
$--color-info: #009688; $--color-info: #009688;
//覆盖el-menu的hover背景颜色
$--menu-item-hover-fill: none;
//引入element的scss时需要覆盖字体路径
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@mixin clearfix {
&::after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
@mixin deep-shadow {
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
}
@mixin mobile {
@media (max-width: $max-mobile-width) {
@content
}
}
@mixin pc {
@media (min-width: $max-mobile-width) {
@content
}
}
@mixin max-width-on-mobile {
@include mobile {
max-width: 384px;
}
}
//菜单中icon的大小 //菜单中icon的大小
$menu-icon-size: 14px; $menu-icon-size: 14px;
//菜单中icon和文字的距离 //菜单中icon和文字的距离
@ -60,7 +23,7 @@ $sub-menu-background-dark: darken($menu-background-dark, 5%);
//暗色主菜单的背景色用于双层侧边栏的主菜单和顶部导航栏 //暗色主菜单的背景色用于双层侧边栏的主菜单和顶部导航栏
$root-menu-background-dark: #1F293D; $root-menu-background-dark: #1F293D;
//亮色菜单的文字颜色 //亮色菜单的文字颜色
$menu-text-color-light: #303133; $menu-text-color-light: $--color-text-primary;
//暗色菜单的文字颜色 //暗色菜单的文字颜色
$menu-text-color-dark: rgba(255, 255, 255, .65); $menu-text-color-dark: rgba(255, 255, 255, .65);
//亮色菜单的文字hover颜色 //亮色菜单的文字hover颜色
@ -100,10 +63,8 @@ $page-view-margin: 24px;
//页脚高度用于max-view-height //页脚高度用于max-view-height
$page-footer-height: 64px; $page-footer-height: 64px;
//亮色边框的颜色使用的element-ui原定义 //暗色边框的颜色用于nav-menu和侧边栏
$border-color-light: #dcdfe6; $--border-color-dark: darken($root-menu-background-dark, 5%);
//暗色边框的颜色
$border-color-dark: darken($root-menu-background-dark, 5%);
//dialog距离顶部的距离用于<el-dialog>的top属性<el-dialog>中滚动区域的最大高度 //dialog距离顶部的距离用于<el-dialog>的top属性<el-dialog>中滚动区域的最大高度
$dialog-top: 100px; $dialog-top: 100px;

@ -1,3 +1,5 @@
@import "~@/asset/style/var";
.ant-empty { .ant-empty {
padding: 20px; padding: 20px;
margin: 0 8px; margin: 0 8px;
@ -29,7 +31,7 @@
&-normal { &-normal {
margin: 32px 0; margin: 32px 0;
color: rgba(0, 0, 0, .25); color: $--color-text-secondary;
.ant-empty-image { .ant-empty-image {
height: 40px; height: 40px;
@ -38,7 +40,7 @@
&-small { &-small {
margin: 8px 0; margin: 8px 0;
color: rgba(0, 0, 0, .25); color: $--color-text-secondary;
.ant-empty-image { .ant-empty-image {
height: 35px; height: 35px;

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.liner-progress { .liner-progress {
position: relative; position: relative;

@ -1,13 +1,16 @@
.rg-header, .rg-search { @import "~@/asset/style/var";
.rg-header,
.rg-search {
padding: 2px 10px 0; padding: 2px 10px 0;
background-color: #ffffff; background-color: $--color-white;
} }
.rg-header { .rg-header {
h3 { h3 {
padding: 6px; padding: 6px;
margin: 0; margin: 0;
color: #24292e; color: $--color-text-primary;
font-size: 16px; font-size: 16px;
} }
@ -19,7 +22,7 @@
background: 0 0; background: 0 0;
border: 0; border: 0;
outline: none; outline: none;
color: #999; color: $--color-text-secondary;
top: 9px; top: 9px;
i { i {
@ -27,7 +30,7 @@
} }
&:hover { &:hover {
color: #000000; color: $--color-text-regular;
} }
&.rg-removeall-button { &.rg-removeall-button {
@ -42,13 +45,12 @@
.rg-level-tabs { .rg-level-tabs {
margin-top: 10px; margin-top: 10px;
background-color: #ffffff;
ul { ul {
padding: 0; padding: 0;
margin: 0; margin: 0;
line-height: 1.5; line-height: 1.5;
border-bottom: 1px solid #eee; border-bottom: $--border-base;
li { li {
display: inline-block; display: inline-block;
@ -58,7 +60,7 @@
display: block; display: block;
padding: 0.2rem 1rem 0.6rem; padding: 0.2rem 1rem 0.6rem;
font-size: 14px; font-size: 14px;
color: #bbb; color: $--color-text-secondary;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
line-height: 1.43; line-height: 1.43;
@ -66,8 +68,7 @@
&.active { &.active {
a { a {
color: #333; color: $--color-text-primary;
background-color: #ffffff;
font-weight: 600; font-weight: 600;
} }
@ -78,7 +79,7 @@
bottom: 0; bottom: 0;
height: 0.2rem; height: 0.2rem;
width: 100%; width: 100%;
background-color: #bbb; background-color: $--color-text-placeholder;
} }
} }
} }
@ -89,28 +90,27 @@
.rg-results { .rg-results {
margin: 0; margin: 0;
padding: 5px; padding: 5px;
width: 364px; width: 384px;
line-height: 1.5; line-height: 1.5;
li { li {
overflow: hidden; overflow: hidden;
padding: 3px 10px; padding: 3px 10px;
font-size: 14px; font-size: 14px;
color: #000000;
cursor: pointer; cursor: pointer;
&.rg-item { &.rg-item {
display: inline-block; display: inline-block;
color: #777; color: $--color-text-secondary;
&:hover { &:hover:not(.active) {
color: #000000; color: $--color-text-primary;
background-color: #F5F5F5; background-color: rgba($--color-text-placeholder,.2);
} }
&.active { &.active {
background-color: #E4EAEE; color: $--color-text-primary;
color: #000000; background-color: rgba($--color-text-placeholder,.6);
} }
} }
@ -125,7 +125,7 @@
} }
.rg-input { .rg-input {
background-color: #f5f5f5; background-color: rgba($--color-text-placeholder,.2);
border: 0; border: 0;
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
@ -134,6 +134,6 @@
border-radius: 2px; border-radius: 2px;
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
color: #999; color: $--color-text-placeholder;
} }
} }

@ -199,7 +199,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.tree-select-container { .tree-select-container {
padding: 8px; padding: 8px;
@ -209,8 +209,8 @@ export default {
} }
.el-tree-node__label.is-disabled { .el-tree-node__label.is-disabled {
background-color: #edf2fc; background-color: $--font-color-disabled-base;
border-color: $border-color-light; border-color: $--border-color-base;
cursor: not-allowed; cursor: not-allowed;
} }
} }

@ -0,0 +1,309 @@
<template>
<div :class="{'upload-file': true, 'is-disabled': disabled}">
<div
v-for="file in fileList"
:key="file.id"
:class="`upload-file-item is-${file.status}`"
:title="file.name"
>
<img :src="file.thumbnailUrl">
<div v-if="file.status === 'uploading'" class="upload-file-item-loading">
<i class="el-icon-loading"/>
<p>上传中</p>
</div>
<template v-else>
<div class="upload-file-item-action">
<template v-if="file.status === 'success'">
<i class="el-icon-zoom-in" title="预览" @click="handlePreview(file)"/>
<i class="el-icon-download" title="下载" @click="handleDownload(file)"/>
</template>
<i v-if="!disabled" class="el-icon-delete" title="删除" @click="handleRemove(file)"/>
</div>
<label :class="`upload-file-item-status-label`">
<i :class="`el-icon-${file.status==='success'?'check':'close'}`"/>
</label>
</template>
</div>
<div v-if="!hideTrigger" class="upload-file-trigger" @click="handleTriggerClick">
<i class="el-icon-plus"></i>
<input ref="input" type="file" :multiple="multiple" :accept="accept" @change="handleInputChange">
</div>
</div>
</template>
<script>
import ajax from 'element-ui/packages/upload/src/ajax'
export default {
name: "UploadPictureCard",
props: {
//
action: String,
//file
headers: [Object, Function],
//file
data: [Object, Function],
//cookie
withCredentials: Boolean,
//
name: {type: String, default: 'file'},
//
httpRequest: {type: Function, default: ajax},
//
multiple: Boolean,
//inputaccept
accept: String,
//
uploadedFileList: {type: Array, default: () => []},
//
disabled: Boolean,
//
hideTriggerOnDisabled: {type: Boolean, default: true},
//<1
limit: {type: Number, default: 0},
//
hideTriggerOnExceed: {type: Boolean, default: true},
//
autoDelete: {type: Boolean, default: true},
//1NumberBStringKBMB
maxSize: {type: [Number, String], default: 0},
// false Promise reject
beforeUpload: Function,
// false Promise reject
beforeRemove: Function
},
data() {
//id
this.tempId = 1
//<file.id, XHR>
this.requestMap = {}
return {
//
fileList: []
}
},
computed: {
needLimit() {
return this.limit >= 1
},
hideTrigger() {
return this.disabled && this.hideTriggerOnDisabled
|| this.needLimit && this.fileList.length >= this.limit && this.hideTriggerOnExceed
}
},
watch: {
uploadedFileList: {
immediate: true,
handler(arr) {
//
Object.entries(this.requestMap).forEach(([id, xhr]) => {
if (arr.every(file => file.id !== id)) {
//abort
xhr.abort && xhr.abort()
delete this.requestMap[id]
}
})
this.fileList = arr.map(item => ({
...item,
id: item.id || this.tempId++,
status: item.status || 'success',
thumbnailUrl: item.thumbnailUrl || item.url,
downloadUrl: item.thumbnailUrl || item.url
}))
}
}
},
methods: {
parseMaxSize(maxSize) {
if (typeof maxSize === 'number') {
return maxSize
}
const map = {KB: 1024, MB: 1024 * 1024},
upper = maxSize.toUpperCase(),
num = upper.replace(/[^0-9]/ig, ""),
unit = upper.replace(num, "")
return parseFloat(num) * (map[unit] || 1)
},
//
handleTriggerClick() {
if (this.disabled) return
this.$refs.input.value = null
this.$refs.input.click()
},
//input
handleInputChange(e) {
const files = e.target.files
if (!files || files.length === 0) return
if (this.needLimit && this.fileList.length + files.length > this.limit) {
return this.$emit('exceed')
}
files.forEach(this.upload)
},
//
handlePreview(file) {
this.$emit('preview', file)
},
//
handleDownload(file) {
this.$emit('download', file)
},
//
handleRemove(file) {
this.remove(file)
},
//
upload(rawFile) {
//
const maxSize = this.parseMaxSize(this.maxSize)
if (maxSize >= 1 && rawFile.size > maxSize) {
return this.$emit('over-max-size', rawFile)
}
rawFile.id = this.tempId++
const file = {
id: rawFile.id,
name: rawFile.name,
thumbnailUrl: rawFile.type.startsWith('image') && window.URL.createObjectURL(rawFile),
status: 'uploading',
percentage: 0,
raw: rawFile
}
this.fileList.push(file)
if (!this.beforeUpload) {
return this.sendRequest(file)
}
const result = this.beforeUpload(file)
if (!result) {
return this.removeFile(file)
}
result.then
? result.then(() => this.sendRequest(file), () => this.removeFile(file))
: this.sendRequest(file)
},
sendRequest(file) {
const {id, raw} = file
const headers = typeof this.headers === 'function' ? this.headers(file) : this.headers
const data = typeof this.data === 'function' ? this.data(file) : this.data
const options = {
headers,
data,
withCredentials: this.withCredentials,
file: raw,
filename: this.name,
action: this.action,
onProgress: e => {
this.onProgress(e, file)
},
onSuccess: res => {
this.onSuccess(res, file)
delete this.requestMap[id]
},
onError: err => {
this.onError(err, file)
delete this.requestMap[id]
}
}
const req = this.httpRequest(options)
this.requestMap[id] = req
req && req.then && req.then(options.onSuccess, options.onError)
},
abortRequest(file) {
const map = this.requestMap
if (file) {
const req = map[file.id]
req && req.abort && req.abort()
delete map[file.id]
return
}
Object.entries(map).forEach(([id, req]) => {
req && req.abort && req.abort()
delete map[id]
})
},
onProgress(e, file) {
file.status = 'uploading'
file.percentage = e.percent || 0
},
onSuccess(res, file) {
file.status = 'success'
file.response = res
this.$emit('success', res, file)
},
onError(err, file) {
file.status = 'error'
if (!file.response) file.response = {}
file.response.err = err
this.autoDelete && this.removeFile(file)
this.$emit('error', err, file)
},
//
remove(file) {
if (!this.beforeRemove) {
return this.removeFile(file)
}
const result = this.beforeRemove(file)
if (!result) return
result.then
? result.then(() => this.removeFile(file), () => ({}))
: this.removeFile(file)
},
removeFile(file) {
this.revokeBlob(file)
this.fileList.splice(this.fileList.indexOf(file), 1)
this.$emit('remove', file)
},
revokeBlob({thumbnailUrl, downloadUrl}) {
if (thumbnailUrl && thumbnailUrl.startsWith('blob:')) {
window.URL.revokeObjectURL(thumbnailUrl)
}
if (downloadUrl && downloadUrl.startsWith('blob:')) {
window.URL.revokeObjectURL(downloadUrl)
}
}
},
beforeDestroy() {
//
this.abortRequest()
//
this.fileList.forEach(this.revokeBlob)
}
}
</script>
<style lang="scss" src="./style.scss"></style>

@ -0,0 +1,147 @@
<template>
<upload-picture-card
ref="upload"
:uploaded-file-list="uploadedFileList"
:limit="limit"
:multiple="multiple"
:http-request="httpRequest"
:before-upload="beforeUpload"
@exceed="onExceed"
@over-max-size="onOverMaxSize"
@success="onSuccess"
@error="onError"
@preview="onPreview"
@download="onDownload"
@remove="onRemove"
/>
</template>
<script>
/*
* 直传七牛云
* 传入fileList自动拼接七牛云外链前缀
* 非图片类型的文件使用kkFileView预览
* successremove事件中的file地址不带七牛云外链前缀
* */
import UploadPictureCard from "./PictureCard"
import axios from 'axios'
import {deleteUpload} from '@/api/file'
import {file as fileConfig} from '@/config'
import {elError} from "@/util/message"
import {isImage, isDoc, isPdf, isPpt, isRar, isXls, isTxt, isZip} from "@/util/validate"
import {preview, download, upload, autoCompleteUrl} from "@/util/file"
const typeMapper = [
{test: isImage}, {test: isDoc, type: 'doc'}, {test: isPdf, type: 'pdf'}, {test: isPpt, type: 'ppt'},
{test: isRar, type: 'rar'}, {test: isXls, type: 'xls'}, {test: isTxt, type: 'txt'}, {test: isZip, type: 'zip'},
]
function getCoverImage(url, fileType) {
const mapper = typeMapper.find(({test}) => test(fileType))
return mapper && mapper.type ? `${process.env.BASE_URL}static/img/fileType/${mapper.type}.png` : url
}
export default {
name: "QiniuUpload",
components: {UploadPictureCard},
props: {
disabled: Boolean,
multiple: {type: Boolean, default: true},
fileList: {type: Array, default: () => []},
limit: {type: Number, default: 10},
maxSize: {type: [Number, String], default: '10MB'}
},
computed: {
//{url, name}{thumbnailUrl, downloadUrl, name}
uploadedFileList() {
return this.fileList.map(file => {
const downloadUrl = autoCompleteUrl(file.url)
return {...file, thumbnailUrl: getCoverImage(downloadUrl, file.name), downloadUrl}
})
}
},
methods: {
//
onExceed() {
elError(`最多只能上传${this.limit}个文件`)
},
//
onOverMaxSize(file) {
const maxSize = this.$refs.upload.parseMaxSize(file.size)
elError(`${file.name}的大小超出${this.$options.filters.number2StorageUnit(maxSize)}`)
},
//successkey
onSuccess(res, file) {
file.raw.key = res.key
file.downloadUrl = autoCompleteUrl(res.key)
this.$emit('success', file, res)
},
//
onError(err) {
elError('上传文件失败:' + err)
},
//
onPreview(file) {
if (!isImage(file.name)) return preview(file.downloadUrl)
const fileList = this.$refs.upload.fileList
const index = fileList.indexOf(file)
const previewUrlList = fileList.filter(i => isImage(i.name)).map(i => i.downloadUrl)
this.$image({index, urlList: previewUrlList})
},
//
onDownload(file) {
download(file.downloadUrl, file.name)
},
//
onRemove(file) {
//remove
if (!file.raw) {
this.$emit('remove', {url: file.downloadUrl.replace(fileConfig.storePrefix, '')})
}
//OSS
else if (file.response && !file.response.err) {
deleteUpload.request(file.raw.key)
}
},
//
beforeUpload(file) {
if (!typeMapper.some(({test}) => test(file.name))) {
elError('只支持图片、word、pdf、ppt、rar、excel、txt、zip类型的文件')
return false
}
const url = getCoverImage(file.thumbnailUrl, file.name)
//
if (url !== file.thumbnailUrl) {
file.thumbnailUrl = url
}
return true
},
//
httpRequest({file, onProgress}) {
const source = axios.CancelToken.source()
const promise = upload(file, file.name, {
onUploadProgress(e) {
if (e.total > 0) {
e.percent = (Number)((e.loaded / e.total * 100).toFixed(2))
}
onProgress(e)
},
cancelToken: source.token
})
//abort
promise.abort = source.cancel
return promise
}
}
}
</script>

@ -0,0 +1,135 @@
@import "~@/asset/style/var";
$--upload-size: 90px;
.upload-file {
display: inline-block;
&-item,
&-trigger {
height: $--upload-size;
width: $--upload-size;
float: left;
border-radius: 4px;
}
&-item {
position: relative;
margin: 0 8px 8px 0;
border: 1px solid $--border-color-base;
overflow: hidden;
> img {
height: 100%;
width: 100%;
object-fit: cover;
}
&-action,
&-loading {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .6);
}
&-action {
display: flex;
justify-content: space-around;
align-items: center;
opacity: 0;
> i {
color: $--color-white;
cursor: pointer;
transition: transform .1s;
&:hover {
transform: scale(1.2);
}
}
}
&-loading {
padding-top: ($--upload-size - $--font-size-small * 2 - $--font-size-extra-large) / 2;
text-align: center;
color: $--color-white;
> i {
font-size: $--font-size-extra-large;
}
> p {
font-size: $--font-size-small;
}
}
&-status-label {
position: absolute;
right: -15px;
top: -6px;
width: 40px;
height: 24px;
text-align: center;
transform: rotate(45deg);
> i {
font-size: 12px;
color: $--color-white;
margin-top: 11px;
transform: rotate(-45deg);
}
}
&.is-success > &-status-label {
background-color: $--color-success;
}
&.is-error > &-status-label {
background-color: $--color-danger;
}
&:hover {
> .upload-file-item-action {
opacity: 1;
}
> .upload-file-item-status-label {
display: none;
}
}
}
&-trigger {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px dashed $--border-color-base;
cursor: pointer;
background-color: $--background-color-base;
> i {
font-size: 20px;
color: $--color-text-regular;
}
> input {
display: none;
}
&:hover {
border-color: $--color-primary;
}
}
&.is-disabled > &-trigger {
border-color: $--disabled-border-base;
cursor: not-allowed;
&:hover {
border-color: $--disabled-border-base;
}
}
}

@ -1,242 +0,0 @@
<template>
<el-upload
ref="upload"
:before-upload="beforeUpload"
:class="{disabled: hideUploader}"
:file-list="tempData"
:http-request="httpRequest"
:limit="limit"
:multiple="multiple"
:on-error="error"
:on-exceed="exceed"
:on-success="success"
action=""
list-type="picture-card"
>
<i class="el-icon-plus"/>
<template v-slot:file="{file}">
<span
class="el-upload-list__item-actions"
@mouseenter="e => handleBlockMouseEnter(e,file)"
@mouseleave="handleBlockMouseLeave"
>
<span class="el-upload-list__item-preview" @click="() => preview(file)">
<i class="el-icon-zoom-in"/>
</span>
<span class="el-upload-list__item-delete" @click="() => download(file)">
<i class="el-icon-download"/>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="() => remove(file)">
<i class="el-icon-delete"/>
</span>
</span>
<template v-if="file.status === 'success'">
<img :src="file.url" class="el-upload-list__item-thumbnail">
<label class="el-upload-list__item-status-label">
<i class="el-icon-upload-success el-icon-check"/>
</label>
</template>
<div v-if="file.status === 'uploading'" class="progress-mask">
<el-progress :percentage="file.percentage" type="circle"/>
</div>
</template>
<el-tooltip ref="tooltip" :content="tooltipContent" popper-class="upload-tooltip"/>
</el-upload>
</template>
<script>
/*
* 直传七牛云
* 传入fileList自动拼接七牛云外链前缀并增加downloadUrl属性图片类型的文件url与downloadUrl相同
* 非图片类型的文件使用kkFileView预览
* successremove事件中的file.url均不带七牛云外链前缀
* */
import axios from 'axios'
import {deleteUpload} from '@/api/file'
import {file as fileConfig} from '@/config'
import {debounce, isEmpty, deepClone} from '@/util'
import {elError} from "@/util/message"
import {isImage, isDoc, isPdf, isPpt, isRar, isXls, isTxt, isZip} from "@/util/validate"
import {preview, download, upload, autoCompleteUrl} from "@/util/file"
const typeMapper = [
{test: isImage}, {test: isDoc, type: 'doc'}, {test: isPdf, type: 'pdf'}, {test: isPpt, type: 'ppt'},
{test: isRar, type: 'rar'}, {test: isXls, type: 'xls'}, {test: isTxt, type: 'txt'}, {test: isZip, type: 'zip'},
]
function parseMaxSize(maxSize) {
if (typeof maxSize === 'number') {
return maxSize
}
const map = {KB: 1024, MB: 1024 * 1024},
upper = maxSize.toUpperCase(),
num = upper.replace(/[^0-9]/ig, ""),
unit = upper.replace(num, "")
return parseInt(num) * (map[unit] || 1)
}
function getCoverImage(url, fileType) {
const mapper = typeMapper.find(({test}) => test(fileType))
return mapper && mapper.type ? `${process.env.BASE_URL}static/img/fileType/${mapper.type}.png` : url
}
export default {
name: 'UploadFile',
props: {
disabled: Boolean,
multiple: {type: Boolean, default: true},
fileList: {type: Array, default: () => []},
limit: {type: Number, default: 10},
maxSize: {type: [Number, String], default: '10MB'}
},
data() {
return {
tempData: this.fileList,
data: this.fileList,
tooltipContent: null
}
},
computed: {
count() {
return this.data.length
},
hideUploader() {
return this.disabled || this.count >= this.limit
},
previewUrlList() {
return this.data ? this.data.map(i => i.downloadUrl) : []
}
},
watch: {
fileList: {
immediate: true,
handler(fileList) {
if (isEmpty(fileList)) this.data = []
else {
this.data = [...this.fileList.map(file => {
const downloadUrl = autoCompleteUrl(file.url)
return {...file, url: getCoverImage(downloadUrl, file.name), downloadUrl}
})]
}
this.tempData = deepClone(this.data)
}
}
},
methods: {
//仿el-tabletooltip
handleBlockMouseEnter(event, file) {
const tooltip = this.$refs.tooltip
this.tooltipContent = file.name
tooltip.referenceElm = event.target
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none')
tooltip.doDestroy()
tooltip.setExpectedState(true)
this.activateTooltip(tooltip)
},
handleBlockMouseLeave() {
const tooltip = this.$refs.tooltip
if (tooltip) {
tooltip.setExpectedState(false)
tooltip.handleClosePopper()
}
},
//remove
remove(file) {
//tooltip
this.handleBlockMouseLeave()
//
//
if (!file.raw) this.$emit('remove', {url: file.downloadUrl.replace(fileConfig.storePrefix, '')})
//
else if (file.response && !file.response.err) deleteUpload.request(file.raw.key)
this.$refs.upload.handleRemove(file)
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl)
if (index > -1) this.data.splice(index, 1)
},
//
preview(file) {
if (!isImage(file.name)) return preview(file.downloadUrl)
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl)
this.$image({index, urlList: this.previewUrlList})
},
//
download(file) {
download(file.downloadUrl, file.name)
},
//
exceed() {
elError(`最多只能上传${this.limit}个文件`)
},
//successkey
success(res, file) {
file.url = getCoverImage(file.url, file.name)
file.raw.key = res.key
file.downloadUrl = autoCompleteUrl(res.key)
this.data.push(file)
this.$emit('success', file, res)
},
//
error(err, file) {
elError('上传文件失败,' + err)
file.response.err = err
this.remove(file)
},
//
beforeUpload(file) {
if (!typeMapper.some(({test}) => test(file.name))) {
elError('只支持图片、word、pdf、ppt、rar、excel、txt、zip类型的文件')
return false
}
const maxSize = parseMaxSize(this.maxSize)
if (file.size > maxSize) {
elError(`${file.name}的大小超出${this.$options.filters.number2StorageUnit(maxSize)}`)
return false
}
},
httpRequest({file, onProgress}) {
const source = axios.CancelToken.source()
const promise = upload(file, file.name, {
onUploadProgress(e) {
if (e.total > 0) {
e.percent = (Number)((e.loaded / e.total * 100).toFixed(2))
}
onProgress(e)
},
cancelToken: source.token
})
//eleajaxabort
promise.abort = source.cancel
return promise
}
},
created() {
this.activateTooltip = debounce(tooltip => tooltip.handleShowPopper(), 200)
}
}
</script>
<style lang="scss" src="./style.scss"></style>

@ -1,34 +0,0 @@
@import "~@/asset/style/variables.scss";
.disabled .el-upload--picture-card {
display: none;
}
.el-upload-list--picture-card {
.el-progress {
width: 100%;
height: 100%;
.el-progress-circle {
height: 100% !important;
width: 100% !important;
}
}
.progress-mask {
position: absolute;
top: 0;
height: 100%;
width: 100%;
background-color: #ffffff;
opacity: 0.9;
.el-progress__text {
color: $--color-primary;
}
}
}
.upload-tooltip {
max-width: 146px;
}

@ -1,5 +1,5 @@
<script type="text/jsx"> <script type="text/jsx">
import cssVariables from '@/asset/style/variables.scss' import cssVar from '@/asset/style/var.scss'
import LoadingMask from './LoadingMask' import LoadingMask from './LoadingMask'
export default { export default {
@ -25,7 +25,7 @@ export default {
v-drag-dialog v-drag-dialog
visible={value} visible={value}
custom-class="abstract-dialog" custom-class="abstract-dialog"
top={cssVariables.dialogTop} top={cssVar.dialogTop}
width={width} width={width}
on-close={onClose} on-close={onClose}
{...data} {...data}

@ -6,11 +6,14 @@ export default {
mixins: [common], mixins: [common],
props: ['img'], props: {
img: String,
label: String
},
render() { render() {
return ( return (
<div class="img-checkbox" on-click={this.click}> <div class="img-checkbox" title={this.label} on-click={this.click}>
<img src={this.img}/> <img src={this.img}/>
{this.checked && <i class="el-icon-check"/>} {this.checked && <i class="el-icon-check"/>}
</div> </div>
@ -20,7 +23,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.img-checkbox { .img-checkbox {
width: 55px; width: 55px;

@ -122,7 +122,8 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/mixin.scss";
@import "~@/asset/style/var";
.form-anchor { .form-anchor {
position: fixed; position: fixed;
@ -130,8 +131,8 @@ export default {
top: 200px; top: 200px;
min-width: 72px; min-width: 72px;
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
background: #ffffff; background-color: $--background-color-base;
box-shadow: 0 2px 12px 0 rgba(166, 167, 173, 0.5); @include deep-shadow;
z-index: 100; z-index: 100;
&-item { &-item {
@ -139,7 +140,7 @@ export default {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
text-align: center; text-align: center;
color: #909399; color: $--color-text-secondary;
padding: 0 10px; padding: 0 10px;
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
@ -150,7 +151,7 @@ export default {
} }
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px solid rgba(166, 167, 173, 0.1); border-bottom: $--border-base;
} }
} }
} }

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.form-validate-info { .form-validate-info {
&.el-popper { &.el-popper {
@ -27,7 +27,7 @@
&-item { &-item {
padding: 8px 16px; padding: 8px 16px;
border-top: 1px solid $border-color-light; border-top: 1px solid $--border-color-base;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@ -43,7 +43,7 @@
&-label { &-label {
margin-top: 2px; margin-top: 2px;
color: #909399; color: $--color-text-secondary;
font-size: 12px; font-size: 12px;
} }
} }

@ -86,11 +86,11 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.context-menu { .context-menu {
margin: 0; margin: 0;
background: $menu-background-dark; background-color: $menu-background-dark;
z-index: 10; z-index: 10;
position: fixed; position: fixed;
list-style-type: none; list-style-type: none;

@ -1,9 +1,9 @@
<script type="text/jsx"> <script type="text/jsx">
import cssVariables from '@/asset/style/variables.scss' import cssVar from '@/asset/style/var.scss'
import Item from './item' import Item from './item'
import {isEmpty, deepClone, trim} from "@/util" import {isEmpty, deepClone, trim} from "@/util"
const inlineIndent = parseFloat(cssVariables.menuPadding) const inlineIndent = parseFloat(cssVar.menuPadding)
export default { export default {
name: 'NavMenu', name: 'NavMenu',

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.el-menu { .el-menu {
//图标的大小 //图标的大小

@ -28,7 +28,7 @@
//设置了显示弹出菜单的父级 //设置了显示弹出菜单的父级
&.el-menu--popup .popover-menu__title { &.el-menu--popup .popover-menu__title {
color: $menu-text-hover-color-dark; color: $menu-text-hover-color-dark;
border-bottom-color: $border-color-dark; border-bottom-color: $--border-color-dark;
} }
} }
} }

@ -16,7 +16,7 @@
//弹出菜单的父级 //弹出菜单的父级
&.el-menu--popup .popover-menu__title { &.el-menu--popup .popover-menu__title {
color: $menu-text-hover-color-light; color: $menu-text-hover-color-light;
border-bottom-color: $border-color-light; border-bottom-color: $--border-color-light;
} }
} }
} }

@ -24,17 +24,7 @@ module.exports = {
mode: 'history', mode: 'history',
//是否使用由后端返回的数据作为路由 //是否使用由后端返回的数据作为路由
useBackendDataAsRoute: false, useBackendDataAsRoute: false
//路由过渡动画设置,关联transition.css
animate: {
//当未启用多页签时的路由动画
default: 'el-fade-in-linear',
//要访问的tab顺序高于上一个访问的tab时的路由动画
next: 'left-out',
//要访问的tab顺序不高于上一个访问的tab时的路由动画
prev: 'right-out',
}
}, },
//文件配置 //文件配置

@ -44,16 +44,16 @@
#guide-stage { #guide-stage {
position: absolute; position: absolute;
background: white; background-color: white;
transition: all 0.3s; transition: all 0.3s;
z-index: 1001 !important; z-index: 1001 !important;
border-radius: 2px; border-radius: 2px;
/*background: transparent !important; /*background-color: transparent !important;
outline: 5000px solid rgba(0, 0, 0, 0.75);*/ outline: 5000px solid rgba(0, 0, 0, 0.75);*/
} }
#guide-overlay { #guide-overlay {
background: black; background-color: black;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@ -65,6 +65,6 @@
#guide-highlight-element-cover { #guide-highlight-element-cover {
position: absolute; position: absolute;
background: transparent !important; background-color: transparent !important;
z-index: 1003 !important; z-index: 1003 !important;
} }

@ -1,11 +1,11 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.puzzle-container { .puzzle-container {
position: absolute; position: absolute;
z-index: 1000; z-index: 1000;
box-shadow: 0 0 10px #cccccc; box-shadow: 0 0 10px #cccccc;
border: 1px solid #cccccc; border: 1px solid #cccccc;
background: #ffffff; background-color: #ffffff;
.puzzle-text-container { .puzzle-text-container {
&:hover { &:hover {

@ -36,7 +36,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
// //
.collapse .aside-search { .collapse .aside-search {
@ -50,7 +50,7 @@ export default {
// //
.dark .aside-search .el-input__inner { .dark .aside-search .el-input__inner {
border: none; border: none;
color: #ffffff; color: $--color-white;
background-color: lighten($menu-background-dark, 5%); background-color: lighten($menu-background-dark, 5%);
} }
</style> </style>

@ -1,4 +1,5 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
@import "~@/asset/style/mixin.scss";
.aside { .aside {
display: flex; display: flex;

@ -10,15 +10,15 @@
} }
.root-sidebar { .root-sidebar {
border-right-color: $border-color-dark; border-right-color: $--border-color-dark;
} }
.sub-sidebar-title { .sub-sidebar-title {
color: #ffffff; color: $--color-white;
} }
.hamburger { .hamburger {
border-top-color: $border-color-dark; border-top-color: $--border-color-dark;
i { i {
color: $menu-text-color-dark; color: $menu-text-color-dark;

@ -1,11 +1,11 @@
.aside.light { .aside.light {
.root-sidebar { .root-sidebar {
background-color: #ffffff; background-color: $--color-white;
border-right-color: $border-color-light; border-right-color: $--border-color-light;
} }
.hamburger { .hamburger {
border-top-color: $border-color-light; border-top-color: $--border-color-light;
i { i {
color: $menu-text-color-light; color: $menu-text-color-light;

@ -26,7 +26,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
$logoSize: 30px; $logoSize: 30px;

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.navbar { .navbar {
display: flex; display: flex;

@ -5,7 +5,7 @@
color: $navbar-item-color-dark; color: $navbar-item-color-dark;
&:hover { &:hover {
background: $navbar-item-hover-color-dark; background-color: $navbar-item-hover-color-dark;
} }
} }
} }

@ -1,11 +1,11 @@
.navbar.light { .navbar.light {
background-color: #ffffff; background-color: $--color-white;
.navbar-item { .navbar-item {
color: $navbar-item-color-light; color: $navbar-item-color-light;
&:hover { &:hover {
background: $navbar-item-hover-color-light; background-color: $navbar-item-hover-color-light;
} }
} }
} }

@ -5,9 +5,7 @@ export default {
render() { render() {
return ( return (
<el-backtop target=".page-main .scroll-container" visibility-height={400} bottom={66}> <el-backtop target=".page-main .scroll-container" visibility-height={400} bottom={66}>
<div class="back-to-top"> <v-icon icon="el-icon-top"/>
<v-icon icon="el-icon-top"/>
</div>
</el-backtop> </el-backtop>
) )
} }

@ -13,7 +13,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.page-footer { .page-footer {
height: $page-footer-height; height: $page-footer-height;

@ -32,7 +32,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.page-header { .page-header {
display: flex; display: flex;
@ -44,12 +44,12 @@ export default {
padding: $page-header-padding $page-view-margin $page-header-padding $page-view-margin; padding: $page-header-padding $page-view-margin $page-header-padding $page-view-margin;
> .el-breadcrumb .no-redirect { > .el-breadcrumb .no-redirect {
color: #909399; color: $--color-text-secondary;
cursor: text; cursor: text;
} }
&-title { &-title {
color: #303133; color: $--color-text-primary;
font-weight: 600; font-weight: 600;
font-size: 18px; font-size: 18px;
overflow: hidden; overflow: hidden;

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.tags-view-container { .tags-view-container {
height: $tags-view-height; height: $tags-view-height;
@ -21,10 +21,10 @@
cursor: pointer; cursor: pointer;
height: #{$tags-view-height - $margin * 2}; height: #{$tags-view-height - $margin * 2};
line-height: #{$tags-view-height - $margin * 2 - $border-width * 2}; line-height: #{$tags-view-height - $margin * 2 - $border-width * 2};
color: #909399; color: $--color-text-secondary;
background-color: #ffffff; background-color: #ffffff;
border-radius: 3px; border-radius: 3px;
border: $border-width solid $border-color-light; border: $border-width solid $--border-color-base;
padding: 0 12px; padding: 0 12px;
font-size: 12px; font-size: 12px;
margin: $margin; margin: $margin;
@ -54,7 +54,7 @@
color: $--color-primary; color: $--color-primary;
&::before { &::before {
background: $--color-primary; background-color: $--color-primary;
} }
} }

@ -26,8 +26,8 @@ export default {
}, },
render() { render() {
const {showIframe, iframeList, currentIframe} = pageGetters const {transition, showIframe, iframeList, currentIframe} = pageGetters
const {cachedViews, transitionName, enabled} = tagsViewGetters const {cachedViews, enabled} = tagsViewGetters
return ( return (
<main class="page-main"> <main class="page-main">
@ -38,7 +38,7 @@ export default {
<page-view <page-view
include={cachedViews} include={cachedViews}
transition-name={transitionName} transition-name={transition.curr}
cacheable={enabled} cacheable={enabled}
/> />
@ -64,38 +64,4 @@ export default {
} }
</script> </script>
<style lang="scss"> <style lang="scss" src="./style.scss"></style>
@import "~@/asset/style/variables.scss";
.page-main {
display: flex;
flex-direction: column;
flex: 1;
position: relative;
overflow: hidden;
background-color: #f0f2f5;
> .scroll-container {
display: flex;
flex: 1;
flex-direction: column;
overflow-y: overlay;
overflow-x: inherit;
> .page-view {
margin: $page-view-margin;
flex: 1;
}
}
.back-to-top {
height: 100%;
width: 100%;
background-color: #f2f5f6;
box-shadow: 0 0 6px rgba(0, 0, 0, .12);
text-align: center;
line-height: 40px;
color: #1989fa;
}
}
</style>

@ -0,0 +1,23 @@
@import "~@/asset/style/var";
.page-main {
display: flex;
flex-direction: column;
flex: 1;
position: relative;
overflow: hidden;
background-color: #f0f2f5;
> .scroll-container {
display: flex;
flex: 1;
flex-direction: column;
overflow-y: overlay;
overflow-x: inherit;
> .page-view {
margin: $page-view-margin;
flex: 1;
}
}
}

@ -7,20 +7,13 @@
@close="$emit('input', false)" @close="$emit('input', false)"
> >
<div class="drawer-container"> <div class="drawer-container">
<el-divider>主题色</el-divider>
<div class="drawer-item">
<checkbox-group :value="appGetters.color" @input="changeThemeColor">
<color-checkbox v-for="i in colors" :key="i" :value="i"/>
</checkbox-group>
</div>
<el-divider>导航模式</el-divider> <el-divider>导航模式</el-divider>
<div class="drawer-item"> <div class="drawer-item">
<checkbox-group :value="appGetters.navMode" @input="changeNavMode"> <checkbox-group :value="appGetters.navMode" @input="changeNavMode">
<img-checkbox <img-checkbox
v-for="{title, value, img} in navModes" v-for="{label, value, img} in navModes"
:key="value" :key="value"
:title="title" :label="label"
:value="value" :value="value"
:img="img" :img="img"
/> />
@ -131,8 +124,6 @@
</template> </template>
<script> <script>
import client from 'webpack-theme-color-replacer/client'
import forElementUI from 'webpack-theme-color-replacer/forElementUI'
import CheckboxGroup from "@/component/checkbox/Group" import CheckboxGroup from "@/component/checkbox/Group"
import ColorCheckbox from "@/component/checkbox/ColorCheckbox" import ColorCheckbox from "@/component/checkbox/ColorCheckbox"
import ImgCheckbox from "@/component/checkbox/ImgCheckbox" import ImgCheckbox from "@/component/checkbox/ImgCheckbox"
@ -155,25 +146,24 @@ export default {
return { return {
appGetters, asideGetters, navbarGetters, pageGetters, tagsViewGetters, appGetters, asideGetters, navbarGetters, pageGetters, tagsViewGetters,
colors: ['#f5222d', '#fa541c', '#fadb14', '#3eaf7c', '#13c2c2', '#1890ff', '#722ed1', '#eb2f96'],
navModes: [ navModes: [
{ {
title: '侧边栏导航', label: '侧边栏导航',
value: 'aside', value: 'aside',
img: 'https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg' img: 'https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg'
}, },
{ {
title: '顶部导航', label: '顶部导航',
value: 'head', value: 'head',
img: 'https://gw.alipayobjects.com/zos/antfincdn/URETY8%24STp/KDNDBbriJhLwuqMoxcAr.svg' img: 'https://gw.alipayobjects.com/zos/antfincdn/URETY8%24STp/KDNDBbriJhLwuqMoxcAr.svg'
}, },
{ {
title: '混合导航', label: '混合导航',
value: 'mix', value: 'mix',
img: 'https://gw.alipayobjects.com/zos/antfincdn/x8Ob%26B8cy8/LCkqqYNmvBEbokSDscrm.svg' img: 'https://gw.alipayobjects.com/zos/antfincdn/x8Ob%26B8cy8/LCkqqYNmvBEbokSDscrm.svg'
}, },
{ {
title: '双层侧边栏导航', label: '双层侧边栏导航',
value: 'aside-two-part', value: 'aside-two-part',
img: 'https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg' img: 'https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg'
} }
@ -181,7 +171,6 @@ export default {
setting: { setting: {
app: { app: {
color: '#1890ff',
navMode: 'mix' navMode: 'mix'
}, },
page: { page: {
@ -238,10 +227,6 @@ export default {
setLocalPersonalSettings(this.setting) setLocalPersonalSettings(this.setting)
}, },
changeThemeColor(v) {
client.changer.changeColor({newColors: forElementUI.getElementUISeries(v)})
appMutations.color(v)
},
changeNavMode(v) { changeNavMode(v) {
appMutations.navMode(v) appMutations.navMode(v)
}, },
@ -315,14 +300,13 @@ export default {
setLocalPersonalSettings(this.setting) setLocalPersonalSettings(this.setting)
this.syncLayoutStore() this.syncLayoutStore()
//
this.changeThemeColor(this.setting.app.color)
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/asset/style/var";
.drawer-container { .drawer-container {
padding: 24px; padding: 24px;
font-size: 14px; font-size: 14px;
@ -332,7 +316,7 @@ export default {
.drawer-item { .drawer-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
color: #606266; color: $--color-text-regular;
font-size: 14px; font-size: 14px;
padding: 12px 0; padding: 12px 0;
} }

@ -1,7 +1,4 @@
import {route as routeConfig} from '@/config' import {getters as pageGetters, mutations as pageViewMutations} from "@/layout/store/page"
import {mutations as tagsViewMutations} from "@/layout/store/tagsView"
const {animate} = routeConfig
export default { export default {
watch: { watch: {
@ -13,7 +10,9 @@ export default {
methods: { methods: {
//根据访问的tab页的左右顺序来确定路由动画 //根据访问的tab页的左右顺序来确定路由动画
decideRouteTransition(to, from) { decideRouteTransition(to, from) {
let transitionName = animate.prev const {next, prev} = pageGetters.transition
let transitionName = prev
//这里认为页签数量不会太多,所以为了可读性使用两次循环查找 //这里认为页签数量不会太多,所以为了可读性使用两次循环查找
const fromIndex = this.visitedViews.findIndex(i => i.path === from.path) const fromIndex = this.visitedViews.findIndex(i => i.path === from.path)
@ -21,10 +20,10 @@ export default {
//新开tab也认为顺序高于上一个tab //新开tab也认为顺序高于上一个tab
if (toIndex === -1 || fromIndex < toIndex) { if (toIndex === -1 || fromIndex < toIndex) {
transitionName = animate.next transitionName = next
} }
tagsViewMutations.transitionName(transitionName) pageViewMutations.transition({curr: transitionName})
}, },
} }
} }

@ -1,11 +1,9 @@
import {isEmpty} from "@/util" import {isLogin} from "@/util/auth"
export default { export default {
computed: { computed: {
showOfflineTip() { showOfflineTip() {
const state = this.$store.state return !this.$store.state.websocket.online && isLogin()
const isLogin = !isEmpty(state.user.id, state.user.token)
return !state.socket.online && isLogin
}, },
}, },

@ -17,8 +17,6 @@ const state = {
//所有的树形菜单,每个元素为顶部菜单,顶部菜单的子级(如果有)为侧边栏菜单 //所有的树形菜单,每个元素为顶部菜单,顶部菜单的子级(如果有)为侧边栏菜单
menus: [], menus: [],
//主题色
color: '#1890ff',
//导航模式,'aside'、'aside-two-part'、'head'、'mix' //导航模式,'aside'、'aside-two-part'、'head'、'mix'
navMode: 'mix' navMode: 'mix'
} }

@ -6,6 +6,18 @@ import {bindThis} from "@/util"
import {createGetters, createMutations} from "@/util/observable" import {createGetters, createMutations} from "@/util/observable"
const state = { const state = {
//路由过渡动画
transition: {
//当未启用多页签时的路由动画
default: 'el-fade-in-linear',
//要访问的tab顺序高于上一个访问的tab时的路由动画
next: 'left-out',
//要访问的tab顺序不高于上一个访问的tab时的路由动画
prev: 'right-out',
//当前使用的路由动画
curr: 'el-fade-in-linear'
},
/*iframe的控制*/ /*iframe的控制*/
showIframe: false, showIframe: false,
currentIframe: '', currentIframe: '',
@ -28,6 +40,11 @@ export const getters = createGetters(store)
export const mutations = bindThis({ export const mutations = bindThis({
...createMutations(store), ...createMutations(store),
//修改transition时使用Object.assign
transition(obj) {
Object.assign(store.transition, obj)
},
addIframe(src) { addIframe(src) {
!store.iframeList.includes(src) && store.iframeList.push(src) !store.iframeList.includes(src) && store.iframeList.push(src)
}, },

@ -2,8 +2,7 @@
* 多页签的响应式数据 * 多页签的响应式数据
*/ */
import Vue from 'vue' import Vue from 'vue'
import {route as routeConfig} from "@/config" import {getters as pageGetters, mutations as pageMutations} from "@/layout/store/page"
import {mutations as pageMutations} from "@/layout/store/page"
import {getRouterViewCacheKey} from "@/layout/util" import {getRouterViewCacheKey} from "@/layout/util"
import {bindThis} from "@/util" import {bindThis} from "@/util"
import {createGetters, createMutations} from "@/util/observable" import {createGetters, createMutations} from "@/util/observable"
@ -20,10 +19,7 @@ const state = {
visitedViews: [], visitedViews: [],
//缓存的页签,用于<keep-router-view-alive/>:include //缓存的页签,用于<keep-router-view-alive/>:include
cachedViews: [], cachedViews: []
//路由过渡动画名称
transitionName: routeConfig.animate.default
} }
const store = Vue.observable(state) const store = Vue.observable(state)
@ -42,7 +38,7 @@ export const mutations = bindThis({
store.enabled = v store.enabled = v
if (!v) { if (!v) {
this.transitionName(routeConfig.animate.default) pageMutations.transition({curr: pageGetters.transition.default})
this.delAllTagAndCache() this.delAllTagAndCache()
} }
}, },

@ -24,5 +24,5 @@ new Vue({
render: h => h(App) render: h => h(App)
}) })
//页面刷新时socket重连 //页面刷新时websocket重连
store.dispatch('socket/init').catch() store.dispatch('websocket/init').catch()

@ -6,7 +6,7 @@ import CollapseCard from '@/component/CollapseCard'
import DetailPage from "@/view/_common/DetailPage" import DetailPage from "@/view/_common/DetailPage"
import DocHistory from '@/component/biz/doc/DocHistory' import DocHistory from '@/component/biz/doc/DocHistory'
import DocSteps from '@/component/biz/doc/DocSteps' import DocSteps from '@/component/biz/doc/DocSteps'
import UploadFile from '@/component/UploadFile' import QiniuUpload from '@/component/Upload/Qiniu'
import {isEmpty, mergeObj} from '@/util' import {isEmpty, mergeObj} from '@/util'
import {auth} from "@/util/auth" import {auth} from "@/util/auth"
import {deleteUpload} from "@/api/file" import {deleteUpload} from "@/api/file"
@ -22,7 +22,7 @@ export default {
DetailPage, DetailPage,
DocHistory, DocHistory,
DocSteps, DocSteps,
UploadFile QiniuUpload
}, },
props: { props: {
@ -268,7 +268,7 @@ export default {
url: res.key, url: res.key,
name: file.name, name: file.name,
sort: this.attachmentSortSeed, sort: this.attachmentSortSeed,
size: file.size size: file.raw.size
}) })
this.attachmentSortSeed++ this.attachmentSortSeed++
}, },

@ -496,7 +496,7 @@ const WebGLFluid = canvas => {
let captureCanvas = textureToCanvas(texture, target.width, target.height) let captureCanvas = textureToCanvas(texture, target.width, target.height)
let dataUri = captureCanvas.toDataURL() let dataUri = captureCanvas.toDataURL()
downloadURI('fluid.png', dataUri) downloadURI('fluid.png', dataUri)
URL.revokeObjectURL(dataUri) window.URL.revokeObjectURL(dataUri)
} }
//startGUI(); //startGUI();

@ -542,6 +542,6 @@ export default class WebGlFluid {
const dataUri = textureToCanvas(texture, target.width, target.height).toDataURL() const dataUri = textureToCanvas(texture, target.width, target.height).toDataURL()
downloadURI('fluid.png', dataUri) downloadURI('fluid.png', dataUri)
URL.revokeObjectURL(dataUri) window.URL.revokeObjectURL(dataUri)
} }
} }

@ -1,6 +1,5 @@
import store from "@/store" import store from "@/store"
import {isEmpty} from "@/util" import {isLogin, auth, needAuth} from "@/util/auth"
import {auth, needAuth} from "@/util/auth"
const noLoginList = ['/login', '/register', '/403', '/404', '/500'] const noLoginList = ['/login', '/register', '/403', '/404', '/500']
@ -11,7 +10,7 @@ const beforeEach = async (to, from, next) => {
} }
//未登录时返回登录页 //未登录时返回登录页
if (isEmpty(store.state.user.id, store.state.user.token)) { if (!isLogin()) {
return next({path: '/login', query: {redirect: to.fullPath}}) return next({path: '/login', query: {redirect: to.fullPath}})
} }

@ -1,7 +0,0 @@
import component from '@/view/dev/uploadFileDev'
export default {
path: 'uploadFile',
component,
meta: {title: '上传'}
}

@ -1,11 +1,8 @@
/*顶级菜单:开发用的页面*/ /*顶级菜单:开发用的页面*/
import {context2array} from '@/router/util' import component from "@/view/dev"
const files = require.context('./child', false, /\.js$/)
export default { export default {
path: '/dev', path: '/dev',
component: {template: `<div><div>开发专用页面</div><router-view/></div>`}, component,
meta: {title: '开发专用页面', icon: 'svg-develop', noAuth: true, noCache: true, alwaysShow: true}, meta: {title: '开发专用页面', icon: 'svg-develop', noAuth: true, noCache: true, alwaysShow: true}
children: context2array(files)
} }

@ -6,12 +6,12 @@ import {getDynamicRoutes} from '@/router/define'
import {str2routeConfig, metaExtend} from "@/router/util" import {str2routeConfig, metaExtend} from "@/router/util"
import {getAll} from "@/api/system/resource" import {getAll} from "@/api/system/resource"
import {isEmpty, deepClone} from "@/util" import {isEmpty, deepClone} from "@/util"
import {needAuth, auth} from "@/util/auth" // 此处存在循环引用? import {needAuth, auth} from "@/util/auth" //此处存在循环引用?
import {createTree, shakeTree} from "@/util/tree" import {createTree, shakeTree} from "@/util/tree"
import {isExternal} from "@/util/validate" import {isExternal} from "@/util/validate"
const state = { const state = {
//权限映射表:<权限路径,权限id>,用于util.auth //权限映射表:<权限路径,权限id>,所有的权限数据,用于util.auth
resourceMap: {}, resourceMap: {},
//权限树(当用户非admin时过滤了admin专属权限),用于菜单管理、角色管理中的权限选择 //权限树(当用户非admin时过滤了admin专属权限),用于菜单管理、角色管理中的权限选择
resourceTree: [], resourceTree: [],
@ -21,13 +21,13 @@ const state = {
} }
const mutations = { const mutations = {
resource(state, {data, admin}) { resource(state, {data, predicate}) {
data = deepClone(data) data = deepClone(data)
state.resourceMap = generateResourceMap(data) state.resourceMap = generateResourceMap(data)
//如果不是admin用户,那么过滤admin类型的权限 //如果传入了过滤方法,那么过滤权限树
if (!admin) data = data.filter(i => !i.admin) if (predicate) data = data.filter(predicate)
//解析meta,这里不处理meta.dynamicTitle //解析meta,这里不处理meta.dynamicTitle
data.forEach(i => { data.forEach(i => {
@ -44,20 +44,30 @@ const mutations = {
} }
const actions = { const actions = {
init({state, commit}, {admin}) { init({state, commit, rootState}) {
const user = rootState.user
return getAll return getAll
.request() .request()
.then(({data}) => { .then(({data}) => {
commit('resource', {data: data || [], admin}) //如果不是admin用户,那么过滤admin类型的权限
const predicate = user.admin ? undefined : i => !i.admin
commit('resource', {data: data || [], predicate})
//未开启后端动态路由功能时,使用前端预设的静态路由
const routes =
routeConfig.useBackendDataAsRoute
? transformOriginRouteData(data)
: getDynamicRoutes()
const routes = transformOriginRouteData(data)
metaExtend(routes) metaExtend(routes)
//动态添加路由,这里不需要进行权限过滤 //动态添加路由,这里不需要进行权限过滤
//可能存在多次调用的情况,所以仅在第一次调用时添加进vue-router //可能存在多次调用的情况,所以仅在第一次调用时添加进vue-router
!state.init && addDynamicRoutes(routes) !state.init && addDynamicRoutes(routes)
//生成经过权限过滤后的菜单 //生成经过权限过滤后的菜单,并传递给layout的store
appMutations.menus(getAuthorizedMenus(routes)) appMutations.menus(getAuthorizedMenus(routes))
//设置初始化完成的标志 //设置初始化完成的标志
@ -70,9 +80,6 @@ const actions = {
function transformOriginRouteData(data) { function transformOriginRouteData(data) {
if (!Array.isArray(data)) return [] if (!Array.isArray(data)) return []
//未开启后端动态路由功能时,返回前端预设的静态路由
if (!routeConfig.useBackendDataAsRoute) return getDynamicRoutes()
const treeArray = deepClone(data).filter(i => { const treeArray = deepClone(data).filter(i => {
//过滤掉数据接口或者未启用的项 //过滤掉数据接口或者未启用的项
if (i.type === 3 || !i.enable) { if (i.type === 3 || !i.enable) {
@ -93,54 +100,56 @@ function transformOriginRouteData(data) {
//获取经过权限控制后的菜单 //获取经过权限控制后的菜单
function getAuthorizedMenus(menus) { function getAuthorizedMenus(menus) {
menus = deepClone(menus) menus = deepClone(menus)
clean(menus)
addFullPath(menus)
return shakeTree(menus, i => {
//非叶子节点时,当定义了children属性却没有子节点时移除
if (i.children && i.children.length === 0) {
return false
}
return !needAuth(i) || auth(i.fullPath) //删除不显示的菜单(没有children且没有meta.title)
}) function clean(menus) {
} for (let i = menus.length - 1; i >= 0; i--) {
const {children = [], meta: {title, alwaysShow, hidden} = {}} = menus[i]
//删除不显示的菜单(没有children且没有meta.title) if (hidden) {
function clean(menus) { menus.splice(i, 1)
for (let i = menus.length - 1; i >= 0; i--) { continue
const {children = [], meta: {title, alwaysShow, hidden} = {}} = menus[i] }
if (hidden) { if (children.length === 0) {
menus.splice(i, 1) if (isEmpty(title)) {
continue menus.splice(i, 1)
} }
continue
}
if (children.length === 0) { clean(children)
if (isEmpty(title)) {
if (children.length === 0 && !alwaysShow) {
menus.splice(i, 1) menus.splice(i, 1)
} }
continue
//移除类似首页那样的无标题父级节点
if (isEmpty(title)) {
menus.splice(i, 1, ...children)
}
} }
}
clean(children) //菜单添加全路径
function addFullPath(routes, basePath = '/') {
routes.forEach(route => {
//外链保持原样
route.fullPath = isExternal(route.path) ? route.path : path.resolve(basePath, route.path)
route.children && addFullPath(route.children, route.fullPath)
})
}
if (children.length === 0 && !alwaysShow) { clean(menus)
menus.splice(i, 1) addFullPath(menus)
}
//移除类似首页那样的无标题父级节点 return shakeTree(menus, i => {
if (isEmpty(title)) { //非叶子节点时,当定义了children属性却没有子节点时移除
menus.splice(i, 1, ...children) if (i.children && i.children.length === 0) {
return false
} }
}
}
//菜单添加全路径 return !needAuth(i) || auth(i.fullPath)
function addFullPath(routes, basePath = '/') {
routes.forEach(route => {
//外链保持原样
route.fullPath = isExternal(route.path) ? route.path : path.resolve(basePath, route.path)
route.children && addFullPath(route.children, route.fullPath)
}) })
} }

@ -1,6 +1,6 @@
import {createMutations} from "@/store/util" import {createMutations} from "@/store/util"
import {emptyOrDefault} from "@/util" import {emptyOrDefault} from "@/util"
import {autoCompleteUrl} from "@/util/file" import {autoCompleteUrl} from "@/util/file" //此处存在循环引用?
import {getUser, setUser} from "@/util/storage" import {getUser, setUser} from "@/util/storage"
import {mutations as tagsViewMutations} from "@/layout/store/tagsView" import {mutations as tagsViewMutations} from "@/layout/store/tagsView"
import {login, logout} from '@/api/account' import {login, logout} from '@/api/account'
@ -33,35 +33,32 @@ const actions = {
.then(({data: user}) => { .then(({data: user}) => {
if (user.admin === true) user.roleName = '超级管理员' if (user.admin === true) user.roleName = '超级管理员'
user.avatar = autoCompleteUrl(user.avatar) user.avatar = autoCompleteUrl(user.avatar)
commit('$all', user) commit('$all', user)
setUser(user) setUser(user)
return dispatch('socket/init', null, {root: true})
return dispatch('websocket/init', undefined, {root: true})
}) })
}, },
logout({commit, state, dispatch}) { logout({commit, state, dispatch}) {
return new Promise((resolve, reject) => { if (state.prepareLogout) return Promise.reject()
if (state.prepareLogout) return Promise.reject()
commit('prepareLogout', true) commit('prepareLogout', true)
logout
.request(state.token) return logout
.then(() => { .request(state.token)
commit('resource/init', false, {root: true}) .then(() => Promise.all([
return Promise.all([ dispatch('websocket/close', null, {root: true}),
dispatch('socket/close', null, {root: true}), dispatch('removeUser'),
dispatch('removeUser'), //因为可能开启了页签的持久化功能,所以退出登陆时通过清空store来清空持久化数据
tagsViewMutations.delAllTagAndCache() tagsViewMutations.delAllTagAndCache()
]) ]))
}) .then(() => window.location.reload())
.then(() => { .finally(() => commit('prepareLogout', false))
resolve()
window.location.reload()
})
.catch(error => reject(error))
.finally(() => commit('prepareLogout', false))
})
}, },
//刷新本地存储中保存的用户数据
refresh({state}) { refresh({state}) {
setUser(state) setUser(state)
}, },

@ -4,7 +4,7 @@ import {useMock, socketUrl} from '@/config'
import SocketIO from 'socket.io-client' import SocketIO from 'socket.io-client'
import {createMutations} from "@/store/util" import {createMutations} from "@/store/util"
let socket let websocket
const state = { const state = {
online: true online: true
@ -18,14 +18,14 @@ const actions = {
if (useMock || isEmpty(id, token)) return if (useMock || isEmpty(id, token)) return
socket = initSocket({id, token}) websocket = initSocket({id, token})
bindDefaultEvent(socket, context) bindDefaultEvent(websocket, context)
bindCustomEvent(socket, context) bindCustomEvent(websocket, context)
}, },
close() { close() {
socket && socket.close() websocket && websocket.close()
} }
} }

@ -53,3 +53,12 @@ export function wic(apiMap) {
return attrs return attrs
} }
/**
* 判断是否已登录已登录则返回true
*
* @return {boolean}
*/
export function isLogin() {
return !isEmpty(store.state.user.token)
}

@ -1,6 +1,6 @@
import cssVariables from '@/asset/style/variables.scss' import cssVar from '@/asset/style/var.scss'
const maxMobileWidth = parseFloat(cssVariables.maxMobileWidth) const maxMobileWidth = parseFloat(cssVar.maxMobileWidth)
/** /**
* 根据body宽度判断是否为移动端是则返回true * 根据body宽度判断是否为移动端是则返回true

@ -1,3 +1,4 @@
import request from "@/api/request" import request from "@/api/request"
import {isEmpty} from "@/util" import {isEmpty} from "@/util"
import {elError} from "@/util/message" import {elError} from "@/util/message"

@ -1,13 +1,15 @@
import {saveAs} from 'file-saver'
import request from "@/api/request" import request from "@/api/request"
import {getToken} from "@/api/file" import {getToken} from "@/api/file"
import {file as fileConfig} from '@/config' import {file as fileConfig} from '@/config'
import store from '@/store'
import {isEmpty, timeFormat} from "@/util" import {isEmpty, timeFormat} from "@/util"
import {isTxt} from "@/util/validate" import {isExternal, isTxt} from "@/util/validate"
const defaultOptions = { const defaultOptions = {
headers: {"Content-Type": "multipart/form-data"}, headers: {"Content-Type": "multipart/form-data"},
generateKey(filename) { generateKey(filename) {
return timeFormat('yyyy/MM/dd/') + Date.now() + '/' + filename return `${timeFormat('yyyy/MM/dd')}/user${store.state.user.id}/${Date.now()}/${filename}`
}, },
onUploadProgress(e) { onUploadProgress(e) {
@ -20,8 +22,9 @@ export function preview(url) {
const connectChar = url.includes('?') ? '&' : '?' const connectChar = url.includes('?') ? '&' : '?'
url = url + connectChar + 'fullfilename=' + url.replace(fileConfig.storePrefix, '') url = url + connectChar + 'fullfilename=' + url.replace(fileConfig.storePrefix, '')
const anchor = document.createElement('a') const anchor = document.createElement('a')
anchor.style.opacity = '0' anchor.style.display = 'none'
anchor.href = `${fileConfig.previewPrefix}/onlinePreview?url=${encodeURIComponent(url)}` anchor.href = `${fileConfig.previewPrefix}/onlinePreview?url=${encodeURIComponent(url)}`
anchor.target = '_blank' anchor.target = '_blank'
document.body.appendChild(anchor) document.body.appendChild(anchor)
@ -31,17 +34,7 @@ export function preview(url) {
//下载文件 //下载文件
export function download(url, name) { export function download(url, name) {
const href = typeof url === 'object' ? window.URL.createObjectURL(url) : url saveAs(url, name)
const anchor = document.createElement('a')
anchor.style.opacity = '0'
anchor.href = href
anchor.download = name
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
if (typeof url === 'object') window.URL.revokeObjectURL(url)
} }
//七牛云直传 //七牛云直传
@ -63,6 +56,6 @@ export function upload(blob, filename, options = {}) {
//自动补全附件链接前缀 //自动补全附件链接前缀
export function autoCompleteUrl(url) { export function autoCompleteUrl(url) {
if (isEmpty(url)) return '' if (isEmpty(url)) return ''
if (url.startsWith('http')) return url if (isExternal(url)) return url
return fileConfig.storePrefix + url return fileConfig.storePrefix + url
} }

@ -1,17 +1,8 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
$bg: #283443;
$light_gray: #ffffff;
$dark_gray: #889aa4;
$cursor: #ffffff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
.login-page { .login-page {
$background-color: #2d3a4b;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
@ -22,7 +13,7 @@ $cursor: #ffffff;
height: 100%; height: 100%;
width: 100%; width: 100%;
z-index: -1; z-index: -1;
background: #2d3a4b; background-color: $background-color;
} }
.login-container { .login-container {
@ -38,12 +29,11 @@ $cursor: #ffffff;
margin-bottom: 40px; margin-bottom: 40px;
cursor: pointer; cursor: pointer;
font-size: 26px; font-size: 26px;
color: #eee; color: #ffffff;
font-weight: bold; font-weight: bold;
position: relative; position: relative;
.set-animation { .set-animation {
color: #ffffff;
position: absolute; position: absolute;
top: 115px; top: 115px;
font-size: 18px; font-size: 18px;
@ -54,7 +44,7 @@ $cursor: #ffffff;
.svg-container { .svg-container {
padding: 6px 5px 6px 0; padding: 6px 5px 6px 0;
color: $dark_gray; color: $--color-text-secondary;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
} }
@ -64,7 +54,7 @@ $cursor: #ffffff;
right: 10px; right: 10px;
top: 7px; top: 7px;
font-size: 16px; font-size: 16px;
color: $dark_gray; color: $--color-text-secondary;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
} }
@ -75,26 +65,20 @@ $cursor: #ffffff;
width: 85%; width: 85%;
input { input {
background: transparent; background-color: transparent;
border: 0; border: 0;
-webkit-appearance: none; -webkit-appearance: none;
border-radius: 0; border-radius: 0;
padding: 12px 5px 12px 15px; padding: 12px 5px 12px 15px;
color: $light_gray; color: #ffffff;
height: 47px; height: 47px;
caret-color: $cursor;
&:-webkit-autofill {
box-shadow: 0 0 0 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
}
} }
} }
.el-form-item { .el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 5px; border-radius: 5px;
background: rgba(0, 0, 0, 0.1); background-color: rgba(0, 0, 0, 0.1);
margin-bottom: 22px; margin-bottom: 22px;
} }

@ -1,4 +1,5 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
@import "~@/asset/style/mixin.scss";
.detail-page { .detail-page {
&.el-loading-parent--relative { &.el-loading-parent--relative {
@ -8,8 +9,8 @@
&-header { &-header {
margin: -#{$page-view-margin} -#{$page-view-margin} 0 -#{$page-view-margin}; margin: -#{$page-view-margin} -#{$page-view-margin} 0 -#{$page-view-margin};
padding: 16px 32px 0 32px; padding: 16px 32px 0 32px;
background: #ffffff; background-color: $--color-white;
border-bottom: 1px solid $border-color-light; border-bottom: $--border-base;
> .el-page-header { > .el-page-header {
padding: 0 0 24px 0; padding: 0 0 24px 0;
@ -42,15 +43,15 @@
min-width: 242px; min-width: 242px;
margin-left: 88px; margin-left: 88px;
@media (max-width: 1200px) { @media (max-width: $--lg) {
margin-left: 44px; margin-left: 44px;
} }
@media (max-width: 992px) { @media (max-width: $--md) {
margin-left: 20px; margin-left: 20px;
} }
@media (max-width: 768px) { @media (max-width: $--sm) {
margin-left: 0; margin-left: 0;
} }
@ -62,13 +63,13 @@
&-title { &-title {
margin-bottom: 4px; margin-bottom: 4px;
color: rgba(0, 0, 0, .45); color: $--color-text-secondary;
font-size: 14px; font-size: 14px;
} }
&-content { &-content {
margin-top: 12px; margin-top: 12px;
color: rgba(0, 0, 0, .85); color: $--color-text-primary;
font-size: 24px; font-size: 24px;
} }
} }
@ -80,7 +81,7 @@
} }
&-label { &-label {
color: rgba(0, 0, 0, .85); color: $--color-text-primary;
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 1.5715; line-height: 1.5715;
@ -88,7 +89,7 @@
} }
&-content { &-content {
color: rgba(0, 0, 0, .65); color: $--color-text-regular;
font-size: 14px; font-size: 14px;
line-height: 1.5715; line-height: 1.5715;
} }
@ -110,7 +111,7 @@
height: 56px; height: 56px;
padding: 0 24px; padding: 0 24px;
line-height: 56px; line-height: 56px;
border-top: 1px solid #f0f0f0; border-top: $--border-base;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);
backdrop-filter: blur(3px); backdrop-filter: blur(3px);

@ -1,4 +1,5 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
@import "~@/asset/style/mixin.scss";
.exception { .exception {
width: 100%; width: 100%;
@ -16,7 +17,7 @@
.content { .content {
h1 { h1 {
color: #434e59; color: $--color-text-primary;
font-size: 72px; font-size: 72px;
font-weight: 600; font-weight: 600;
line-height: 72px; line-height: 72px;
@ -24,7 +25,7 @@
} }
.desc { .desc {
color: rgba(0, 0, 0, .45); color: $--color-text-secondary;
font-size: 20px; font-size: 20px;
line-height: 28px; line-height: 28px;
margin-bottom: 16px; margin-bottom: 16px;

@ -3,6 +3,7 @@
* 监听el-table动态设置max-height属性 * 监听el-table动态设置max-height属性
*/ */
import {debounce} from "@/util"
import {findComponentByTag} from "@/util/vue" import {findComponentByTag} from "@/util/vue"
export default { export default {
@ -19,7 +20,7 @@ export default {
}, },
methods: { methods: {
//设置表格距离卡片容器底部的理想距离(分页高度 + el-card的1px边距 + el-card padding + 页面margin //设置表格距离卡片容器底部的理想距离(分页高度 + el-card的1px边距 + el-card padding)
calcExpectedDistance() { calcExpectedDistance() {
const paginationHeight = this.$el.querySelector('.table-container > .el-pagination') ? 50 : 0 const paginationHeight = this.$el.querySelector('.table-container > .el-pagination') ? 50 : 0
this.expectedDistance = paginationHeight + 1 + 20 this.expectedDistance = paginationHeight + 1 + 20
@ -51,24 +52,21 @@ export default {
this.tableMaxHeight = tableRect.height - overHeight this.tableMaxHeight = tableRect.height - overHeight
} }
}, },
createResizeObserver() {
if (this.resizeObserver) return
this.resizeObserver = new ResizeObserver(this.resize)
this.resizeObserver.observe(this.$el)
const searchFormInstance = findComponentByTag(this, 'search-form')
if (searchFormInstance) {
this.resizeObserver.observe(searchFormInstance.$el)
}
this.$once('hook:beforeDestroy', () => {
this.resizeObserver && this.resizeObserver.disconnect()
})
},
}, },
mounted() { mounted() {
this.createResizeObserver() //resize必须异步,否则新版chrome(87.0.4280.88)会出问题
this.resize = debounce(this.resize, 0)
this.resizeObserver = new ResizeObserver(this.resize)
this.resizeObserver.observe(this.$el)
const searchFormInstance = findComponentByTag(this, 'search-form')
if (searchFormInstance) {
this.resizeObserver.observe(searchFormInstance.$el)
}
this.$once('hook:beforeDestroy', () => {
this.resizeObserver && this.resizeObserver.disconnect()
})
} }
} }

@ -1,4 +1,4 @@
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.skeleton-page-wrapper { .skeleton-page-wrapper {
margin: $page-view-margin; margin: $page-view-margin;

@ -31,7 +31,8 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
@import "~@/asset/style/mixin.scss";
.card-panel { .card-panel {
height: 108px; height: 108px;
@ -40,7 +41,7 @@ export default {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
color: #666; color: #666;
background: #ffffff; background-color: #ffffff;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
border-color: rgba(0, 0, 0, .05); border-color: rgba(0, 0, 0, .05);

@ -8,7 +8,7 @@
<script> <script>
import PanelGroupItem from './PanelGroupItem' import PanelGroupItem from './PanelGroupItem'
import cssVariables from '@/asset/style/variables.scss' import cssVar from '@/asset/style/var.scss'
import {getFourBlock} from '@/api/statistic' import {getFourBlock} from '@/api/statistic'
import {isEmpty} from "@/util" import {isEmpty} from "@/util"
import {auth} from "@/util/auth" import {auth} from "@/util/auth"
@ -26,7 +26,7 @@ export default {
id: 'online', id: 'online',
path: '/system/user', path: '/system/user',
icon: 'svg-user', icon: 'svg-user',
color: cssVariables.info, color: cssVar.info,
value: 0, value: 0,
text: '在线用户' text: '在线用户'
}, },
@ -34,7 +34,7 @@ export default {
id: 'purchase', id: 'purchase',
path: '/purchase/order', path: '/purchase/order',
icon: 'svg-shopping', icon: 'svg-shopping',
color: cssVariables.primary, color: cssVar.primary,
value: 0, value: 0,
text: '今日采购额' text: '今日采购额'
}, },
@ -42,14 +42,14 @@ export default {
id: 'sell', id: 'sell',
path: '/sell/order', path: '/sell/order',
icon: 'svg-sell', icon: 'svg-sell',
color: cssVariables.warning, color: cssVar.warning,
value: 0, value: 0,
text: '今日销售额' text: '今日销售额'
}, },
{ {
id: 'profit', id: 'profit',
icon: 'svg-money', icon: 'svg-money',
color: cssVariables.success, color: cssVar.success,
value: 0, value: 0,
text: '今日毛利润' text: '今日毛利润'
}, },

@ -153,7 +153,7 @@ export default {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
color: #666; color: #666;
background: #FFF9EA; background-color: #FFF9EA;
& + & { & + & {
margin-top: 20px; margin-top: 20px;
@ -161,7 +161,7 @@ export default {
&.viewed { &.viewed {
cursor: auto; cursor: auto;
background: transparent; background-color: transparent;
&::before { &::before {
visibility: hidden; visibility: hidden;

@ -43,7 +43,7 @@ export default {
text-align: center; text-align: center;
&.is-active { &.is-active {
background: #f0faff; background-color: #f0faff;
} }
} }

@ -44,7 +44,7 @@
</collapse-card> </collapse-card>
<collapse-card ref="attachment" header="附件"> <collapse-card ref="attachment" header="附件">
<upload-file <qiniu-upload
:file-list="form.imageList" :file-list="form.imageList"
:disabled="!canSave" :disabled="!canSave"
@remove="removeUpload" @remove="removeUpload"

@ -90,7 +90,7 @@
</collapse-card> </collapse-card>
<collapse-card ref="attachment" header="附件"> <collapse-card ref="attachment" header="附件">
<upload-file <qiniu-upload
:file-list="form.imageList" :file-list="form.imageList"
:disabled="!canSave" :disabled="!canSave"
@remove="removeUpload" @remove="removeUpload"

@ -73,7 +73,7 @@
</collapse-card> </collapse-card>
<collapse-card ref="attachment" header="附件"> <collapse-card ref="attachment" header="附件">
<upload-file <qiniu-upload
:file-list="form.imageList" :file-list="form.imageList"
:disabled="!canSave" :disabled="!canSave"
@remove="removeUpload" @remove="removeUpload"

@ -59,7 +59,7 @@
</collapse-card> </collapse-card>
<collapse-card ref="attachment" header="附件"> <collapse-card ref="attachment" header="附件">
<upload-file <qiniu-upload
:file-list="form.imageList" :file-list="form.imageList"
:disabled="!canSave" :disabled="!canSave"
@remove="removeUpload" @remove="removeUpload"

@ -320,7 +320,7 @@ export default {
init() { init() {
this.curNode = null this.curNode = null
this.clearForm() this.clearForm()
return this.$store.dispatch('resource/init', this.$store.state.user) return this.$store.dispatch('resource/init')
}, },
clearForm() { clearForm() {
resetObj(this.form) resetObj(this.form)

@ -53,7 +53,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.user-card { .user-card {
.avatar-container { .avatar-container {
@ -81,7 +81,7 @@ export default {
list-style: none; list-style: none;
li { li {
border-bottom: 1px solid #EBEEF5; border-bottom: 1px solid $--border-color-lighter;
padding: 11px 0; padding: 11px 0;
font-size: 13px; font-size: 13px;
} }

@ -4,6 +4,6 @@
<script> <script>
export default { export default {
name: "uploadFileDev" name: "dev"
} }
</script> </script>

@ -40,14 +40,14 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/var";
.q-skeleton + .q-skeleton { .q-skeleton + .q-skeleton {
margin-top: 20px; margin-top: 20px;
} }
.bg-primary { .bg-primary {
background: $--color-primary; background-color: $--color-primary;
} }
.bg-info { .bg-info {

@ -1,22 +1,25 @@
<template> <template>
<div> <div>
<div class="tip-row"> <div class="tip-row">
根据业务需求简单封装了upload集成自带预览组件<br/> 根据业务需求简单封装了upload<br/>
图片使用<a href="https://github.com/sachinchoolur/lightgallery.js" target="_blank">lightgallery</a>预览
非图片类型的文件使用<a href="https://github.com/kekingcn/kkFileView" target="_blank">kkFileView</a>预览 非图片类型的文件使用<a href="https://github.com/kekingcn/kkFileView" target="_blank">kkFileView</a>预览
</div> </div>
<upload-file :file-list="fileList"/> <qiniu-upload :file-list="fileList"/>
<div class="tip-row">原始使用axios的上传</div> <div class="tip-row">原始使用axios的上传</div>
<input @change="change" type="file"> <input @change="change" type="file">
</div> </div>
</template> </template>
<script> <script>
import UploadFile from '@/component/UploadFile' import QiniuUpload from "@/component/Upload/Qiniu"
import request from "@/api/request" import request from "@/api/request"
export default { export default {
name: "uploadExample", name: "uploadExample",
components: {UploadFile},
components: {QiniuUpload},
data() { data() {
return { return {
fileList: [ fileList: [
@ -35,6 +38,7 @@ export default {
] ]
} }
}, },
methods: { methods: {
change(e) { change(e) {
let file = e.target.files[0] let file = e.target.files[0]
@ -51,7 +55,8 @@ export default {
//RequestParam //RequestParam
param.append('extraParam1', "额外参数1") param.append('extraParam1', "额外参数1")
param.append('extraParam2', "额外参数2") param.append('extraParam2', "额外参数2")
request.post('/test/upload', param, {headers: {"Content-Type": "multipart/form-data"}}) request
.post('/test/upload', param, {headers: {"Content-Type": "multipart/form-data"}})
.then(({data}) => alert(data)) .then(({data}) => alert(data))
} }
} }

@ -174,6 +174,8 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/asset/style/var";
.container { .container {
max-width: 1200px; max-width: 1200px;
text-align: center; text-align: center;
@ -197,7 +199,7 @@ export default {
text-align: center; text-align: center;
p { p {
color: #909399; color: $--color-text-regular;
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
} }

@ -3,8 +3,6 @@ const path = require('path')
const settings = require('./src/config') const settings = require('./src/config')
const CompressionWebpackPlugin = require('compression-webpack-plugin') const CompressionWebpackPlugin = require('compression-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const forElementUI = require('webpack-theme-color-replacer/forElementUI')
const EMPTY_PLUGIN = {apply: () => ({})} const EMPTY_PLUGIN = {apply: () => ({})}
@ -56,12 +54,6 @@ module.exports = {
} }
}, },
plugins: [ plugins: [
new ThemeColorReplacer({
fileName: 'css/theme-colors.[contenthash:8].css',
matchColors: forElementUI.getElementUISeries('#1890ff'),
changeSelector: forElementUI.changeSelector,
injectCss: true
}),
new CompressionWebpackPlugin({ new CompressionWebpackPlugin({
filename: '[path].gz[query]', filename: '[path].gz[query]',
algorithm: 'gzip', algorithm: 'gzip',

Loading…
Cancel
Save