first commit
@@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 sakuya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
### sentcms后台管理系统
|
||||
|
||||
1.安装依赖
|
||||
|
||||
~~~
|
||||
npm install
|
||||
~~~
|
||||
|
||||
2.启动项目
|
||||
|
||||
~~~
|
||||
npm run dev
|
||||
~~~
|
||||
|
||||
3.打包项目
|
||||
|
||||
~~~
|
||||
npm run build
|
||||
~~~
|
||||
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title>Admin</title>
|
||||
<script type="text/javascript">
|
||||
document.write("<script src='admin.js?"+new Date().getTime()+"'><\/script>");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but Admin doesn't work properly without JavaScript
|
||||
enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<script type="text/javascript">
|
||||
var dark = window.localStorage.getItem('APP_DARK');
|
||||
if(dark){
|
||||
document.documentElement.classList.add("dark")
|
||||
}
|
||||
</script>
|
||||
<div id="app" class="aminui">
|
||||
<div class="app-loading">
|
||||
<div class="app-loading__logo">
|
||||
<img src="/static/images/logo.png"/>
|
||||
</div>
|
||||
<div class="app-loading__loader"></div>
|
||||
<div class="app-loading__title">Admin</div>
|
||||
</div>
|
||||
<style>
|
||||
.app-loading {position: absolute;top:0px;left:0px;right:0px;bottom:0px;display: flex;justify-content: center;align-items: center;flex-direction: column;background: #fff;}
|
||||
.app-loading__logo {margin-bottom: 30px;}
|
||||
.app-loading__logo img {width: 90px;vertical-align: bottom;}
|
||||
.app-loading__loader {box-sizing: border-box;width: 35px;height: 35px;border: 5px solid transparent;border-top-color: #000;border-radius: 50%;animation: .5s loader linear infinite;position: relative;}
|
||||
.app-loading__loader:before {box-sizing: border-box;content: '';display: block;width: inherit;height: inherit;position: absolute;top: -5px;left: -5px;border: 5px solid #ccc;border-radius: 50%;opacity: .5;}
|
||||
.app-loading__title {font-size: 24px;color: #333;margin-top: 30px;}
|
||||
.dark .app-loading {background: #222225;}
|
||||
.dark .app-loading__loader {border-top-color: #fff;}
|
||||
.dark .app-loading__title {color: #d0d0d0;}
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
<div id="versionCheck" style="display: none;position: absolute;z-index: 99;top:0;left:0;right:0;bottom:0;padding:40px;background:rgba(255,255,255,0.9);color: #333;">
|
||||
<h2 style="line-height: 1;margin: 0;font-size: 24px;">当前使用的浏览器内核版本过低 :(</h2>
|
||||
<p style="line-height: 1;margin: 0;font-size: 14px;margin-top: 20px;opacity: 0.8;">当前版本:<span id="versionCheck-type">--</span> <span id="versionCheck-version">--</span></p>
|
||||
<p style="line-height: 1;margin: 0;font-size: 14px;margin-top: 10px;opacity: 0.8;">最低版本要求:Chrome 71+、Firefox 65+、Safari 12+、Edge 97+。</p>
|
||||
<p style="line-height: 1;margin: 0;font-size: 14px;margin-top: 10px;opacity: 0.8;">请升级浏览器版本,或更换现代浏览器,如果你使用的是双核浏览器,请切换到极速/高速模式。</p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function getBrowerInfo(){
|
||||
var userAgent = window.navigator.userAgent;
|
||||
var browerInfo = {
|
||||
type: 'unknown',
|
||||
version: 'unknown',
|
||||
userAgent: userAgent
|
||||
};
|
||||
if(document.documentMode){
|
||||
browerInfo.type = "IE"
|
||||
browerInfo.version = document.documentMode + ''
|
||||
}else if(indexOf(userAgent, "Firefox")){
|
||||
browerInfo.type = "Firefox"
|
||||
browerInfo.version = userAgent.match(/Firefox\/([\d.]+)/)[1]
|
||||
}else if(indexOf(userAgent, "Opera")){
|
||||
browerInfo.type = "Opera"
|
||||
browerInfo.version = userAgent.match(/Opera\/([\d.]+)/)[1]
|
||||
}else if(indexOf(userAgent, "Edg")){
|
||||
browerInfo.type = "Edg"
|
||||
browerInfo.version = userAgent.match(/Edg\/([\d.]+)/)[1]
|
||||
}else if(indexOf(userAgent, "Chrome")){
|
||||
browerInfo.type = "Chrome"
|
||||
browerInfo.version = userAgent.match(/Chrome\/([\d.]+)/)[1]
|
||||
}else if(indexOf(userAgent, "Safari")){
|
||||
browerInfo.type = "Safari"
|
||||
browerInfo.version = userAgent.match(/Safari\/([\d.]+)/)[1]
|
||||
}
|
||||
return browerInfo
|
||||
}
|
||||
function indexOf(userAgent, brower) {
|
||||
return userAgent.indexOf(brower) > -1
|
||||
}
|
||||
function isSatisfyBrower(){
|
||||
var minVer = {
|
||||
"Chrome": 71,
|
||||
"Firefox": 65,
|
||||
"Safari": 12,
|
||||
"Edg": 97,
|
||||
"IE": 999
|
||||
}
|
||||
var browerInfo = getBrowerInfo()
|
||||
var materVer = browerInfo.version.split('.')[0]
|
||||
return materVer >= minVer[browerInfo.type]
|
||||
}
|
||||
if(!isSatisfyBrower()){
|
||||
document.getElementById('versionCheck').style.display = 'block';
|
||||
document.getElementById('versionCheck-type').innerHTML = getBrowerInfo().type;
|
||||
document.getElementById('versionCheck-version').innerHTML = getBrowerInfo().version;
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "sentos-admin",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ckeditor/ckeditor5-vue": "^7.2.0",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"aieditor": "^1.3.5",
|
||||
"axios": "^1.7.2",
|
||||
"ckeditor5": "^43.2.0",
|
||||
"codemirror": "5.65.5",
|
||||
"core-js": "3.29.0",
|
||||
"cropperjs": "1.5.13",
|
||||
"crypto-js": "4.2.0",
|
||||
"echarts": "5.6.0",
|
||||
"element-plus": "2.8.4",
|
||||
"nprogress": "0.2.0",
|
||||
"pinyin-match": "^1.2.4",
|
||||
"qrcodejs2": "0.0.2",
|
||||
"sortablejs": "1.15.0",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.2",
|
||||
"vuedraggable": "^4.0.3",
|
||||
"vuex": "^4.1.0",
|
||||
"xgplayer": "2.32.2",
|
||||
"xgplayer-hls": "2.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"sass": "1.77.2",
|
||||
"terser": "^5.31.0",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
// 此文件非必要,在生产环境下此文件配置可覆盖运行配置,开发环境下不起效
|
||||
// 详情见 src/config/index.js
|
||||
|
||||
const APP_CONFIG = {
|
||||
//标题
|
||||
APP_NAME: "后台管理系统",
|
||||
|
||||
//接口地址,如遇跨域需使用nginx代理
|
||||
API_URL: "http://www.sentcms.com/admin/"
|
||||
}
|
||||
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 750 750" style="enable-background:new 0 0 750 750;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.35;fill:#B3B3B3;}
|
||||
.st1{opacity:0.1;fill:#B3B3B3;}
|
||||
.st2{opacity:0.3;fill:#B3B3B3;}
|
||||
.st3{opacity:0.1;}
|
||||
.st4{fill:#B3B3B3;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M465.1,261.4H264c-1.3,0-2.4,1.1-2.4,2.4v255.6c0,1.3,1.1,2.4,2.4,2.4h201.1c1.3,0,2.4-1.1,2.4-2.4V263.8
|
||||
C467.5,262.4,466.4,261.4,465.1,261.4z M417.9,443c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4
|
||||
h102c1.3,0,2.4,1.1,2.4,2.4V443z M417.9,397.2c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4h102
|
||||
c1.3,0,2.4,1.1,2.4,2.4V397.2z M417.9,351.5c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4h102
|
||||
c1.3,0,2.4,1.1,2.4,2.4V351.5z"/>
|
||||
<g>
|
||||
<path class="st1" d="M462.1,236.8L462.1,236.8C384.8,236.2,321,295.1,314,370.7c-18.5-19.1-44.4-31.1-73.1-31.3h0
|
||||
c-56.8-0.4-103.2,45.3-103.6,102.1l-0.8,101.4l175.6,1.3l30.1,0.2l265.1,2l1.2-160.9C609.2,304,543.6,237.4,462.1,236.8z"/>
|
||||
<path class="st2" d="M216.9,227.4c-3.4,0-6.5,1.1-9,2.9c0.2-1,0.3-2,0.3-3c0.1-8.3-6.6-15.1-15-15.2s-15.1,6.6-15.2,15
|
||||
c0,0.3,0,0.6,0,0.9c-1.6-0.6-3.4-1-5.2-1c-8.3-0.1-15.1,6.6-15.2,15c-0.1,8.2,6.4,14.9,14.5,15.2l0,0l44.6,0.3
|
||||
c8.3,0.1,15.1-6.6,15.2-15S225.2,227.5,216.9,227.4z"/>
|
||||
<path class="st2" d="M596.4,194.2c-3.4,0-6.5,1.1-9,2.9c0.2-1,0.3-2,0.3-3c0.1-8.3-6.6-15.1-15-15.2s-15.1,6.6-15.2,15
|
||||
c0,0.3,0,0.6,0,0.9c-1.6-0.6-3.4-1-5.2-1c-8.3-0.1-15.1,6.6-15.2,15c-0.1,8.2,6.4,14.9,14.5,15.2l0,0l44.6,0.3
|
||||
c8.3,0.1,15.1-6.6,15.2-15S604.7,194.3,596.4,194.2z"/>
|
||||
<g>
|
||||
<g class="st3">
|
||||
<path class="st4" d="M496.9,497.5c-2.1,0-3.7,1.6-3.7,3.7c0,1.5,0.8,2.7,2,3.3l-0.5,65.1l3.5,0l0.5-65.3
|
||||
c1.1-0.6,1.8-1.8,1.8-3.1C500.6,499.1,499,497.6,496.9,497.5z"/>
|
||||
<path class="st4" d="M572.3,501.7c0-1.9-1.6-3.6-3.7-3.7c-2.1,0-3.7,1.6-3.7,3.7c0,1.4,0.8,2.6,1.9,3.2l-0.5,65.2l3.5,0
|
||||
l0.5-65.2C571.5,504.3,572.2,503.1,572.3,501.7z"/>
|
||||
</g>
|
||||
|
||||
<rect x="522.7" y="472.2" transform="matrix(7.448311e-03 -1 1 7.448311e-03 8.6828 1045.4733)" class="st1" width="16.5" height="92.3"/>
|
||||
<polygon class="st1" points="495.4,509.8 495.2,510.1 485.5,526.3 484.8,526.3 484.9,509.8 "/>
|
||||
<polygon class="st1" points="518.7,510 508.8,526.5 496.3,526.4 500.2,519.8 506,509.9 "/>
|
||||
<polygon class="st1" points="542,510.2 532.1,526.6 519.6,526.5 529.3,510.1 "/>
|
||||
<polygon class="st1" points="565.3,510.4 555.5,526.8 542.9,526.7 552.7,510.3 "/>
|
||||
<polygon class="st1" points="577.2,510.4 577.1,527 566.2,526.9 576,510.4 "/>
|
||||
|
||||
<rect x="522.5" y="497.7" transform="matrix(7.448311e-03 -1 1 7.448311e-03 -17.0149 1070.603)" class="st1" width="16.5" height="92.3"/>
|
||||
<polygon class="st1" points="495.2,535.3 495,535.6 485.3,551.8 484.6,551.8 484.7,535.3 "/>
|
||||
<polygon class="st1" points="518.5,535.5 508.6,552 496.1,551.9 500,545.3 505.8,535.4 "/>
|
||||
<polygon class="st1" points="541.8,535.7 531.9,552.1 519.4,552 529.1,535.6 "/>
|
||||
<polygon class="st1" points="565.1,535.9 555.4,552.3 542.7,552.2 552.5,535.8 "/>
|
||||
<polygon class="st1" points="577,536 576.9,552.5 566,552.4 575.8,536 "/>
|
||||
<path class="st1" d="M577.1,527c0,0,0-0.1,0-0.3l0-0.9c0-0.7,0-1.8,0-3.2c0-2.8,0.1-6.9,0.1-12.2l0.1,0.1l-92.3-0.5l0,0l0.1-0.1
|
||||
c0,5.6-0.1,11.2-0.1,16.5l-0.1-0.1l65.8,0.6l19.5,0.1l5.3,0l0,0l-5.3,0l-19.5-0.1l-65.8-0.3l-0.3,0l0.1-16.8l0.1,0l92.3,0.8
|
||||
l0.1,0l0,0.1c0,5.3-0.1,9.4-0.1,12.2c0,1.5,0,2.5,0,3.2l0,0.7C577.1,526.8,577.1,527,577.1,527z"/>
|
||||
<path class="st1" d="M576.9,552.5c0,0,0-0.1,0-0.3l0-0.9c0-0.7,0-1.8,0-3.2c0-2.8,0.1-6.9,0.1-12.2l0.1,0.1l-92.3-0.5l0,0
|
||||
l0.1-0.1c0,5.6-0.1,11.2-0.1,16.5l-0.1-0.1l65.8,0.6l19.5,0.1l5.3,0l0,0l-5.3,0l-19.5-0.1l-65.8-0.3l-0.3,0l0.1-16.7l0.1,0
|
||||
l92.3,0.8l0.1,0l0,0.1c0,5.3-0.1,9.4-0.1,12.2c0,1.5,0,2.5,0,3.2l0,0.7C576.9,552.3,576.9,552.5,576.9,552.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 456 262.1" style="enable-background:new 0 0 456 262.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.4;fill:url(#SVGID_1_);enable-background:new ;}
|
||||
.st1{opacity:0.7;}
|
||||
.st2{opacity:0.4;fill:url(#SVGID_2_);enable-background:new ;}
|
||||
.st3{opacity:0.4;fill:url(#SVGID_3_);enable-background:new ;}
|
||||
.st4{opacity:0.4;fill:url(#SVGID_4_);enable-background:new ;}
|
||||
.st5{opacity:0.4;fill:url(#SVGID_5_);enable-background:new ;}
|
||||
.st6{opacity:0.6;}
|
||||
.st7{fill:#0073CD;}
|
||||
.st8{fill:#40A8F5;}
|
||||
.st9{fill:#53B9F5;}
|
||||
.st10{fill:#85D3FF;}
|
||||
.st11{fill:#8CD7FF;}
|
||||
.st12{fill:#EBFCFF;}
|
||||
.st13{fill:none;stroke:url(#SVGID_6_);stroke-width:2;stroke-miterlimit:10;}
|
||||
.st14{fill:none;stroke:url(#SVGID_7_);stroke-width:2;stroke-miterlimit:10;}
|
||||
.st15{fill:none;stroke:url(#SVGID_8_);stroke-width:2;stroke-miterlimit:10;}
|
||||
.st16{fill:none;stroke:url(#SVGID_9_);stroke-width:2;stroke-miterlimit:10;}
|
||||
.st17{fill:none;stroke:url(#SVGID_10_);stroke-width:2;stroke-miterlimit:10;}
|
||||
.st18{fill:none;stroke:url(#SVGID_11_);stroke-width:2;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<title>升级中</title>
|
||||
<g id="图层_2_1_">
|
||||
<g id="图层_1-2">
|
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="232.745" y1="39.57" x2="232.745" y2="1.88" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path class="st0" d="M412.3,262.1c-23-23-61-37.7-179.5-37.7S76.2,239.1,53.2,262.1H412.3z"/>
|
||||
<g class="st1">
|
||||
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="349.365" y1="237.3224" x2="349.365" y2="59.9676" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path class="st2" d="M380.7,26.7h-62.6c-1.5-0.1-2.8,1.1-2.8,2.6v172.2c0.1,1.5,1.3,2.7,2.8,2.6h62.6c1.5,0.1,2.7-1.1,2.8-2.6
|
||||
V29.3C383.4,27.8,382.2,26.6,380.7,26.7z M328.3,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.8c0,0,0,0,0,0
|
||||
v-19.7c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V147z M328.3,116.8c0,0.5-0.4,0.9-0.9,0.9
|
||||
c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V97c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9
|
||||
c0,0,0,0,0,0V116.8z M328.3,86.5c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V66.8
|
||||
c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V86.5z M328.3,56.3c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0
|
||||
h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V36.6c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V56.3z
|
||||
M340,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,1-0.9h3.6
|
||||
c0.5,0,0.9,0.4,0.9,0.9V147z M340,116.8c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V97c0-0.5,0.4-0.9,1-0.9h3.6
|
||||
c0.5,0,0.9,0.4,0.9,0.9V116.8z M340,86.5c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V66.8c0-0.5,0.4-0.9,1-0.9h3.6
|
||||
c0.5,0,0.9,0.4,0.9,0.9V86.5z M340,56.3c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V36.6c0-0.5,0.4-0.9,1-0.9h3.6
|
||||
c0.5,0,0.9,0.4,0.9,0.9V56.3z M351.7,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7
|
||||
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V147z M351.7,116.8c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97
|
||||
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V116.8z M351.7,86.5c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8
|
||||
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V86.5z M351.7,56.3c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6
|
||||
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V56.3z M363.4,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6
|
||||
c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V147z M363.4,116.8
|
||||
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V116.8z M363.4,86.5
|
||||
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V86.5z M363.4,56.3
|
||||
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V56.3z M375.1,147
|
||||
c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,0.9-0.9h3.6
|
||||
c0.5,0,0.9,0.4,1,0.9V147z M375.1,116.8c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97c0-0.5,0.4-0.9,0.9-0.9h3.6
|
||||
c0.5,0,0.9,0.4,1,0.9V116.8z M375.1,86.5c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8c0-0.5,0.4-0.9,0.9-0.9h3.6
|
||||
c0.5,0,0.9,0.4,1,0.9V86.5z M375.1,56.3c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6c0-0.5,0.4-0.9,0.9-0.9h3.6
|
||||
c0.5,0,0.9,0.4,1,0.9V56.3z"/>
|
||||
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="201.46" y1="208.3924" x2="201.46" y2="59.9976" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path class="st3" d="M231.1,55.6h-59.3c-1.5-0.1-2.7,1.1-2.8,2.6v143.2c0.1,1.5,1.3,2.6,2.8,2.6h59.3c1.5,0.1,2.8-1.1,2.8-2.6
|
||||
V58.2C233.9,56.7,232.6,55.5,231.1,55.6z M182.5,159.4c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V159.4z M182.5,146.5c0,0.6-0.6,1.1-1.2,1.1
|
||||
c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V146.5z
|
||||
M182.5,133.6c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
|
||||
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V133.6z M182.5,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1
|
||||
c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V120.7z M182.5,107.8c0,0.6-0.6,1.1-1.2,1.1h-5.4
|
||||
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V107.8z M182.5,94.9
|
||||
c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4
|
||||
c0.6,0,1.2,0.5,1.2,1.1V94.9z M182.5,82.1c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1
|
||||
c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V82.1z M182.5,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V69.2z M192.6,159.4
|
||||
c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1
|
||||
c0,0,0,0,0,0L192.6,159.4z M192.6,146.5c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,146.5z M192.6,133.6c0,0.6-0.6,1.1-1.2,1.1H186
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,133.6z
|
||||
M192.6,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
|
||||
c0.6,0,1.2,0.5,1.2,1.1L192.6,120.7z M192.6,107.8c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,107.8z M192.6,94.9c0,0.6-0.6,1.1-1.2,1.1
|
||||
c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,94.9z
|
||||
M192.6,82.1c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
|
||||
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,82.1z M192.6,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1V64
|
||||
c0-0.6,0.5-1.2,1.2-1.2c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,69.2z M202.6,159.4c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196
|
||||
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L202.6,159.4z M202.6,146.5
|
||||
c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1
|
||||
c0,0,0,0,0,0L202.6,146.5z M202.6,133.6c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L202.6,133.6z M202.6,120.7c0,0.6-0.6,1.1-1.2,1.1H196
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,120.7z
|
||||
M202.6,107.8c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
|
||||
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,107.8z M202.6,94.9c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0
|
||||
v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,94.9z M202.6,82.1c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0
|
||||
H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,82.1z
|
||||
M202.6,69.2c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.4,1.2,1.1
|
||||
c0,0,0,0,0,0V69.2z M227.8,159.4c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0
|
||||
h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,159.4z M227.8,146.5c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,146.5z M227.8,133.6
|
||||
c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1
|
||||
c0,0,0,0,0,0L227.8,133.6z M227.8,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,120.7z M227.8,107.8c0,0.6-0.6,1.1-1.2,1.1h-20.5
|
||||
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,107.8z
|
||||
M227.8,94.9c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h20.5
|
||||
c0.6,0,1.2,0.5,1.2,1.1L227.8,94.9z M227.8,82.1c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1
|
||||
c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,82.1z M227.8,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,69.2z"/>
|
||||
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="122.975" y1="237.3228" x2="122.975" y2="59.9971" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path class="st4" d="M161.1,26.7H84.8c-1.5-0.1-2.8,1.1-2.8,2.6c0,0,0,0,0,0v172.2c0.1,1.5,1.3,2.6,2.8,2.6h76.3
|
||||
c1.5,0.1,2.8-1.1,2.8-2.6V29.3C163.9,27.8,162.6,26.6,161.1,26.7z M154.3,161c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1
|
||||
c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V161z M154.3,146.3
|
||||
c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
|
||||
c0.6,0,1.2,0.5,1.2,1.1V146.3z M154.3,131.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
|
||||
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V131.6z M154.3,117c0,0.6-0.6,1.1-1.2,1.1H92.8
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V117z
|
||||
M154.3,102.3c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1V97c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
|
||||
c0.6,0,1.2,0.5,1.2,1.1V102.3z M154.3,87.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3
|
||||
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V87.6z M154.3,72.9c0,0.6-0.6,1.1-1.2,1.1H92.8
|
||||
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V72.9z
|
||||
M154.3,58.3c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1V53c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
|
||||
c0.6,0,1.2,0.5,1.2,1.1V58.3z M154.3,43.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3
|
||||
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V43.6z"/>
|
||||
|
||||
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="273.51" y1="264" x2="273.51" y2="54.46" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<path class="st5" d="M306.2,0h-65.4c-1.4,0-2.5,1.2-2.4,2.6v204.4c-0.1,1.4,1,2.5,2.4,2.6c0,0,0,0,0,0h65.4
|
||||
c1.4-0.1,2.5-1.2,2.4-2.6V2.6C308.7,1.2,307.6,0.1,306.2,0z M300.4,119.6c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
|
||||
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,119.6z M300.4,90.3c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1V85
|
||||
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,90.3z M300.4,60.9c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
|
||||
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,60.9z M300.4,31.5c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
|
||||
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,31.5z M300.4,16.9c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
|
||||
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,16.9z"/>
|
||||
</g>
|
||||
<g class="st6">
|
||||
<path class="st7" d="M244.2,130.7c3.3,1.9,5.7,13.6,1,20.7c-5.3,6.8-7,9-6.1,14c-4.3-7.3-0.6-7.4-5.8-14.2
|
||||
c-4.6-6.2-2.5-17.4,0.7-20.5C237.4,131.3,240.8,131.3,244.2,130.7L244.2,130.7z"/>
|
||||
<path class="st8" d="M280.4,211.9c-0.1,0.6-0.1,1.2-0.1,1.7c0,0.7,0.1,1.3,0.2,2c-2.1-0.8-4.3-1.2-6.5-1.2
|
||||
c-2.6,0-5.2,0.6-7.5,1.7c-0.8-4.2-4.8-7-9-6.2c-2.8,0.5-5,2.5-5.9,5.2c-3.4-2.1-7.7-2-11.1,0.1c-0.4-5.7-4.1-0.8-9.8-0.8
|
||||
c-6,0-11.8-4.4-11.8,1.7c0,1.1,0.2,2.2,0.4,3.2c-1.1,0.1-2.1,0.5-3,1c-0.9-7-6.8-12.4-13.9-12.7c22.6-10.3,22.4-44.6,24.8-74.6
|
||||
c0.1-1.7,1.2-4.1,1.8-4.4l0,0c1.5,1.1,3.2,1.8,5.1,2.1c-3.2,3.1-5.4,14.3-0.7,20.5c5.3,6.8,1.6,6.9,5.8,14.2
|
||||
c-0.9-5,0.8-7.2,6.1-14c4.8-7.2,2.3-18.8-1-20.7c1.8-0.3,3.6-1,5.1-2.1l0,0c0.6,0.3,1.7,2.7,1.8,4.4
|
||||
C253.6,164.6,250.4,198.5,280.4,211.9z"/>
|
||||
<path class="st9" d="M303.3,203.2c-5.4,0-9.8,4.4-9.8,9.8c0,0.3,0,0.6,0,0.9c-1.5-1.7-3.7-2.6-6-2.5c-0.7,0-1.4,0.1-2.1,0.3
|
||||
c0.7-1.3,1.1-2.8,1.1-4.3c-0.1-4.6-3.9-8.3-8.5-8.2c-4,0.1-7.4,3-8.1,6.9c-0.1,0.4-0.1,0.9-0.1,1.3c0,0.5,0,1,0.1,1.6
|
||||
c-3.5-1.4-7.4-1.3-10.8,0.4c-0.7-3.2-3.9-5.3-7.2-4.6c-2,0.4-3.6,1.9-4.3,3.8c-1.3-0.8-2.7-1.2-4.2-1.2c-1.5,0-3.1,0.4-4.4,1.3
|
||||
c-0.2-4.6-4.1-8.2-8.7-8s-8.2,4.1-8,8.7l0,0c0,0.8,0.1,1.7,0.3,2.5c-0.8,0.1-1.6,0.4-2.3,0.8c-0.7-5.4-5.2-9.6-10.7-9.8h-0.4
|
||||
c-3.4,0-6.7,1.6-8.8,4.3c-3-6.2-10.4-8.9-16.7-5.9c-2.9,1.4-5.2,3.9-6.3,7c4.1,3,6.6,7.8,6.6,12.9c0,2.3-0.5,4.6-1.5,6.6
|
||||
c2-2.2,4.8-3.4,7.8-3.4c0.9,0,1.8,0.1,2.7,0.3c-2.9-5-1.2-11.5,3.8-14.4c1.2-0.7,2.6-1.2,3.9-1.3c0.5-0.1,0.9-0.1,1.4-0.1
|
||||
c5.8,0,10.6,4.7,10.6,10.6l0,0c0,0.6-0.1,1.3-0.2,1.9c4.4-1.7,9.3-1.6,13.6,0.4c0.9-4.1,4.9-6.7,9-5.8c2.6,0.6,4.7,2.4,5.5,4.9
|
||||
c1.6-0.9,3.5-1.4,5.3-1.4c1.9,0,3.8,0.5,5.4,1.5l0.1,0.1c0.3-4.5,3.6-8.4,8-9.4c0.8-0.2,1.7-0.3,2.5-0.3
|
||||
c5.8,0,10.6,4.7,10.6,10.6c0,1-0.2,2-0.4,3c1.1,0.1,2.1,0.5,3,1c1-7,7-12.1,14-12.1c1.5,0,3,0.2,4.4,0.7c2.6,0.8,5,2.5,6.7,4.6
|
||||
c2.4-4.8,7-8.2,12.4-8.8C311.4,205.9,307.6,203.2,303.3,203.2z"/>
|
||||
<path class="st10" d="M314.4,209.9c-0.6,0-1.2,0-1.8,0.1c-5.4,0.6-10.1,3.9-12.5,8.7c-1.7-2.2-4-3.8-6.7-4.6
|
||||
c-1.4-0.5-2.9-0.7-4.4-0.7c-7,0-13,5.2-14,12.1c-0.9-0.5-1.9-0.9-3-1c0.3-1,0.4-2,0.4-3c0-5.8-4.7-10.5-10.5-10.5
|
||||
c-0.8,0-1.7,0.1-2.5,0.3c-4.4,1.1-7.7,4.9-8,9.4l-0.1-0.1c-1.6-1-3.5-1.5-5.4-1.5c-1.9,0-3.7,0.5-5.3,1.4c-1.4-3.9-5.7-6-9.6-4.6
|
||||
c-2.5,0.9-4.3,3-4.9,5.5c-4.3-2-9.2-2.1-13.6-0.4c0.1-0.6,0.2-1.3,0.2-1.9c0-5.8-4.7-10.6-10.6-10.6l0,0c-0.5,0-0.9,0-1.4,0.1
|
||||
c-5.8,0.8-9.9,6.1-9.1,11.8c0.2,1.4,0.6,2.7,1.3,3.9c-0.9-0.2-1.8-0.3-2.7-0.3c-2.9,0-5.8,1.2-7.8,3.4c1-2.1,1.5-4.3,1.5-6.6
|
||||
c0-5.1-2.4-9.9-6.6-12.9l0,0c-7.1-5.2-17-3.6-22.2,3.5c-5.2,7.1-3.6,17,3.5,22.2c6.8,5,16.4,3.7,21.7-2.8
|
||||
c-2.1,5.5,0.7,11.6,6.1,13.6c5.5,2.1,11.6-0.7,13.6-6.1c0.5-1.2,0.7-2.5,0.7-3.7c0-1.9,0.4-0.1,0.4,2.7c0,9.7,7.8,17.6,17.5,17.6
|
||||
c9.7,0,17.6-7.8,17.6-17.5c0-2.4-0.5-4.8-1.4-7l0.5-0.1c0.5,5.8,5.7,10.1,11.5,9.5c5-0.5,9-4.4,9.5-9.4c1.7,1,3.6,1.6,5.5,1.6
|
||||
c0.6,0,1.2,0,1.7-0.1v0.1c0,4.2,3.4,7.6,7.6,7.5c2.6,0,5-1.4,6.4-3.6c4.7,6.3,13.5,7.5,19.8,2.9c1.5-1.1,2.8-2.5,3.7-4.2
|
||||
c4.9,7.3,14.8,9.1,22.1,4.2s9.1-14.8,4.2-22.1C324.5,212.5,319.6,209.9,314.4,209.9L314.4,209.9z"/>
|
||||
</g>
|
||||
<path class="st11" d="M239.1,70c6.4,0,11.6,5.2,11.6,11.6c0,6.4-5.2,11.6-11.6,11.6c-6.4,0-11.6-5.2-11.6-11.6c0,0,0,0,0,0
|
||||
C227.5,75.2,232.7,70,239.1,70z"/>
|
||||
<path class="st8" d="M239.1,137.2c8.3,0,13.5-1.7,16.6-7.9c0,3.2-1.5,6.2-4.1,8.1c-1.9,1.3-4,2.2-6.2,2.6c-4.1,0.8-8.4,0.8-12.5,0
|
||||
c-2.2-0.4-4.3-1.3-6.2-2.6c-2.6-1.9-4.2-4.9-4.2-8.1C225.6,135.5,230.7,137.2,239.1,137.2z"/>
|
||||
<path class="st8" d="M288.4,151.7c0,0.5-0.3,0.8-0.8,0.8c-0.3,0-0.5-0.1-0.7-0.3c-7.6-10.3-25.5-25-30.4-24.7
|
||||
c2.4-6.1,3.4-15.9,3.6-31.2C271.5,100.3,288.3,122.4,288.4,151.7z"/>
|
||||
<path class="st8" d="M218.1,96.3c0.3,15.2,1.2,25,3.6,31.1l-0.2,0.1c-4.9-0.3-22.8,14.5-30.4,24.7c-0.3,0.4-0.8,0.5-1.1,0.2
|
||||
c-0.2-0.2-0.4-0.4-0.3-0.7C189.7,122.3,206.6,100.2,218.1,96.3L218.1,96.3z"/>
|
||||
<path class="st8" d="M250.7,81.6c0-6.4-5.2-11.6-11.6-11.6c-6.4,0-11.6,5.2-11.6,11.6s5.2,11.6,11.6,11.6l0,0
|
||||
C245.5,93.2,250.7,88,250.7,81.6z M252.9,81.6c0,7.6-6.2,13.8-13.8,13.8c-7.6,0-13.8-6.2-13.8-13.8c0-7.6,6.2-13.8,13.8-13.8
|
||||
c0,0,0,0,0,0C246.7,67.8,252.9,74,252.9,81.6z"/>
|
||||
<path class="st8" d="M239.1,48.7c5.7,0,10.7-2,13.8-4.9c0.5,1.3,1,2.6,1.4,4c-3.3,3.3-8.9,5.4-15.3,5.4s-11.9-2.1-15.3-5.4
|
||||
c0.5-1.3,1-2.7,1.4-4C228.4,46.8,233.4,48.7,239.1,48.7z"/>
|
||||
<path class="st9" d="M252.9,43.8c-3.1,3-8.1,4.9-13.8,4.9s-10.7-2-13.8-4.9c2-5.1,4.5-10,7.6-14.5c0.5-0.7,0.9-1.3,1.4-1.9
|
||||
c2-2.7,5.8-3.2,8.5-1.2c0.4,0.3,0.8,0.7,1.2,1.2c0.5,0.6,0.9,1.2,1.4,1.9C248.4,33.8,250.9,38.7,252.9,43.8z"/>
|
||||
<path class="st10" d="M260.1,96.4c-0.3,15.3-1.2,25-3.6,31.2c-0.2,0.6-0.5,1.2-0.8,1.7c-3.1,6.2-8.3,7.9-16.6,7.9
|
||||
s-13.5-1.7-16.6-7.9c-0.3-0.6-0.5-1.1-0.8-1.8c-2.4-6.2-3.4-15.9-3.6-31.1c-0.1-3.4-0.1-7.1-0.1-11.1c-0.1-12.7,1.8-25.4,5.8-37.5
|
||||
c3.3,3.3,8.9,5.4,15.3,5.4s11.9-2.1,15.3-5.4c4,12.1,6,24.8,5.8,37.5C260.2,89.2,260.1,92.9,260.1,96.4z M252.9,81.6
|
||||
c0-7.6-6.2-13.8-13.8-13.8c-7.6,0-13.8,6.2-13.8,13.8c0,7.6,6.2,13.8,13.8,13.8c0,0,0,0,0,0C246.7,95.4,252.9,89.2,252.9,81.6
|
||||
L252.9,81.6z"/>
|
||||
<path class="st12" d="M139.2,246.1l18.4,0.4v0.7l-19.4-0.4v-0.7V246C138.4,246.1,138.6,246.1,139.2,246.1z"/>
|
||||
|
||||
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="112.2357" y1="190.775" x2="112.2357" y2="101.005" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st13" x1="112.2" y1="73.2" x2="112.2" y2="163"/>
|
||||
|
||||
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="348.955" y1="195.605" x2="348.955" y2="105.835" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st14" x1="349" y1="68.4" x2="349" y2="158.2"/>
|
||||
|
||||
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="40.9" y1="120.12" x2="40.9" y2="64.49" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st15" x1="40.9" y1="143.9" x2="40.9" y2="199.5"/>
|
||||
|
||||
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="64.97" y1="168.64" x2="64.97" y2="140.83" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st16" x1="65" y1="95.4" x2="65" y2="123.2"/>
|
||||
|
||||
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="397.23" y1="159.8" x2="397.23" y2="131.98" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st17" x1="397.2" y1="104.2" x2="397.2" y2="132"/>
|
||||
|
||||
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="424.75" y1="130.51" x2="424.75" y2="74.87" gradientTransform="matrix(1 0 0 -1 0 264)">
|
||||
<stop offset="0" style="stop-color:#81CFFF"/>
|
||||
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<line class="st18" x1="424.8" y1="133.5" x2="424.8" y2="189.1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<el-config-provider :locale="locale" :size="config.size" :zIndex="config.zIndex" :button="config.button">
|
||||
<router-view></router-view>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import colorTool from '@/utils/color'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
size: "small",
|
||||
zIndex: 2000,
|
||||
button: {
|
||||
autoInsertSpace: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
locale(){
|
||||
return this.$i18n.messages[this.$i18n.locale].el
|
||||
},
|
||||
},
|
||||
created() {
|
||||
//设置主题颜色
|
||||
const app_color = this.$CONFIG.COLOR || this.$TOOL.data.get('APP_COLOR')
|
||||
if(app_color){
|
||||
document.documentElement.style.setProperty('--el-color-primary', app_color);
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, colorTool.lighten(app_color,i/10));
|
||||
}
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, colorTool.darken(app_color,i/10));
|
||||
}
|
||||
}
|
||||
const debounce = (fn, delay) => {
|
||||
let timer = null;
|
||||
return function () {
|
||||
let context = this;
|
||||
let args = arguments;
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
fn.apply(context, args);
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
const _ResizeObserver = window.ResizeObserver;
|
||||
window.ResizeObserver = class ResizeObserver extends _ResizeObserver{
|
||||
constructor(callback) {
|
||||
callback = debounce(callback, 16);
|
||||
super(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/assets/style/style.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @description 自动import导入所有 api 模块
|
||||
*/
|
||||
|
||||
const files = import.meta.glob('./module/*.js', { eager: true })
|
||||
const modules = {}
|
||||
Object.keys(files).forEach(key => {
|
||||
modules[key.replace(/^\.\/module\/(.*)\.js$/g, '$1')] = files[key].default
|
||||
})
|
||||
|
||||
export default modules
|
||||
@@ -0,0 +1,153 @@
|
||||
import config from "@/config"
|
||||
import http from "@/utils/request"
|
||||
|
||||
export default {
|
||||
family: {
|
||||
list: {
|
||||
url: `${config.API_URL}account/families/index`,
|
||||
name: "家庭列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
url: `${config.API_URL}account/families/detail`,
|
||||
name: "家庭详情",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}account/families/add`,
|
||||
name: "家庭添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}account/families/edit`,
|
||||
name: "家庭编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}account/families/delete`,
|
||||
name: "家庭删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
accounts: {
|
||||
list: {
|
||||
url: `${config.API_URL}account/accounts/index`,
|
||||
name: "账户列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
url: `${config.API_URL}account/accounts/detail`,
|
||||
name: "账户详情",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}account/accounts/add`,
|
||||
name: "账户添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}account/accounts/edit`,
|
||||
name: "账户编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}account/accounts/delete`,
|
||||
name: "账户删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
members: {
|
||||
list: {
|
||||
url: `${config.API_URL}account/members/index`,
|
||||
name: "成员列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
url: `${config.API_URL}account/members/detail`,
|
||||
name: "成员详情",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}account/members/add`,
|
||||
name: "成员添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}account/members/edit`,
|
||||
name: "成员编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}account/members/delete`,
|
||||
name: "成员删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
records: {
|
||||
list: {
|
||||
url: `${config.API_URL}account/records/index`,
|
||||
name: "记录列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
url: `${config.API_URL}account/records/detail`,
|
||||
name: "记录详情",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}account/records/add`,
|
||||
name: "记录添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}account/records/edit`,
|
||||
name: "记录编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}account/records/delete`,
|
||||
name: "记录删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import config from "@/config";
|
||||
import http from "@/utils/request";
|
||||
|
||||
export default {
|
||||
list: {
|
||||
url: `${config.API_URL}ads/index`,
|
||||
name: "获得广告列表",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}ads/add`,
|
||||
name: "添加广告",
|
||||
post: async function (params) {
|
||||
return await http.post(this.url, params);
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}ads/edit`,
|
||||
name: "编辑广告",
|
||||
post: async function (params) {
|
||||
return await http.put(this.url, params);
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}ads/delete`,
|
||||
name: "删除广告",
|
||||
post: async function (params) {
|
||||
return await http.delete(this.url, params);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
import config from "@/config"
|
||||
import http from "@/utils/request"
|
||||
|
||||
export default {
|
||||
login: {
|
||||
url: `${config.API_URL}auth/login`,
|
||||
name: "登录获取TOKEN",
|
||||
post: async function(data={}){
|
||||
return await http.post(this.url, data);
|
||||
}
|
||||
},
|
||||
logout:{
|
||||
url: `${config.API_URL}auth/logout`,
|
||||
name: "登出",
|
||||
get: async function(data={}){
|
||||
return await http.get(this.url, data);
|
||||
}
|
||||
},
|
||||
user: {
|
||||
url: `${config.API_URL}auth/user`,
|
||||
name: "获取当前登录用户",
|
||||
get: async function(data={}){
|
||||
return await http.get(this.url, data);
|
||||
}
|
||||
},
|
||||
users: {
|
||||
list: {
|
||||
url: `${config.API_URL}auth/users/index`,
|
||||
name: "获得用户列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}auth/users/add`,
|
||||
name: "添加用户",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}auth/users/edit`,
|
||||
name: "编辑用户",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
uppasswd:{
|
||||
url: `${config.API_URL}auth/users/passwd`,
|
||||
name: "修改密码",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
uprole: {
|
||||
url: `${config.API_URL}auth/users/uprole`,
|
||||
name: "设置角色",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}auth/users/delete`,
|
||||
name: "删除用户",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
role: {
|
||||
list: {
|
||||
url: `${config.API_URL}auth/role/index`,
|
||||
name: "获得角色列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}auth/role/add`,
|
||||
name: "添加角色",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}auth/role/edit`,
|
||||
name: "编辑角色",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
auth: {
|
||||
url: `${config.API_URL}auth/role/auth`,
|
||||
name: "角色授权",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}auth/role/delete`,
|
||||
name: "删除角色",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
department: {
|
||||
list: {
|
||||
url: `${config.API_URL}auth/department/index`,
|
||||
name: "获得部门列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}auth/department/add`,
|
||||
name: "添加部门",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}auth/department/edit`,
|
||||
name: "编辑部门",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}auth/department/delete`,
|
||||
name: "删除部门",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
myMenus: {
|
||||
url: `${config.API_URL}auth/menu/my`,
|
||||
name: "获取我的菜单",
|
||||
get: async function(){
|
||||
return await http.get(this.url);
|
||||
}
|
||||
},
|
||||
list: {
|
||||
url: `${config.API_URL}auth/menu/index`,
|
||||
name: "获取菜单",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}auth/menu/add`,
|
||||
name: "添加菜单",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}auth/menu/edit`,
|
||||
name: "编辑菜单",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}auth/menu/delete`,
|
||||
name: "删除菜单",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import config from "@/config"
|
||||
import http from "@/utils/request"
|
||||
|
||||
export default {
|
||||
upload: {
|
||||
url: `${config.API_URL}system/file/upload`,
|
||||
name: "文件上传",
|
||||
post: async function(data, config={}){
|
||||
return await http.post(this.url, data, config);
|
||||
}
|
||||
},
|
||||
ckeditor: `${config.API_URL}system/file/ckeditor`,
|
||||
file: {
|
||||
menu: {
|
||||
url: `${config.API_URL}system/file/menu`,
|
||||
name: "获取文件分类",
|
||||
get: async function(){
|
||||
return await http.get(this.url);
|
||||
}
|
||||
},
|
||||
list: {
|
||||
url: `${config.API_URL}system/file/list`,
|
||||
name: "获取文件列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import config from "@/config";
|
||||
import http from "@/utils/request";
|
||||
|
||||
export default {
|
||||
lists: {
|
||||
list: {
|
||||
url: `${config.API_URL}member/index/index`,
|
||||
name: "获得会员列表",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}member/index/add`,
|
||||
name: "添加会员",
|
||||
post: async function (params) {
|
||||
return await http.post(this.url, params);
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}member/index/edit`,
|
||||
name: "编辑会员",
|
||||
post: async function (params) {
|
||||
return await http.put(this.url, params);
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}member/index/delete`,
|
||||
name: "删除会员",
|
||||
post: async function (params) {
|
||||
return await http.delete(this.url, params);
|
||||
},
|
||||
},
|
||||
import: {
|
||||
url: `${config.API_URL}member/index/import`,
|
||||
name: "导入会员",
|
||||
post: async function (params) {
|
||||
return await http.post(this.url, params);
|
||||
},
|
||||
},
|
||||
export: {
|
||||
url: `${config.API_URL}member/index/export`,
|
||||
name: "导出会员",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
},
|
||||
level: {
|
||||
list: {
|
||||
url: `${config.API_URL}member/level/index`,
|
||||
name: "获得会员等级列表",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}member/level/add`,
|
||||
name: "添加会员等级",
|
||||
post: async function (params) {
|
||||
return await http.post(this.url, params);
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}member/level/edit`,
|
||||
name: "编辑会员等级",
|
||||
post: async function (params) {
|
||||
return await http.put(this.url, params);
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}member/level/delete`,
|
||||
name: "删除会员等级",
|
||||
post: async function (params) {
|
||||
return await http.delete(this.url, params);
|
||||
},
|
||||
},
|
||||
},
|
||||
field: {
|
||||
list: {
|
||||
url: `${config.API_URL}member/field/index`,
|
||||
name: "获得会员字段列表",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}member/field/add`,
|
||||
name: "添加会员字段",
|
||||
post: async function (params) {
|
||||
return await http.post(this.url, params);
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}member/field/edit`,
|
||||
name: "编辑会员字段",
|
||||
post: async function (params) {
|
||||
return await http.put(this.url, params);
|
||||
},
|
||||
},
|
||||
field: {
|
||||
url: `${config.API_URL}member/field/fields`,
|
||||
name: "获得会员字段",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}member/field/delete`,
|
||||
name: "删除会员字段",
|
||||
post: async function (params) {
|
||||
return await http.delete(this.url, params);
|
||||
},
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
list: {
|
||||
url: `${config.API_URL}member/extend/index`,
|
||||
name: "获得会员扩展列表",
|
||||
get: async function (params) {
|
||||
return await http.get(this.url, params);
|
||||
},
|
||||
},
|
||||
audit: {
|
||||
url: `${config.API_URL}member/extend/audit`,
|
||||
name: "申请审核",
|
||||
post: async function (params) {
|
||||
return await http.put(this.url, params);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,342 @@
|
||||
import config from "@/config"
|
||||
import http from "@/utils/request"
|
||||
|
||||
export default {
|
||||
version:{
|
||||
url: `${config.API_URL}system/index/version`,
|
||||
name: "获取最新版本号",
|
||||
get: async function(){
|
||||
return await http.get(this.url);
|
||||
}
|
||||
},
|
||||
clearcache: {
|
||||
url: `${config.API_URL}system/index/clearcache`,
|
||||
name: "清除缓存",
|
||||
post: async function(){
|
||||
return await http.post(this.url);
|
||||
}
|
||||
},
|
||||
info: {
|
||||
url: `${config.API_URL}system/index/info`,
|
||||
name: "系统信息",
|
||||
get: function(data){
|
||||
return http.get(this.url, data);
|
||||
}
|
||||
},
|
||||
setting:{
|
||||
list: {
|
||||
url: `${config.API_URL}system/setting/index`,
|
||||
name: "获取配置信息",
|
||||
get: function(params){
|
||||
return http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
fields: {
|
||||
url: `${config.API_URL}system/setting/fields`,
|
||||
name: "获取配置字段",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}system/setting/add`,
|
||||
name: "保存配置信息",
|
||||
post: function(data){
|
||||
return http.post(this.url, data);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}system/setting/edit`,
|
||||
name: "编辑配置信息",
|
||||
post: function(data){
|
||||
return http.put(this.url, data);
|
||||
}
|
||||
},
|
||||
save: {
|
||||
url: `${config.API_URL}system/setting/save`,
|
||||
name: "保存配置信息",
|
||||
post: function(data){
|
||||
return http.put(this.url, data);
|
||||
}
|
||||
}
|
||||
},
|
||||
dictionary: {
|
||||
category: {
|
||||
url: `${config.API_URL}system/dict/category`,
|
||||
name: "获取字典树",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
editcate:{
|
||||
url: `${config.API_URL}system/dict/editcate`,
|
||||
name: "编辑字典树",
|
||||
post: async function(data = {}){
|
||||
return await http.put(this.url, data);
|
||||
}
|
||||
},
|
||||
addcate:{
|
||||
url: `${config.API_URL}system/dict/addcate`,
|
||||
name: "添加字典树",
|
||||
post: async function(data = {}){
|
||||
return await http.post(this.url, data);
|
||||
}
|
||||
},
|
||||
delCate:{
|
||||
url: `${config.API_URL}system/dict/deletecate`,
|
||||
name: "删除字典树",
|
||||
post: async function(data = {}){
|
||||
return await http.delete(this.url, data);
|
||||
}
|
||||
},
|
||||
list: {
|
||||
url: `${config.API_URL}system/dict/lists`,
|
||||
name: "字典明细",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
get: {
|
||||
url: `${config.API_URL}system/dict/detail`,
|
||||
name: "获取字典数据",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
edit:{
|
||||
url: `${config.API_URL}system/dict/edit`,
|
||||
name: "编辑字典明细",
|
||||
post: async function(data = {}){
|
||||
return await http.put(this.url, data);
|
||||
}
|
||||
},
|
||||
add:{
|
||||
url: `${config.API_URL}system/dict/add`,
|
||||
name: "添加字典明细",
|
||||
post: async function(data = {}){
|
||||
return await http.post(this.url, data);
|
||||
}
|
||||
},
|
||||
delete:{
|
||||
url: `${config.API_URL}system/dict/delete`,
|
||||
name: "删除字典明细",
|
||||
post: async function(data = {}){
|
||||
return await http.delete(this.url, data);
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
url: `${config.API_URL}system/dict/detail`,
|
||||
name: "字典明细",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
alldic: {
|
||||
url: `${config.API_URL}system/dict/all`,
|
||||
name: "全部字典",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
area: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/area/index`,
|
||||
name: "地区列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}system/area/add`,
|
||||
name: "地区添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}system/area/edit`,
|
||||
name: "地区编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
},
|
||||
app: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/app/list`,
|
||||
name: "应用列表",
|
||||
get: async function(){
|
||||
return await http.get(this.url);
|
||||
}
|
||||
}
|
||||
},
|
||||
client: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/client/index`,
|
||||
name: "客户端列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}system/client/add`,
|
||||
name: "客户端添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}system/client/edit`,
|
||||
name: "客户端编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}system/client/delete`,
|
||||
name: "客户端删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/menu/index`,
|
||||
name: "客户端菜单列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}system/menu/add`,
|
||||
name: "客户端菜单添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}system/menu/edit`,
|
||||
name: "客户端菜单编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}system/menu/delete`,
|
||||
name: "客户端菜单删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
log: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/log/index`,
|
||||
name: "日志列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
my: {
|
||||
url: `${config.API_URL}system/log/my`,
|
||||
name: "我的日志",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}system/log/delete`,
|
||||
name: "日志删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
tasks: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/tasks/index`,
|
||||
name: "任务列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}system/tasks/delete`,
|
||||
name: "任务删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
},
|
||||
},
|
||||
crontab: {
|
||||
list: {
|
||||
url: `${config.API_URL}system/crontab/index`,
|
||||
name: "定时任务列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
add: {
|
||||
url: `${config.API_URL}system/crontab/add`,
|
||||
name: "定时任务添加",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
url: `${config.API_URL}system/crontab/edit`,
|
||||
name: "定时任务编辑",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
url: `${config.API_URL}system/crontab/delete`,
|
||||
name: "定时任务删除",
|
||||
post: async function(params){
|
||||
return await http.delete(this.url, params);
|
||||
}
|
||||
},
|
||||
log: {
|
||||
url: `${config.API_URL}system/crontab/log`,
|
||||
name: "定时任务日志",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
reload: {
|
||||
url: `${config.API_URL}system/crontab/reload`,
|
||||
name: "定时任务重载",
|
||||
post: async function(params){
|
||||
return await http.put(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
modules:{
|
||||
list: {
|
||||
url: `${config.API_URL}system/modules/index`,
|
||||
name: "模块列表",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
},
|
||||
update: {
|
||||
url: `${config.API_URL}system/modules/update`,
|
||||
name: "更新模块",
|
||||
post: async function(params){
|
||||
return await http.post(this.url, params);
|
||||
}
|
||||
}
|
||||
},
|
||||
sms: {
|
||||
count: {
|
||||
url: `${config.API_URL}system/sms/count`,
|
||||
name: "短信发送统计",
|
||||
get: async function(params){
|
||||
return await http.get(this.url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M468.1 320.3V158.7c0.9-8.1-1.3-16.4-7.5-22.7-10.9-10.8-28.4-10.8-39.3 0L147.6 436.1c-5.8 5.7-8.3 13.4-7.9 20.9-0.4 7.5 2.1 15.1 7.9 20.9l272.2 298.4c10.2 8.7 28.8 13.6 40.8 1.6 6.2-6.2 8.9-11.4 8-19.5V594c180.8 0 344.4 130.2 376.8 301.6 21.9-50.2 34.1-105.6 34.1-163.9 0.1-227.2-184.1-411.4-411.4-411.4z" p-id="1450"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M258.389333 354.133333a299.093333 299.093333 0 0 1 8.490667-12.8h490.24c2.944 4.181333 5.76 8.490667 8.490667 12.8l86.186666-49.749333 42.666667 73.898667-94.421333 54.528c6.912 25.173333 10.624 51.754667 10.624 79.189333v42.666667h128v85.333333h-128a296.96 296.96 0 0 1-22.869334 114.773333l106.666667 61.610667-42.666667 73.898667-107.776-62.208A298.325333 298.325333 0 0 1 554.666667 935.637333V597.333333h-85.333334v338.346667a298.325333 298.325333 0 0 1-189.354666-107.605333l-107.776 62.208-42.666667-73.898667 106.666667-61.568A297.770667 297.770667 0 0 1 213.333333 640H85.333333v-85.333333h128v-42.666667c0-27.434667 3.712-53.973333 10.624-79.189333L129.536 378.282667l42.666667-73.898667L258.389333 354.133333zM341.333333 256a170.666667 170.666667 0 1 1 341.333334 0H341.333333z" p-id="26147"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M554.666667 849.066667a213.418667 213.418667 0 0 0 170.666666-209.066667v-128a212.48 212.48 0 0 0-17.706666-85.333333h-391.253334A212.48 212.48 0 0 0 298.666667 512v128a213.418667 213.418667 0 0 0 170.666666 209.066667V597.333333h85.333334v251.733334z m-318.464-94.293334A297.770667 297.770667 0 0 1 213.333333 640H85.333333v-85.333333h128v-42.666667c0-27.434667 3.712-53.973333 10.624-79.189333L129.536 378.282667l42.666667-73.898667L258.389333 354.133333a299.093333 299.093333 0 0 1 8.490667-12.8h490.24c2.944 4.181333 5.76 8.490667 8.490667 12.8l86.186666-49.749333 42.666667 73.898667-94.421333 54.528c6.912 25.173333 10.624 51.754667 10.624 79.189333v42.666667h128v85.333333h-128a296.96 296.96 0 0 1-22.869334 114.773333l106.666667 61.610667-42.666667 73.898667-107.776-62.208A298.069333 298.069333 0 0 1 512 938.666667a298.069333 298.069333 0 0 1-232.021333-110.592l-107.776 62.208-42.666667-73.898667 106.666667-61.568zM341.333333 256a170.666667 170.666667 0 1 1 341.333334 0H341.333333z" p-id="25873"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M981.333333 512l-301.696 301.696-60.330666-60.330667L860.672 512l-241.365333-241.365333 60.330666-60.330667L981.333333 512zM163.328 512l241.365333 241.365333-60.330666 60.330667L42.666667 512l301.696-301.696 60.330666 60.330667L163.328 512z" p-id="4503"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M554.666667 426.666667h213.333333l-256 256-256-256h213.333333V128h85.333334v298.666667z m-384 384h682.666666v-298.666667h85.333334v341.333333a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667v-341.333333h85.333334v298.666667z" p-id="26056"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M121.984 122.752l536.32-76.586667a21.333333 21.333333 0 0 1 24.362667 21.12v889.429334a21.333333 21.333333 0 0 1-24.32 21.12L121.941333 901.248a42.666667 42.666667 0 0 1-36.650666-42.24V164.992a42.666667 42.666667 0 0 1 36.650666-42.24zM725.333333 128h170.666667a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667h-170.666667V128z m-290.133333 384L554.666667 341.333333h-102.4L384 438.869333 315.733333 341.333333H213.333333l119.466667 170.666667L213.333333 682.666667h102.4L384 585.130667 452.266667 682.666667H554.666667l-119.466667-170.666667z" p-id="3794"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M725.333333 128h170.666667a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667h-170.666667V128zM121.984 122.752l536.32-76.586667a21.333333 21.333333 0 0 1 24.362667 21.12v889.429334a21.333333 21.333333 0 0 1-24.32 21.12L121.941333 901.248a42.666667 42.666667 0 0 1-36.650666-42.24V164.992a42.666667 42.666667 0 0 1 36.650666-42.24zM213.333333 341.333333v341.333334h85.333334v-85.333334h256V341.333333H213.333333z m85.333334 85.333334h170.666666v85.333333H298.666667v-85.333333z" p-id="3925"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M725.333333 128h170.666667a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667h-170.666667V128zM121.984 122.752l536.32-76.586667a21.333333 21.333333 0 0 1 24.362667 21.12v889.429334a21.333333 21.333333 0 0 1-24.32 21.12L121.941333 901.248a42.666667 42.666667 0 0 1-36.650666-42.24V164.992a42.666667 42.666667 0 0 1 36.650666-42.24zM469.333333 341.333333v212.864L384 469.333333l-84.906667 85.333334L298.666667 341.333333H213.333333v341.333334h85.333334l85.333333-85.333334 85.333333 85.333334h85.333334V341.333333h-85.333334z" p-id="3663"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M640 128a42.666667 42.666667 0 0 1 42.666667 42.666667v170.666666a42.666667 42.666667 0 0 1-42.666667 42.666667h-85.333333v85.333333h170.666666a42.666667 42.666667 0 0 1 42.666667 42.666667v128h85.333333a42.666667 42.666667 0 0 1 42.666667 42.666667v170.666666a42.666667 42.666667 0 0 1-42.666667 42.666667h-256a42.666667 42.666667 0 0 1-42.666666-42.666667v-170.666666a42.666667 42.666667 0 0 1 42.666666-42.666667h85.333334v-85.333333H341.333333v85.333333h85.333334a42.666667 42.666667 0 0 1 42.666666 42.666667v170.666666a42.666667 42.666667 0 0 1-42.666666 42.666667H170.666667a42.666667 42.666667 0 0 1-42.666667-42.666667v-170.666666a42.666667 42.666667 0 0 1 42.666667-42.666667h85.333333v-128a42.666667 42.666667 0 0 1 42.666667-42.666667h170.666666V384H384a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667h256zM384 725.333333H213.333333v85.333334h170.666667v-85.333334z m426.666667 0h-170.666667v85.333334h170.666667v-85.333334zM597.333333 213.333333h-170.666666v85.333334h170.666666V213.333333z" p-id="51975"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M170.666667 810.666667h682.666666v-298.666667h85.333334v341.333333a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667v-341.333333h85.333334v298.666667z m384-426.666667v298.666667h-85.333334V384H256l256-256 256 256h-213.333333z" p-id="25917"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M42.666667 128h170.666666l298.666667 512 298.666667-512h170.666666L512 938.666667 42.666667 128z m369.792 0L512 298.666667l99.541333-170.666667h172.16L512 597.333333 240.298667 128h172.16z" p-id="4634"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M792.490667 585.002667a38.826667 38.826667 0 0 0 38.314666-38.314667c0-21.248-17.024-38.314667-38.314666-38.314667s-38.314667 17.066667-38.314667 38.314667c0 21.333333 17.066667 38.314667 38.314667 38.314667z m-188.8 0a38.826667 38.826667 0 0 0 38.314666-38.314667c0-21.248-17.066667-38.314667-38.314666-38.314667-21.333333 0-38.314667 17.066667-38.314667 38.314667 0 21.333333 17.024 38.314667 38.314667 38.314667z m280.192 215.04a14.805333 14.805333 0 0 0-7.338667 15.786666c0 2.048 0 4.138667 1.066667 6.272 4.181333 17.792 12.544 46.122667 12.544 47.189334 0 3.114667 1.066667 5.205333 1.066666 7.338666a9.386667 9.386667 0 0 1-9.429333 9.386667c-2.133333 0-3.157333-1.024-5.248-2.048l-61.824-35.669333a34.090667 34.090667 0 0 0-14.677333-4.181334c-3.114667 0-6.272 0-8.362667 1.024-29.354667 8.405333-59.733333 12.586667-92.202667 12.586667-156.16 0-281.898667-104.832-281.898666-234.88 0-130.005333 125.738667-234.88 281.898666-234.88 156.117333 0 281.856 104.874667 281.856 234.88 0 70.272-37.717333 134.229333-97.450666 177.237333zM711.381333 345.557333a388.48 388.48 0 0 0-11.946666-0.213333c-178.090667 0-324.522667 122.026667-324.522667 277.546667 0 23.637333 3.413333 46.506667 9.728 68.266666h-3.797333a425.088 425.088 0 0 1-110.250667-15.701333c-3.157333-1.066667-6.314667-1.066667-9.472-1.066667a35.498667 35.498667 0 0 0-17.834667 5.248l-74.581333 42.88c-2.133333 1.066667-4.224 2.133333-6.314667 2.133334a11.648 11.648 0 0 1-11.52-11.52c0-3.157333 1.024-5.248 2.090667-8.405334 1.024-1.024 10.496-35.584 15.744-56.490666 0-2.133333 1.024-5.248 1.024-7.338667a23.722667 23.722667 0 0 0-9.429333-18.858667C87.808 570.709333 42.666667 494.336 42.666667 409.514667 42.666667 253.653333 194.986667 128 381.866667 128c160.64 0 295.68 92.544 329.514666 217.514667z m-219.904 17.834667c24.448 0 43.776-20.352 43.776-43.776 0-24.448-19.328-43.776-43.776-43.776s-43.776 19.328-43.776 43.776 19.328 43.776 43.776 43.776z m-224.426666 0c24.448 0 43.818667-20.352 43.818666-43.776 0-24.448-19.370667-43.776-43.818666-43.776-24.405333 0-43.776 19.328-43.776 43.776s19.370667 43.776 43.776 43.776z" p-id="4372"></path></svg>
|
||||
</template>
|
||||
@@ -0,0 +1,12 @@
|
||||
export { default as Vue } from './Vue.vue'
|
||||
export { default as Code } from './Code.vue'
|
||||
export { default as Wechat } from './Wechat.vue'
|
||||
export { default as BugFill } from './BugFill.vue'
|
||||
export { default as BugLine } from './BugLine.vue'
|
||||
export { default as FileWord } from './FileWord.vue'
|
||||
export { default as FileExcel } from './FileExcel.vue'
|
||||
export { default as FilePpt } from './FilePpt.vue'
|
||||
export { default as Organization } from './Organization.vue'
|
||||
export { default as Upload } from './Upload.vue'
|
||||
export { default as Download } from './Download.vue'
|
||||
export { default as Back } from './Back.vue'
|
||||
@@ -0,0 +1,103 @@
|
||||
/* 全局 */
|
||||
#app, body, html {width: 100%;height: 100%;background-color: #f6f8f9;font-size: 12px;}
|
||||
a {color: #333;text-decoration: none;}
|
||||
a:hover, a:focus {color: #000;text-decoration: none;}
|
||||
a:link {text-decoration: none;}
|
||||
a:-webkit-any-link {text-decoration: none;}
|
||||
a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: border-box;outline:none !important; -webkit-appearance: none;}
|
||||
* {margin: 0;padding: 0;box-sizing: border-box;outline: none;}
|
||||
|
||||
/* 大布局样式 */
|
||||
.aminui {display: flex;flex-flow: column;}
|
||||
.aminui-wrapper {display: flex;flex:1;overflow: auto;}
|
||||
|
||||
/* 全局滚动条样式 */
|
||||
.scrollable {-webkit-overflow-scrolling: touch;}
|
||||
::-webkit-scrollbar {width: 5px;height: 5px;}
|
||||
::-webkit-scrollbar-thumb {background-color: rgba(50, 50, 50, 0.3);}
|
||||
::-webkit-scrollbar-thumb:hover {background-color: rgba(50, 50, 50, 0.6);}
|
||||
::-webkit-scrollbar-track {background-color: rgba(50, 50, 50, 0.1);}
|
||||
::-webkit-scrollbar-track:hover {background-color: rgba(50, 50, 50, 0.2);}
|
||||
|
||||
/*布局设置*/
|
||||
.layout-setting {position: fixed;width: 40px;height: 40px;border-radius: 3px 0 0 3px;bottom: 100px;right: 0px;z-index: 100;background: #409EFF;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;}
|
||||
.layout-setting i {font-size: 18px;color: #fff;}
|
||||
|
||||
/* 头部 */
|
||||
.adminui-header {height: 58px;background: #fff;color: #222b45; border-bottom: 1px solid rgba(0, 0, 0, 0.05); display: flex;justify-content:space-between;}
|
||||
.adminui-header-left {display: flex;align-items: center;padding-left:20px;}
|
||||
.adminui-header-right {display: flex;align-items: center;}
|
||||
.adminui-header .logo-bar {font-size: 20px;font-weight: bold;display: flex;align-items: center;}
|
||||
.adminui-header .logo-bar .logo {margin-right: 10px;height: 35px;}
|
||||
.adminui-header .nav {display: flex;height: 100%;margin-left: 40px;}
|
||||
.adminui-header .nav li {padding:0 10px;margin: 0 10px 0 0;font-size: 14px;color: rgba(255, 255, 255, 0.6);list-style: none;height: 100%;display: flex;align-items: center;cursor: pointer;}
|
||||
.adminui-header .nav li i {margin-right: 5px;}
|
||||
.adminui-header .nav li:hover {color: #222b45;}
|
||||
.adminui-header .nav li.active {background: rgba(255, 255, 255, 0.1);color: #222b45;}
|
||||
.adminui-header .user-bar .panel-item:hover {background: rgba(255, 255, 255, 0.1)!important;}
|
||||
.adminui-header .user-bar .user label{color: #222b45;}
|
||||
|
||||
/* 左侧菜单 */
|
||||
.aminui-side-split {width:65px;flex-shrink:0;background: #222b45;display: flex;flex-flow: column;}
|
||||
.aminui-side-split-top {height: 49px;}
|
||||
.aminui-side-split-top a {display: inline-block;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}
|
||||
.aminui-side-split-top .logo {height:30px;vertical-align: bottom;}
|
||||
.adminui-side-split-scroll {overflow: auto;overflow-x:hidden;height: 100%;flex: 1;}
|
||||
.aminui-side-split li {cursor: pointer;width: 65px;height: 65px;color: #fff;text-align: center;display: flex;flex-direction: column;align-items: center;justify-content: center;}
|
||||
.aminui-side-split li i {font-size: 18px;}
|
||||
.aminui-side-split li p {margin-top:5px;}
|
||||
.aminui-side-split li:hover {background: rgba(255, 255, 255, 0.1);}
|
||||
.aminui-side-split li.active {background: #409EFF;}
|
||||
|
||||
.adminui-side-split-scroll::-webkit-scrollbar-thumb {background-color: rgba(255, 255, 255, 0.4);border-radius:5px;}
|
||||
.adminui-side-split-scroll::-webkit-scrollbar-thumb:hover {background-color: rgba(255, 255, 255, 0.5);}
|
||||
.adminui-side-split-scroll::-webkit-scrollbar-track {background-color: rgba(255, 255, 255, 0);}
|
||||
.adminui-side-split-scroll::-webkit-scrollbar-track:hover {background-color: rgba(255, 255, 255, 0);}
|
||||
|
||||
.aminui-side {display: flex;flex-flow: column;flex-shrink:0;width:210px;background: #fff;box-shadow: 2px 0 8px 0 rgba(29,35,41,.05);border-right: 1px solid #e6e6e6;transition:width 0.3s;}
|
||||
.adminui-side-top {border-bottom: 1px solid #ebeef5;height:50px;line-height: 50px;}
|
||||
.adminui-side-top h2 {padding:0 20px;font-size: 17px;color: #3c4a54;}
|
||||
.adminui-side-scroll {overflow: auto;overflow-x:hidden;flex: 1;}
|
||||
.adminui-side-bottom {border-top: 1px solid #ebeef5;height:51px;cursor: pointer;display: flex;align-items: center;justify-content: center;}
|
||||
.adminui-side-bottom i {font-size: 16px;}
|
||||
.adminui-side-bottom:hover {color: var(--el-color-primary);}
|
||||
.aminui-side.isCollapse {width: 65px;}
|
||||
.el-menu .menu-tag {position: absolute;height: 18px;line-height: 18px;background: var(--el-color-danger);font-size: 12px;color: #fff;right: 20px;border-radius:18px;padding:0 6px;}
|
||||
.el-menu .el-sub-menu__title .menu-tag {right: 40px;}
|
||||
.el-menu--horizontal > li .menu-tag {display: none;}
|
||||
|
||||
/* 右侧内容 */
|
||||
.aminui-body {flex: 1;display: flex;flex-flow: column;}
|
||||
|
||||
.adminui-topbar {height: 50px;border-bottom: 1px solid #ebeef5;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);display: flex;justify-content:space-between;}
|
||||
.adminui-topbar .left-panel {display: flex;align-items: center;}
|
||||
.adminui-topbar .right-panel {display: flex;align-items: center;}
|
||||
|
||||
.right-panel-search {display: flex;align-items: center;}
|
||||
.right-panel-search > * + * {margin-left:10px;}
|
||||
|
||||
.adminui-tags {height:35px;background: #fff;border-bottom: 1px solid #e6e6e6;}
|
||||
.adminui-tags ul {display: flex;overflow: hidden;}
|
||||
.adminui-tags li {cursor: pointer;display: inline-block;float: left;height:34px;line-height: 34px;position: relative;flex-shrink: 0;}
|
||||
.adminui-tags li::after {content: " ";width:1px;height:100%;position: absolute;right:0px;background-image: linear-gradient(#fff, #e6e6e6);}
|
||||
.adminui-tags li a {display: inline-block;padding:0 10px;width:100%;height:100%;color: #999;text-decoration:none;display: flex;align-items: center;}
|
||||
.adminui-tags li i {margin-left:10px;border-radius: 3px;width:18px;height:18px;display: flex;align-items: center;justify-content: center;}
|
||||
.adminui-tags li i:hover {background: rgba(0,0,0,.2);color: #fff;}
|
||||
.adminui-tags li:hover {background: #ecf5ff;}
|
||||
.adminui-tags li.active {background: #409EFF;}
|
||||
.adminui-tags li.active a {color: #fff;}
|
||||
.adminui-tags li.sortable-ghost {opacity: 0;}
|
||||
|
||||
.adminui-main {overflow: auto;background-color: #f6f8f9;flex: 1;}
|
||||
|
||||
/*页面最大化*/
|
||||
.aminui.main-maximize {
|
||||
.main-maximize-exit {display: block;}
|
||||
.aminui-side-split, .aminui-side, .adminui-header, .adminui-topbar, .adminui-tags {display: none;}
|
||||
}
|
||||
.main-maximize-exit {display: none;position: fixed;z-index: 3000;top:-20px;left:50%;margin-left: -20px;border-radius: 50%;width: 40px;height: 40px;cursor: pointer;background: rgba(0,0,0,0.2);text-align: center;}
|
||||
.main-maximize-exit i {font-size: 14px;margin-top: 22px;color: #fff;}
|
||||
.main-maximize-exit:hover {background: rgba(0,0,0,0.4);}
|
||||
|
||||
/*定宽页面*/
|
||||
.sc-page {width: 1230px;margin: 0 auto;}
|
||||
@@ -0,0 +1,37 @@
|
||||
@import 'element-plus/theme-chalk/src/dark/css-vars.scss';
|
||||
|
||||
html.dark {
|
||||
//变量
|
||||
--el-text-color-primary: #d0d0d0;
|
||||
--el-color-primary-dark-2: var(--el-color-primary-light-2) !important;
|
||||
--el-color-primary-light-9: var(--el-color-primary-dark-8) !important;
|
||||
--el-color-primary-light-8: var(--el-color-primary-dark-7) !important;
|
||||
--el-color-primary-light-7: var(--el-color-primary-dark-6) !important;
|
||||
--el-color-primary-light-5: var(--el-color-primary-dark-4) !important;
|
||||
--el-color-primary-light-3: var(--el-color-primary-dark-3) !important;
|
||||
|
||||
//背景
|
||||
#app {background: var(--el-bg-color);}
|
||||
|
||||
//登录背景
|
||||
.login_bg {background: var(--el-bg-color);}
|
||||
|
||||
//框架
|
||||
.adminui-header {background: var(--el-bg-color-overlay);border-bottom: 1px solid var(--el-border-color-light);height:59px;}
|
||||
.aminui-side-split {background: var(--el-bg-color);}
|
||||
.aminui-side-split li {color: var(--el-text-color-primary);}
|
||||
.aminui-side {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
|
||||
.adminui-side-top, .adminui-side-bottom {border-color: var(--el-border-color-light);}
|
||||
.adminui-side-top h2 {color: var(--el-text-color-primary);}
|
||||
.adminui-topbar, .adminui-tags {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
|
||||
.adminui-main {background: var(--el-bg-color);}
|
||||
.drawerBG {background: var(--el-bg-color);}
|
||||
.adminui-header-menu .el-menu {--el-menu-bg-color:var(--el-bg-color-overlay) !important;--el-menu-hover-bg-color: #171819 !important;}
|
||||
|
||||
//组件
|
||||
.el-header, .el-main.nopadding, .el-footer {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
|
||||
.el-main {background: var(--el-bg-color);}
|
||||
.el-aside {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
|
||||
.el-table .el-table__body-wrapper {background: var(--el-bg-color);}
|
||||
.el-table th.is-sortable:hover {background: #111;}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button { -webkit-appearance: none;}
|
||||
|
||||
.action-icon{cursor: pointer; width: 1.2em; height: 1.2em; color: #409EFC; vertical-align: middle; margin-right: 5px;}
|
||||
.el-tabs__new-tab{margin: 10px;}
|
||||
|
||||
.el-drawer__header{margin-bottom: 10px;}
|
||||
|
||||
.el-card__header{padding: 10px; padding-bottom: 0; font-size: 14px;}
|
||||
.el-card__body{padding: 10px;}
|
||||
|
||||
.el-dialog__body{padding: calc(var(--el-dialog-padding-primary)) var(--el-dialog-padding-primary);}
|
||||
.el-upload-dragger, .el-upload-dragger.is-dragover{padding: 0; border: none;}
|
||||
@@ -0,0 +1,83 @@
|
||||
/* 覆盖element-plus样式 */
|
||||
|
||||
:root {
|
||||
--el-color-primary: #409EFF;
|
||||
--el-color-primary-light-1: #53a7ff;
|
||||
--el-color-primary-light-2: #66b1ff;
|
||||
--el-color-primary-light-3: #79bbff;
|
||||
--el-color-primary-light-4: #8cc4ff;
|
||||
--el-color-primary-light-5: #9fceff;
|
||||
--el-color-primary-light-6: #b2d8ff;
|
||||
--el-color-primary-light-7: #c5e1ff;
|
||||
--el-color-primary-light-8: #d8ebff;
|
||||
--el-color-primary-light-9: #ebf5ff;
|
||||
--el-color-primary-dark-1: #398ee5;
|
||||
--el-color-primary-dark-2: #337ecc;
|
||||
--el-color-primary-dark-3: #2c6eb2;
|
||||
--el-color-primary-dark-4: #265e99;
|
||||
--el-color-primary-dark-5: #204f7f;
|
||||
--el-color-primary-dark-6: #193f66;
|
||||
--el-color-primary-dark-7: #132f4c;
|
||||
--el-color-primary-dark-8: #0c1f32;
|
||||
--el-color-primary-dark-9: #060f19;
|
||||
}
|
||||
|
||||
.el-menu {border: none!important;}
|
||||
.el-menu .el-menu-item a {color: inherit;text-decoration: none;display: block;width:100%;height:100%;position: absolute;top:0px;left:0px;}
|
||||
.el-form-item-msg {font-size: 12px;color: #999;clear: both;width: 100%;}
|
||||
.el-container {height: 100%;}
|
||||
.el-aside {border-right: 1px solid var(--el-border-color-light);}
|
||||
.el-container + .el-aside {border-right: 0;border-left: 1px solid var(--el-border-color-light);}
|
||||
.el-header {background: #fff;border-bottom: 1px solid var(--el-border-color-light);padding:13px 15px;display: flex;justify-content: space-between;align-items: center;}
|
||||
.el-header .left-panel {display: flex;align-items: center;}
|
||||
.el-header .right-panel {display: flex;align-items: center;}
|
||||
.el-header .right-panel > * + * {margin-left:10px;}
|
||||
.el-footer {background: #fff;border-top: 1px solid var(--el-border-color-light);padding:13px 15px;height: 51px;}
|
||||
.el-main {padding:15px;}
|
||||
.el-main.nopadding {padding:0;background: #fff;}
|
||||
.el-drawer__body {overflow: auto;padding:0;}
|
||||
.el-popconfirm__main {margin: 14px 0;}
|
||||
.el-card__header {border-bottom: 0;font-size: 17px;font-weight: bold;padding:15px 20px 0px 20px;}
|
||||
.el-dialog__title {font-size: 17px;font-weight: bold;}
|
||||
.el-drawer__header>:first-child {font-size: 17px;font-weight: bold;}
|
||||
.el-tree.menu .el-tree-node__content {height:36px;}
|
||||
.el-tree.menu .el-tree-node__content .el-tree-node__label .icon {margin-right: 5px;}
|
||||
.el-progress__text {font-size: 12px!important;}
|
||||
.el-progress__text i {font-size: 14.4px!important;}
|
||||
.el-step.is-horizontal .el-step__line {height:1px;}
|
||||
.el-step__title {font-size: 14px;}
|
||||
.drawerBG {background: #f6f8f9;}
|
||||
.el-button+.el-dropdown {margin-left: 10px;}
|
||||
.el-button-group+.el-dropdown {margin-left: 10px;}
|
||||
.el-tag+.el-tag {margin-left: 10px;}
|
||||
.el-button-group+.el-button-group {margin-left: 10px;}
|
||||
.el-tabs__nav-wrap::after {height: 1px;}
|
||||
.el-table th.is-sortable {transition: .1s;}
|
||||
.el-table th.is-sortable:hover {background: #eee;}
|
||||
.el-table .el-table__body-wrapper {background: #f6f8f9;}
|
||||
.el-col .el-card {margin-bottom: 15px;}
|
||||
.el-main {flex-basis: 100%;}
|
||||
.el-main > .scTable .el-table--border::before {display: none;}
|
||||
.el-main > .scTable .el-table--border::after {display: none;}
|
||||
.el-main > .scTable .el-table--border .el-table__inner-wrapper::after {display: none;}
|
||||
.el-main > .scTable .el-table__border-left-patch {display: none;}
|
||||
.el-main > .scTable .el-table--border .el-table__inner-wrapper tr:first-child td:first-child {border-left: 0;}
|
||||
.el-main > .scTable .el-table--border .el-table__inner-wrapper tr:first-child th:first-child {border-left: 0;}
|
||||
.el-table.el-table--large {font-size: 14px;}
|
||||
.el-table.el-table--small {font-size: 12px;}
|
||||
.el-table {font-size: 12px;}
|
||||
.el-radio-button__inner {font-size: 12px;}
|
||||
.el-checkbox-button__inner {font-size: 12px;}
|
||||
.el-sub-menu .el-icon {font-size: 17px;}
|
||||
.el-sub-menu .el-sub-menu__icon-arrow {font-size: 12px;}
|
||||
|
||||
.aminui-side-split li.active {background-color: var(--el-color-primary);}
|
||||
.adminui-tags li:hover {background-color: var(--el-color-primary-light-9);}
|
||||
.adminui-tags li.active {background-color: var(--el-color-primary)!important;}
|
||||
.contextmenu li:hover {background-color: var(--el-color-primary-light-9)!important;color: var(--el-color-primary-light-2)!important;}
|
||||
.data-box .item-background {background-color: var(--el-color-primary)!important;}
|
||||
.layout-setting,.diy-grid-setting {background-color: var(--el-color-primary)!important;}
|
||||
|
||||
/* 覆盖tinymce样式 */
|
||||
.sceditor .tox-tinymce {border: 1px solid #DCDFE6;border-radius: 0;}
|
||||
body .tox-tinymce-aux {z-index: 5700;}
|
||||
@@ -0,0 +1,50 @@
|
||||
@media (max-width: 992px){
|
||||
// 移动端样式覆盖
|
||||
.el-form-item {display: block;}
|
||||
.el-form-item__label {display: block;text-align: left;padding: 0 0 10px;}
|
||||
.el-dialog {width: 90%!important;}
|
||||
.el-dialog.is-fullscreen {width: 100%!important;}
|
||||
.el-drawer.rtl {width: 90%!important;}
|
||||
.el-form-item__content {margin-left: 0px!important;}
|
||||
|
||||
.adminui-main {
|
||||
>.el-container {display: block;height:auto;}
|
||||
>.el-container > .el-aside {width: 100%!important;border: 0}
|
||||
}
|
||||
.scTable {
|
||||
.el-table,
|
||||
.el-table__body-wrapper {display: block!important;height:auto!important;}
|
||||
.el-scrollbar__wrap {height:auto!important;}
|
||||
.scTable-page {padding: 0 5px!important;}
|
||||
.el-pagination__total,
|
||||
.el-pagination__jump,
|
||||
.el-pagination__sizes {display: none!important;}
|
||||
}
|
||||
|
||||
.headerPublic {
|
||||
height: auto!important;display: block;
|
||||
.left-panel {overflow: auto;}
|
||||
.left-panel::-webkit-scrollbar{display: none;}
|
||||
.right-panel {display: block;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
|
||||
.right-panel .right-panel-search {display: block;}
|
||||
.right-panel .right-panel-search >* {width: 100%;margin: 0;margin-top: 15px;}
|
||||
}
|
||||
.adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) {border: 0;margin-top: 0;}
|
||||
.adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) + .el-aside {margin-top: 0;}
|
||||
.adminui-main > .el-container > .el-aside {border-bottom: 1px solid var(--el-border-color-light)!important;}
|
||||
.adminui-main > .el-container > .el-container {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
|
||||
.adminui-main > .el-container > .el-container + .el-aside {border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
|
||||
.adminui-main > .el-container > .el-header {@extend .headerPublic;}
|
||||
.adminui-main > .el-container > .el-main.nopadding {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
|
||||
.adminui-main > .el-container > .el-main + .el-aside {border-left: 0!important;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
|
||||
.adminui-main > .el-container > .el-footer {margin-top: 15px;border-bottom: 1px solid var(--el-border-color-light);}
|
||||
.adminui-main > .el-container > .el-container > .el-header {@extend .headerPublic}
|
||||
.adminui-main > .el-container > .el-container > .el-header .left-panel {display: block;}
|
||||
.adminui-main > .el-container > .el-container > .el-header .right-panel {display: block;margin-top: 15px;}
|
||||
|
||||
.sc-page {width: 100%;margin: 0;}
|
||||
|
||||
.common-main .el-form {width: 100% !important;}
|
||||
.common-header-logo label {display: none;}
|
||||
.common-header-title {display: none;}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/* USERCENTER */
|
||||
.page-user {
|
||||
.user-info-top {text-align: center;}
|
||||
.user-info-top h2 {font-size: 18px;margin-top: 5px;}
|
||||
.user-info-top p {margin: 8px 0 10px 0;}
|
||||
.menu {background: none;}
|
||||
.menu .el-menu-item {font-size: 12px;--el-menu-item-height:50px;}
|
||||
.menu .el-menu-item-group {border-top: 1px solid var(--el-border-color-light);}
|
||||
.menu .el-menu-item-group:first-child {border: 0;}
|
||||
}
|
||||
|
||||
/*static-table*/
|
||||
.static-table {border-collapse: collapse;width: 100%;font-size: 14px;margin-bottom: 45px;line-height: 1.5em;}
|
||||
.static-table th {text-align: left;white-space: nowrap;color: #909399;font-weight: 400;border-bottom: 1px solid #dcdfe6;padding: 15px;max-width: 250px;}
|
||||
.static-table td {border-bottom: 1px solid #dcdfe6;padding: 15px;max-width: 250px;color: #606266;}
|
||||
|
||||
/*header-tabs*/
|
||||
.header-tabs {padding:10px 0 0 0;display:block;border:0!important;height:50px;background: none;}
|
||||
.header-tabs .el-tabs__header {padding-left:10px;margin: 0;}
|
||||
.header-tabs .el-tabs__content {display: none;}
|
||||
.header-tabs .el-tabs__nav {border-radius: 0 !important;}
|
||||
.header-tabs .el-tabs__item {font-size: 13px;}
|
||||
.header-tabs .el-tabs__item.is-active {background-color: var(--el-bg-color-overlay);}
|
||||
|
||||
/*common-page*/
|
||||
.common-page {}
|
||||
.common-header-left {display: flex;align-items: center;}
|
||||
.common-header-logo {display: flex;align-items: center;}
|
||||
.common-header-logo img {height:30px;margin-right: 10px;vertical-align: bottom;}
|
||||
.common-header-logo label {font-size: 20px;}
|
||||
.common-header-title {font-size: 16px;border-left: 1px solid var(--el-border-color-light);margin-left: 15px;padding-left: 15px;}
|
||||
.common-header-right {display: flex;align-items: center;}
|
||||
.common-header-right a {font-size: 14px;color: var(--el-color-primary);cursor: pointer;}
|
||||
.common-header-right a:hover {color: var(--el-color-primary-light-3);}
|
||||
.common-container {max-width: 1240px;margin:30px auto 30px auto;}
|
||||
.common-main {padding:20px;}
|
||||
.common-title {font-size: 26px;margin-bottom: 20px;font-weight: normal;}
|
||||
.common-main .el-form {width: 500px;margin:30px auto;}
|
||||
.common-main .el-steps .el-step__title {font-size: 14px;}
|
||||
.common-main .el-steps .el-step__icon {border: 1px solid;}
|
||||
.common-main .yzm {display: flex;width: 100%;}
|
||||
.common-main .yzm .el-button {margin-left: 10px;}
|
||||
.common-main .link {color: var(--el-color-primary);cursor: pointer;}
|
||||
.common-main .link:hover {color: var(--el-color-primary-light-3);}
|
||||
@@ -0,0 +1,6 @@
|
||||
@import 'app.scss';
|
||||
@import 'fix.scss';
|
||||
@import 'pages.scss';
|
||||
@import 'media.scss';
|
||||
@import 'dark.scss';
|
||||
@import 'diy.scss';
|
||||
@@ -0,0 +1,115 @@
|
||||
<!--
|
||||
* @Descripttion: 代码编辑器
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2022年5月20日21:46:29
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-code-editor" :style="{'height':_height}">
|
||||
<textarea ref="textarea" v-model="contentValue"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { markRaw } from "vue"
|
||||
|
||||
//框架
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
|
||||
//主题
|
||||
import 'codemirror/theme/idea.css'
|
||||
import 'codemirror/theme/darcula.css'
|
||||
|
||||
//功能
|
||||
import 'codemirror/addon/selection/active-line'
|
||||
|
||||
//语言
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/mode/sql/sql'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: "javascript"
|
||||
},
|
||||
height: {
|
||||
type: [String,Number],
|
||||
default: 300,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: "idea"
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
contentValue: this.modelValue,
|
||||
coder: null,
|
||||
opt: {
|
||||
theme: this.theme, //主题
|
||||
styleActiveLine: true, //高亮当前行
|
||||
lineNumbers: true, //行号
|
||||
lineWrapping: false, //自动换行
|
||||
tabSize: 4, //Tab缩进
|
||||
indentUnit: 4, //缩进单位
|
||||
indentWithTabs : true, //自动缩进
|
||||
mode : this.mode, //语言
|
||||
readOnly: this.readOnly, //只读
|
||||
...this.options
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
_height() {
|
||||
return Number(this.height)?Number(this.height)+'px':this.height
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.contentValue = val
|
||||
if (val !== this.coder.getValue()) {
|
||||
this.coder.setValue(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
//获取挂载的所有modes
|
||||
//console.log(CodeMirror.modes)
|
||||
},
|
||||
methods: {
|
||||
init(){
|
||||
this.coder = markRaw(CodeMirror.fromTextArea(this.$refs.textarea, this.opt))
|
||||
this.coder.on('change', (coder) => {
|
||||
this.contentValue = coder.getValue()
|
||||
this.$emit('update:modelValue', this.contentValue)
|
||||
})
|
||||
},
|
||||
formatStrInJson(strValue) {
|
||||
return JSON.stringify(JSON.parse(strValue), null, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-code-editor {font-size: 14px;border: 1px solid #ddd;line-height: 150%;}
|
||||
.sc-code-editor:deep(.CodeMirror) {height: 100%;}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!--
|
||||
* @Descripttion: scContextmenu组件
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月23日09:25:57
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月30日20:17:42
|
||||
* @other: 代码完全开源,欢迎参考,也欢迎PR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<transition name="el-zoom-in-top">
|
||||
<div v-if="visible" ref="contextmenu" class="sc-contextmenu" :style="{left:left+'px',top:top+'px'}" @contextmenu.prevent="fun">
|
||||
<ul class="sc-contextmenu__menu">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
provide() {
|
||||
return {
|
||||
menuClick: this.menuClick
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
top: 0,
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.cm, true)
|
||||
}else{
|
||||
document.body.removeEventListener('click', this.cm, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
cm(e){
|
||||
let sp = this.$refs.contextmenu
|
||||
if(sp&&!sp.contains(e.target)){
|
||||
this.closeMenu()
|
||||
}
|
||||
},
|
||||
menuClick(command){
|
||||
this.closeMenu()
|
||||
this.$emit('command', command)
|
||||
},
|
||||
openMenu(e) {
|
||||
e.preventDefault()
|
||||
this.visible = true
|
||||
this.left = e.clientX + 1
|
||||
this.top = e.clientY + 1
|
||||
|
||||
this.$nextTick(() => {
|
||||
var ex = e.clientX + 1
|
||||
var ey = e.clientY + 1
|
||||
var innerWidth = window.innerWidth
|
||||
var innerHeight = window.innerHeight
|
||||
var menuHeight = this.$refs.contextmenu.offsetHeight
|
||||
var menuWidth = this.$refs.contextmenu.offsetWidth
|
||||
//位置修正公示
|
||||
//left = (当前点击X + 菜单宽度 > 可视区域宽度 ? 可视区域宽度 - 菜单宽度 : 当前点击X)
|
||||
//top = (当前点击Y + 菜单高度 > 可视区域高度 ? 当前点击Y - 菜单高度 : 当前点击Y)
|
||||
this.left = ex + menuWidth > innerWidth ? innerWidth - menuWidth : ex
|
||||
this.top = ey + menuHeight > innerHeight ? ey - menuHeight : ey
|
||||
})
|
||||
this.$emit('visibleChange', true)
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false;
|
||||
this.$emit('visibleChange', false)
|
||||
},
|
||||
fun(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.sc-contextmenu {position: fixed;z-index: 3000;font-size: 12px;}
|
||||
.sc-contextmenu__menu {display: inline-block;min-width: 120px;border: 1px solid #e4e7ed;background: #fff;box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);z-index: 3000;list-style-type: none;padding: 10px 0;}
|
||||
.sc-contextmenu__menu > hr {margin:5px 0;border: none;height: 1px;font-size: 0px;background-color: #ebeef5;}
|
||||
.sc-contextmenu__menu > li {margin:0;cursor: pointer;line-height: 30px;padding: 0 17px 0 10px;color: #606266;display: flex;justify-content: space-between;white-space: nowrap;text-decoration: none;position: relative;}
|
||||
.sc-contextmenu__menu > li:hover {background-color: #ecf5ff;color: #66b1ff;}
|
||||
.sc-contextmenu__menu > li.disabled {cursor: not-allowed;color: #bbb;background: transparent;}
|
||||
.sc-contextmenu__icon {display: inline-block;width: 14px;font-size: 14px;margin-right: 10px;}
|
||||
.sc-contextmenu__suffix {margin-left: 40px;color: #999;}
|
||||
.sc-contextmenu__menu li ul {position: absolute;top:0px;left:100%;display: none;margin: -11px 0;}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
* @Descripttion: scContextmenuItem组件
|
||||
* @version: 1.2
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月23日16:29:36
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年2月8日15:51:07
|
||||
-->
|
||||
|
||||
<template>
|
||||
<hr v-if="divided">
|
||||
<li :class="disabled?'disabled':''" @click.stop="liClick" @mouseenter="openSubmenu($event)" @mouseleave="closeSubmenu($event)">
|
||||
<span class="title">
|
||||
<el-icon class="sc-contextmenu__icon"><component v-if="icon" :is="icon" /></el-icon>
|
||||
{{title}}
|
||||
</span>
|
||||
<span class="sc-contextmenu__suffix">
|
||||
<el-icon v-if="$slots.default"><el-icon-arrow-right /></el-icon>
|
||||
<template v-else>{{suffix}}</template>
|
||||
</span>
|
||||
<ul v-if="$slots.default" class="sc-contextmenu__menu">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
command: { type: String, default: "" },
|
||||
title: { type: String, default: "" },
|
||||
suffix: { type: String, default: "" },
|
||||
icon: { type: String, default: "" },
|
||||
divided: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
},
|
||||
inject: ['menuClick'],
|
||||
methods: {
|
||||
liClick(){
|
||||
if(this.$slots.default){
|
||||
return false
|
||||
}
|
||||
if(this.disabled){
|
||||
return false
|
||||
}
|
||||
this.menuClick(this.command)
|
||||
},
|
||||
openSubmenu(e){
|
||||
var menu = e.target.querySelector('ul')
|
||||
if(!menu){
|
||||
return false
|
||||
}
|
||||
menu.style.display = 'inline-block'
|
||||
var rect = menu.getBoundingClientRect()
|
||||
var menuX = rect.left
|
||||
var menuY = rect.top
|
||||
var innerWidth = window.innerWidth
|
||||
var innerHeight = window.innerHeight
|
||||
var menuHeight = menu.offsetHeight
|
||||
var menuWidth = menu.offsetWidth
|
||||
if(menuX + menuWidth > innerWidth){
|
||||
menu.style.left = 'auto'
|
||||
menu.style.right = '100%'
|
||||
}
|
||||
if(menuY + menuHeight > innerHeight){
|
||||
menu.style.top = 'auto'
|
||||
menu.style.bottom = '0'
|
||||
}
|
||||
},
|
||||
closeSubmenu(e){
|
||||
var menu = e.target.querySelector('ul')
|
||||
if(!menu){
|
||||
return false
|
||||
}
|
||||
menu.removeAttribute("style")
|
||||
menu.style.display = 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="任意值" :value="0"></el-radio-button>
|
||||
<el-radio-button label="范围" :value="1"></el-radio-button>
|
||||
<el-radio-button label="间隔" :value="2"></el-radio-button>
|
||||
<el-radio-button label="指定" :value="3"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" v-if="form.type==1">
|
||||
<el-input-number v-model="form.range.start" :min="1" :max="31" controls-position="right"></el-input-number>
|
||||
<span style="padding:0 15px;">-</span>
|
||||
<el-input-number v-model="form.range.end" :min="1" :max="31" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" v-if="form.type==2">
|
||||
<el-input-number v-model="form.loop.start" :min="1" :max="31" controls-position="right"></el-input-number>
|
||||
号开始,每
|
||||
<el-input-number v-model="form.loop.end" :min="1" :max="31" controls-position="right"></el-input-number>
|
||||
天执行一次
|
||||
</el-form-item>
|
||||
<el-form-item label="指定" v-if="form.type==3">
|
||||
<el-select v-model="form.appoint" multiple style="width: 100%;">
|
||||
<el-option v-for="(item, index) in day" :key="index" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: { type: String, default: "*" },
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
form: {
|
||||
type: '0',
|
||||
range: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
loop: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
appoint: []
|
||||
},
|
||||
day: []
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue:{
|
||||
handler(val){
|
||||
if(val == '*'){
|
||||
this.form.type = 0
|
||||
}else if(val.indexOf('-') > -1){
|
||||
this.form.type = 1
|
||||
}else if(val.indexOf('/') > -1){
|
||||
this.form.type = 2
|
||||
}else if(val.indexOf(',') > -1){
|
||||
this.form.type = 3
|
||||
}else{
|
||||
this.form.type = 3
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
"form": {
|
||||
handler(val){
|
||||
let data = ''
|
||||
if(val.type == 0){
|
||||
data = '*'
|
||||
}else if(val.type==1){
|
||||
data = this.form.range.start + '-' + this.form.range.end
|
||||
}else if(val.type==2){
|
||||
data = this.form.loop.start + '/' + this.form.loop.end
|
||||
}else if(val.type==3){
|
||||
data = this.form.appoint.length>0 ? this.form.appoint.join(',') : '1'
|
||||
}else{
|
||||
data = '*'
|
||||
}
|
||||
this.$emit('update:modelValue', data)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="任意值" :value="0"></el-radio-button>
|
||||
<el-radio-button label="范围" :value="1"></el-radio-button>
|
||||
<el-radio-button label="间隔" :value="2"></el-radio-button>
|
||||
<el-radio-button label="指定" :value="3"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" v-if="form.type==1">
|
||||
<el-input-number v-model="form.range.start" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
<span style="padding:0 15px;">-</span>
|
||||
<el-input-number v-model="form.range.end" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" v-if="form.type==2">
|
||||
<el-input-number v-model="form.loop.start" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
小时开始,每
|
||||
<el-input-number v-model="form.loop.end" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
小时执行一次
|
||||
</el-form-item>
|
||||
<el-form-item label="指定" v-if="form.type==3">
|
||||
<el-select v-model="form.appoint" multiple style="width: 100%;">
|
||||
<el-option v-for="(item, index) in hour" :key="index" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: { type: String, default: "*" },
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
form: {
|
||||
type: '0',
|
||||
range: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
loop: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
appoint: []
|
||||
},
|
||||
hour: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue:{
|
||||
handler(val){
|
||||
if(val == '*'){
|
||||
this.form.type = 0
|
||||
}else if(val.indexOf('-') > -1){
|
||||
this.form.type = 1
|
||||
}else if(val.indexOf('/') > -1){
|
||||
this.form.type = 2
|
||||
}else if(val.indexOf(',') > -1){
|
||||
this.form.type = 3
|
||||
}else{
|
||||
this.form.type = 3
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
"form": {
|
||||
handler(val){
|
||||
let data = ''
|
||||
if(val.type == 0){
|
||||
data = '*'
|
||||
}else if(val.type==1){
|
||||
data = this.form.range.start + '-' + this.form.range.end
|
||||
}else if(val.type==2){
|
||||
data = this.form.loop.start + '/' + this.form.loop.end
|
||||
}else if(val.type==3){
|
||||
data = this.form.appoint.length>0 ? this.form.appoint.join(',') : '1'
|
||||
}else{
|
||||
data = '*'
|
||||
}
|
||||
this.$emit('update:modelValue', data)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="任意值" :value="0"></el-radio-button>
|
||||
<el-radio-button label="范围" :value="1"></el-radio-button>
|
||||
<el-radio-button label="间隔" :value="2"></el-radio-button>
|
||||
<el-radio-button label="指定" :value="3"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" v-if="form.type==1">
|
||||
<el-input-number v-model="form.range.start" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
<span style="padding:0 15px;">-</span>
|
||||
<el-input-number v-model="form.range.end" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" v-if="form.type==2">
|
||||
<el-input-number v-model="form.loop.start" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
分钟开始,每
|
||||
<el-input-number v-model="form.loop.end" :min="0" :max="23" controls-position="right"></el-input-number>
|
||||
分钟执行一次
|
||||
</el-form-item>
|
||||
<el-form-item label="指定" v-if="form.type==3">
|
||||
<el-select v-model="form.appoint" multiple style="width: 100%;">
|
||||
<el-option v-for="(item, index) in minute" :key="index" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: { type: String, default: "*" },
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
form: {
|
||||
type: '0',
|
||||
range: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
loop: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
appoint: []
|
||||
},
|
||||
minute: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,51, 52, 53, 54, 55, 56, 57, 58, 59]
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue:{
|
||||
handler(val){
|
||||
if(val == '*'){
|
||||
this.form.type = 0
|
||||
}else if(val.indexOf('-') > -1){
|
||||
this.form.type = 1
|
||||
}else if(val.indexOf('/') > -1){
|
||||
this.form.type = 2
|
||||
}else if(val.indexOf(',') > -1){
|
||||
this.form.type = 3
|
||||
}else{
|
||||
this.form.type = 3
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
"form": {
|
||||
handler(val){
|
||||
let data = ''
|
||||
if(val.type == 0){
|
||||
data = '*'
|
||||
}else if(val.type==1){
|
||||
data = this.form.range.start + '-' + this.form.range.end
|
||||
}else if(val.type==2){
|
||||
data = this.form.loop.start + '/' + this.form.loop.end
|
||||
}else if(val.type==3){
|
||||
data = this.form.appoint.length>0 ? this.form.appoint.join(',') : '1'
|
||||
}else{
|
||||
data = '*'
|
||||
}
|
||||
this.$emit('update:modelValue', data)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="任意值" :value="0"></el-radio-button>
|
||||
<el-radio-button label="范围" :value="1"></el-radio-button>
|
||||
<el-radio-button label="间隔" :value="2"></el-radio-button>
|
||||
<el-radio-button label="指定" :value="3"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" v-if="form.type==1">
|
||||
<el-input-number v-model="form.range.start" :min="1" :max="12" controls-position="right"></el-input-number>
|
||||
<span style="padding:0 15px;">-</span>
|
||||
<el-input-number v-model="form.range.end" :min="1" :max="12" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" v-if="form.type==2">
|
||||
<el-input-number v-model="form.loop.start" :min="1" :max="12" controls-position="right"></el-input-number>
|
||||
月开始,每
|
||||
<el-input-number v-model="form.loop.end" :min="1" :max="12" controls-position="right"></el-input-number>
|
||||
月执行一次
|
||||
</el-form-item>
|
||||
<el-form-item label="指定" v-if="form.type==3">
|
||||
<el-select v-model="form.appoint" multiple style="width: 100%;">
|
||||
<el-option v-for="(item, index) in data.month" :key="index" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: { type: String, default: "*" },
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
form: {
|
||||
type: '0',
|
||||
range: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
loop: {
|
||||
start: 1,
|
||||
end: 1
|
||||
},
|
||||
appoint: []
|
||||
},
|
||||
data: {
|
||||
month: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue:{
|
||||
handler(val){
|
||||
if(val == '*'){
|
||||
this.form.type = 0
|
||||
}else if(val.indexOf('-') > -1){
|
||||
this.form.type = 1
|
||||
}else if(val.indexOf('/') > -1){
|
||||
this.form.type = 2
|
||||
}else if(val.indexOf(',') > -1){
|
||||
this.form.type = 3
|
||||
}else{
|
||||
this.form.type = 3
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
"form": {
|
||||
handler(val){
|
||||
let data = ''
|
||||
if(val.type == 0){
|
||||
data = '*'
|
||||
}else if(val.type==1){
|
||||
data = this.form.range.start + '-' + this.form.range.end
|
||||
}else if(val.type==2){
|
||||
data = this.form.loop.start + '/' + this.form.loop.end
|
||||
}else if(val.type==3){
|
||||
data = this.form.appoint.length>0 ? this.form.appoint.join(',') : '1'
|
||||
}else{
|
||||
data = '*'
|
||||
}
|
||||
this.$emit('update:modelValue', data)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="任意值" :value="0"></el-radio-button>
|
||||
<el-radio-button label="范围" :value="1"></el-radio-button>
|
||||
<el-radio-button label="间隔" :value="2"></el-radio-button>
|
||||
<el-radio-button label="指定" :value="3"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" v-if="form.type==1">
|
||||
<el-input-number v-model="form.range.start" :min="0" :max="59" controls-position="right"></el-input-number>
|
||||
<span style="padding:0 15px;">-</span>
|
||||
<el-input-number v-model="form.range.end" :min="0" :max="59" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" v-if="form.type==2">
|
||||
<el-input-number v-model="form.loop.start" :min="0" :max="59" controls-position="right"></el-input-number>
|
||||
秒开始,每
|
||||
<el-input-number v-model="form.loop.end" :min="0" :max="59" controls-position="right"></el-input-number>
|
||||
秒执行一次
|
||||
</el-form-item>
|
||||
<el-form-item label="指定" v-if="form.type==3">
|
||||
<el-select v-model="form.appoint" multiple style="width: 100%;">
|
||||
<el-option v-for="(item, index) in second" :key="index" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: { type: String, default: "*" },
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
form: {
|
||||
type: '0',
|
||||
range: {
|
||||
start: 0,
|
||||
end: 59
|
||||
},
|
||||
loop: {
|
||||
start: 0,
|
||||
end: 59
|
||||
},
|
||||
appoint: []
|
||||
},
|
||||
second: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59]
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue:{
|
||||
handler(val){
|
||||
if(val == '*'){
|
||||
this.form.type = 0
|
||||
}else if(val.indexOf('-') > -1){
|
||||
this.form.type = 1
|
||||
}else if(val.indexOf('/') > -1){
|
||||
this.form.type = 2
|
||||
}else if(val.indexOf(',') > -1){
|
||||
this.form.type = 3
|
||||
}else{
|
||||
this.form.type = 3
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
"form": {
|
||||
handler(val){
|
||||
let data = ''
|
||||
if(val.type == 0){
|
||||
data = '*'
|
||||
}else if(val.type==1){
|
||||
data = this.form.range.start + '-' + this.form.range.end
|
||||
}else if(val.type==2){
|
||||
data = this.form.loop.start + '/' + this.form.loop.end
|
||||
}else if(val.type==3){
|
||||
data = this.form.appoint.length>0 ? this.form.appoint.join(',') : '1'
|
||||
}else{
|
||||
data = '*'
|
||||
}
|
||||
this.$emit('update:modelValue', data)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<el-input v-model="defaultValue" v-bind="$attrs">
|
||||
<template #append>
|
||||
<el-dropdown size="medium" @command="handleShortcuts">
|
||||
<el-button icon="el-icon-arrow-down"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="* * * * *">每分钟</el-dropdown-item>
|
||||
<el-dropdown-item command="0 * * * *">每小时</el-dropdown-item>
|
||||
<el-dropdown-item command="0 0 * * *">每天零点</el-dropdown-item>
|
||||
<el-dropdown-item command="0 0 1 * *">每月一号零点</el-dropdown-item>
|
||||
<el-dropdown-item command="0 0 L * *">每月最后一天零点</el-dropdown-item>
|
||||
<el-dropdown-item command="0 0 ? * 1">每周星期日零点</el-dropdown-item>
|
||||
<el-dropdown-item v-for="(item, index) in shortcuts" :key="item.value" :divided="index==0" :command="item.value">{{item.text}}</el-dropdown-item>
|
||||
<el-dropdown-item icon="el-icon-plus" divided command="custom">自定义</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-dialog title="cron规则生成器" v-model="dialogVisible" :width="580" destroy-on-close append-to-body>
|
||||
<div class="sc-cron">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane v-for="(item, index) in tabsList" :key="index" :name="item.name">
|
||||
<template #label>
|
||||
<div class="sc-cron-num">
|
||||
<h2>{{item.label}}</h2>
|
||||
<h4>{{item.value}}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<component :is="item.name" v-model="item.value" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible=false" >取 消</el-button>
|
||||
<el-button type="primary" @click="submit()">确 认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import second from './components/second.vue'
|
||||
import minute from './components/minute.vue'
|
||||
import hour from './components/hour.vue'
|
||||
import day from './components/day.vue'
|
||||
import month from './components/month.vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: String, default: "* * * * * ?" },
|
||||
shortcuts: { type: Array, default: () => [] }
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
defaultValue: '',
|
||||
dialogVisible: false,
|
||||
activeName: 'second',
|
||||
tabsList: [
|
||||
{name: 'second', label: '秒', value: '*'},
|
||||
{name: 'minute', label: '分钟', value: '*'},
|
||||
{name: 'hour', label: '小时', value: '*'},
|
||||
{name: 'day', label: '日', value: '*'},
|
||||
{name: 'month', label: '月', value: '*'},
|
||||
// {name: 'week', label: '周', value: '*'}
|
||||
// {name: 'year', label: '年', value: '*'}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue: {
|
||||
handler(val){
|
||||
this.defaultValue = val
|
||||
let arr = val.split(' ')
|
||||
this.tabsList.forEach((item, index) => {
|
||||
item.value = arr[index]
|
||||
})
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
components:{
|
||||
second,
|
||||
minute,
|
||||
hour,
|
||||
day,
|
||||
month
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
methods:{
|
||||
handleShortcuts(command){
|
||||
if(command == 'custom'){
|
||||
this.open()
|
||||
}else{
|
||||
this.defaultValue = command
|
||||
this.$emit('update:modelValue', this.defaultValue)
|
||||
}
|
||||
},
|
||||
open(){
|
||||
this.dialogVisible = true
|
||||
},
|
||||
submit(){
|
||||
let data = []
|
||||
for(let item of this.tabsList) {
|
||||
data.push(item.value)
|
||||
}
|
||||
this.defaultValue = data.join(' ')
|
||||
this.dialogVisible = false
|
||||
this.$emit('update:modelValue', this.defaultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-cron:deep(.el-tabs__item) {height: auto;line-height: 1;padding:0 7px;vertical-align: bottom;}
|
||||
.sc-cron-num {text-align: center;margin-bottom: 15px;width: 100%;}
|
||||
.sc-cron-num h2 {font-size: 12px;margin-bottom: 15px;font-weight: normal;}
|
||||
.sc-cron-num h4 {display: block;height: 32px;line-height: 30px;width: 100%;font-size: 12px;padding:0 15px;background: var(--el-color-primary-light-9);border-radius:4px;}
|
||||
.sc-cron:deep(.el-tabs__item.is-active) .sc-cron-num h4 {background: var(--el-color-primary);color: #fff;}
|
||||
|
||||
[data-theme='dark'] .sc-cron-num h4 {background: var(--el-color-white);}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
* @Descripttion: 图像裁剪组件
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月24日17:05:43
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
* @other: 代码完全开源,欢迎参考,也欢迎PR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-cropper">
|
||||
<div class="sc-cropper__img">
|
||||
<img :src="src" ref="img">
|
||||
</div>
|
||||
<div class="sc-cropper__preview">
|
||||
<h4>图像预览</h4>
|
||||
<div class="sc-cropper__preview__img" ref="preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
src: { type: String, default: "" },
|
||||
compress: {type: Number, default: 1},
|
||||
aspectRatio: {type: Number, default: NaN},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
crop: null
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
aspectRatio(val){
|
||||
this.crop.setAspectRatio(val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init(){
|
||||
this.crop = new Cropper(this.$refs.img, {
|
||||
viewMode: 2,
|
||||
dragMode: 'move',
|
||||
responsive: false,
|
||||
aspectRatio: this.aspectRatio,
|
||||
preview: this.$refs.preview
|
||||
})
|
||||
},
|
||||
setAspectRatio(aspectRatio){
|
||||
this.crop.setAspectRatio(aspectRatio)
|
||||
},
|
||||
getCropData(cb, type='image/jpeg'){
|
||||
cb(this.crop.getCroppedCanvas().toDataURL(type, this.compress))
|
||||
},
|
||||
getCropBlob(cb, type='image/jpeg'){
|
||||
this.crop.getCroppedCanvas().toBlob((blob) => {
|
||||
cb(blob)
|
||||
}, type, this.compress)
|
||||
},
|
||||
getCropFile(cb, fileName='fileName.jpg', type='image/jpeg'){
|
||||
this.crop.getCroppedCanvas().toBlob((blob) => {
|
||||
let file = new File([blob], fileName, {type: type})
|
||||
cb(file)
|
||||
}, type, this.compress)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-cropper {height:300px;}
|
||||
.sc-cropper__img {height:100%;width:400px;float: left;background: #EBEEF5;}
|
||||
.sc-cropper__img img {display: none;}
|
||||
.sc-cropper__preview {width: 120px;margin-left: 20px;float: left;}
|
||||
.sc-cropper__preview h4 {font-weight: normal;font-size: 12px;color: #999;margin-bottom: 20px;}
|
||||
.sc-cropper__preview__img {overflow: hidden;width: 120px;height: 120px;border: 1px solid #ebeef5;}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
* @Descripttion: 弹窗扩展组件
|
||||
* @version: 2.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年8月27日08:51:52
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月14日15:13:41
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-dialog" ref="scDialog">
|
||||
<el-dialog ref="dialog" v-model="dialogVisible" :fullscreen="isFullscreen" v-bind="$attrs" :show-close="false">
|
||||
<template #header>
|
||||
<slot name="header">
|
||||
<span class="el-dialog__title">{{ title }}</span>
|
||||
</slot>
|
||||
<div class="sc-dialog__headerbtn">
|
||||
<button v-if="showFullscreen" aria-label="fullscreen" type="button" @click="setFullscreen">
|
||||
<el-icon v-if="isFullscreen" class="el-dialog__close"><el-icon-bottom-left /></el-icon>
|
||||
<el-icon v-else class="el-dialog__close"><el-icon-full-screen /></el-icon>
|
||||
</button>
|
||||
<button v-if="showClose" aria-label="close" type="button" @click="closeDialog">
|
||||
<el-icon class="el-dialog__close"><el-icon-close /></el-icon>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-loading="loading">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<template #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: "" },
|
||||
showClose: { type: Boolean, default: true },
|
||||
showFullscreen: { type: Boolean, default: true },
|
||||
loading: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
isFullscreen: false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(){
|
||||
this.dialogVisible = this.modelValue
|
||||
if(this.dialogVisible){
|
||||
this.isFullscreen = false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.dialogVisible = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
//关闭
|
||||
closeDialog(){
|
||||
this.dialogVisible = false
|
||||
},
|
||||
//最大化
|
||||
setFullscreen(){
|
||||
this.isFullscreen = !this.isFullscreen
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-dialog__headerbtn {position: absolute;top: var(--el-dialog-padding-primary);right: var(--el-dialog-padding-primary);}
|
||||
.sc-dialog__headerbtn button {padding: 0;background: transparent;border: none;outline: none;cursor: pointer;font-size: var(--el-message-close-size,16px);margin-left: 15px;color: var(--el-color-info);}
|
||||
.sc-dialog__headerbtn button:hover .el-dialog__close {color: var(--el-color-primary);}
|
||||
.sc-dialog:deep(.el-dialog).is-fullscreen {display: flex;flex-direction: column;top:0px !important;left:0px !important;}
|
||||
.sc-dialog:deep(.el-dialog).is-fullscreen .el-dialog__header {}
|
||||
.sc-dialog:deep(.el-dialog).is-fullscreen .el-dialog__body {flex:1;overflow: auto;}
|
||||
.sc-dialog:deep(.el-dialog).is-fullscreen .el-dialog__footer {padding-bottom: 10px;border-top: 1px solid var(--el-border-color-base);}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
const T = {
|
||||
"color": [
|
||||
"#409EFF",
|
||||
"#36CE9E",
|
||||
"#f56e6a",
|
||||
"#626c91",
|
||||
"#edb00d",
|
||||
"#909399"
|
||||
],
|
||||
'grid': {
|
||||
'left': '3%',
|
||||
'right': '3%',
|
||||
'bottom': '10',
|
||||
'top': '40',
|
||||
'containLabel': true
|
||||
},
|
||||
"legend": {
|
||||
"textStyle": {
|
||||
"color": "#999"
|
||||
},
|
||||
"inactiveColor": "rgba(128,128,128,0.4)"
|
||||
},
|
||||
"categoryAxis": {
|
||||
"axisLine": {
|
||||
"show": true,
|
||||
"lineStyle": {
|
||||
"color": "rgba(128,128,128,0.2)",
|
||||
"width": 1
|
||||
}
|
||||
},
|
||||
"axisTick": {
|
||||
"show": false,
|
||||
"lineStyle": {
|
||||
"color": "#333"
|
||||
}
|
||||
},
|
||||
"axisLabel": {
|
||||
"color": "#999"
|
||||
},
|
||||
"splitLine": {
|
||||
"show": false,
|
||||
"lineStyle": {
|
||||
"color": [
|
||||
"#eee"
|
||||
]
|
||||
}
|
||||
},
|
||||
"splitArea": {
|
||||
"show": false,
|
||||
"areaStyle": {
|
||||
"color": [
|
||||
"rgba(255,255,255,0.01)",
|
||||
"rgba(0,0,0,0.01)"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"valueAxis": {
|
||||
"axisLine": {
|
||||
"show": false,
|
||||
"lineStyle": {
|
||||
"color": "#999"
|
||||
}
|
||||
},
|
||||
"splitLine": {
|
||||
"show": true,
|
||||
"lineStyle": {
|
||||
"color": "rgba(128,128,128,0.2)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default T
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div ref="scEcharts" :style="{height:height, width:width}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import T from './echarts-theme-T.js';
|
||||
echarts.registerTheme('T', T);
|
||||
const unwarp = (obj) => obj && (obj.__v_raw || obj.valueOf() || obj);
|
||||
|
||||
export default {
|
||||
...echarts,
|
||||
name: "scEcharts",
|
||||
props: {
|
||||
height: { type: String, default: "100%" },
|
||||
width: { type: String, default: "100%" },
|
||||
nodata: {type: Boolean, default: false },
|
||||
option: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActivat: false,
|
||||
myChart: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
option: {
|
||||
deep:true,
|
||||
handler (v) {
|
||||
unwarp(this.myChart).setOption(v);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
myOptions: function() {
|
||||
return this.option || {};
|
||||
}
|
||||
},
|
||||
activated(){
|
||||
if(!this.isActivat){
|
||||
this.$nextTick(() => {
|
||||
this.myChart.resize()
|
||||
})
|
||||
}
|
||||
},
|
||||
deactivated(){
|
||||
this.isActivat = false;
|
||||
},
|
||||
mounted(){
|
||||
this.isActivat = true;
|
||||
this.$nextTick(() => {
|
||||
this.draw();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
draw(){
|
||||
var myChart = echarts.init(this.$refs.scEcharts, 'T');
|
||||
myChart.setOption(this.myOptions);
|
||||
this.myChart = myChart;
|
||||
window.addEventListener('resize', () => myChart.resize());
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
export default class UploadAdapter {
|
||||
constructor( loader, options ) {
|
||||
this.loader = loader;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
upload() {
|
||||
return this.loader.file
|
||||
.then( file => new Promise( ( resolve, reject ) => {
|
||||
this._initRequest();
|
||||
this._initListeners( resolve, reject, file );
|
||||
this._sendRequest( file );
|
||||
} ) );
|
||||
}
|
||||
|
||||
abort() {
|
||||
if ( this.xhr ) {
|
||||
this.xhr.abort();
|
||||
}
|
||||
}
|
||||
|
||||
_initRequest() {
|
||||
const xhr = this.xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open( 'POST', this.options.upload.uploadUrl, true );
|
||||
xhr.responseType = 'json';
|
||||
}
|
||||
|
||||
_initListeners( resolve, reject, file ) {
|
||||
const xhr = this.xhr;
|
||||
const loader = this.loader;
|
||||
const genericErrorText = `Couldn't upload file: ${ file.name }.`;
|
||||
|
||||
xhr.addEventListener( 'error', () => reject( genericErrorText ) );
|
||||
xhr.addEventListener( 'abort', () => reject() );
|
||||
xhr.addEventListener( 'load', () => {
|
||||
const response = xhr.response;
|
||||
if ( !response || response.code == 0 ) {
|
||||
return reject( response && response.code == 0 ? response.message : genericErrorText );
|
||||
}
|
||||
resolve( {
|
||||
default: response.data.url ? response.data.url : response.data.src
|
||||
} );
|
||||
} );
|
||||
|
||||
if ( xhr.upload ) {
|
||||
xhr.upload.addEventListener( 'progress', evt => {
|
||||
if ( evt.lengthComputable ) {
|
||||
loader.uploadTotal = evt.total;
|
||||
loader.uploaded = evt.loaded;
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
_sendRequest( file ) {
|
||||
// Set headers if specified.
|
||||
const headers = this.options.upload.headers || {};
|
||||
const extendData = this.options.upload.extendData || {};
|
||||
// Use the withCredentials flag if specified.
|
||||
const withCredentials = this.options.upload.withCredentials || false;
|
||||
const uploadName = this.options.upload.uploadName || 'file';
|
||||
for (const headerName of Object.keys(headers)) {
|
||||
this.xhr.setRequestHeader(headerName, headers[headerName]);
|
||||
}
|
||||
this.xhr.withCredentials = withCredentials;
|
||||
const data = new FormData();
|
||||
for (const key of Object.keys(extendData)) {
|
||||
data.append(key, extendData[key]);
|
||||
}
|
||||
data.append( uploadName, file );
|
||||
this.xhr.send( data );
|
||||
}
|
||||
}
|
||||
|
||||
export function UploadAdapterPlugin( editor ) {
|
||||
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
|
||||
return new UploadAdapter( loader, editor.config._config );
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div :style="{'--editor-height': editorHeight}">
|
||||
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :disabled="disabled" @blur="onBlur" @focus="onFocus"></ckeditor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ClassicEditor, Alignment, AutoImage, Autoformat, BlockQuote, Bold, CodeBlock, DataFilter, DataSchema, Essentials, FindAndReplace,
|
||||
FontBackgroundColor, FontColor, FontFamily, FontSize, GeneralHtmlSupport, Heading, Highlight, HorizontalLine, Image, ImageCaption, ImageInsert, ImageResize,
|
||||
ImageStyle, ImageToolbar, ImageUpload, Indent, IndentBlock, Italic, Link, LinkImage, List, MediaEmbed, MediaEmbedToolbar, Mention, Paragraph, PasteFromOffice,
|
||||
RemoveFormat, SelectAll, ShowBlocks, SourceEditing, SpecialCharacters, SpecialCharactersArrows, SpecialCharactersCurrency, SpecialCharactersEssentials, SpecialCharactersLatin,
|
||||
SpecialCharactersMathematical, SpecialCharactersText, Style, Subscript, Superscript, Table, TableCaption, TableCellProperties, TableColumnResize,
|
||||
TableProperties, TableToolbar, TextTransformation, TodoList, Underline, Undo, WordCount
|
||||
} from 'ckeditor5';
|
||||
import { Ckeditor } from '@ckeditor/ckeditor5-vue';
|
||||
import { UploadAdapterPlugin } from './UploadAdapter.js'
|
||||
|
||||
import coreTranslations from 'ckeditor5/translations/zh-cn.js'
|
||||
import 'ckeditor5/ckeditor5.css';
|
||||
|
||||
export default {
|
||||
name: 'scCkeditor',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入内容……'
|
||||
},
|
||||
toolbar: {
|
||||
type: String,
|
||||
default: 'basic'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '400px'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Ckeditor
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
editorData: '',
|
||||
editor: ClassicEditor,
|
||||
editorHeight: 0,
|
||||
toolbars: {
|
||||
full: [
|
||||
'sourceEditing', 'undo', 'redo', 'heading', 'style',
|
||||
'|', 'superscript', 'subscript', 'removeFormat', 'bold', 'italic', 'underline', 'link', 'fontBackgroundColor', 'fontFamily', 'fontSize', 'fontColor',
|
||||
'|', 'outdent', 'indent', 'alignment', 'bulletedList', 'numberedList', 'todoList',
|
||||
'|', 'blockQuote', 'insertTable', 'imageInsert', 'mediaEmbed', 'highlight', 'horizontalLine', 'selectAll', 'showBlocks', 'specialCharacters', 'codeBlock', 'findAndReplace'
|
||||
],
|
||||
basic: [
|
||||
'sourceEditing', 'undo', 'redo', 'heading',
|
||||
'|', 'removeFormat', 'bold', 'italic', 'underline', 'link', 'fontBackgroundColor', 'fontFamily', 'fontSize', 'fontColor',
|
||||
'|', 'outdent', 'indent', 'alignment', 'bulletedList', 'numberedList', 'todoList',
|
||||
'|', 'insertTable', 'imageInsert', 'mediaEmbed'
|
||||
],
|
||||
simple: [
|
||||
'undo', 'redo', 'heading',
|
||||
'|', 'removeFormat', 'bold', 'italic', 'underline', 'link', 'fontBackgroundColor', 'fontFamily', 'fontSize', 'fontColor',
|
||||
'|', 'insertTable', 'imageInsert', 'mediaEmbed'
|
||||
]
|
||||
},
|
||||
editorConfig: {
|
||||
language: {ui: 'zh-cn', content: 'zh-cn'},
|
||||
translations: [ coreTranslations ],
|
||||
plugins: [ Alignment, AutoImage, Autoformat, BlockQuote, Bold, CodeBlock, DataFilter, DataSchema, Essentials, FindAndReplace,
|
||||
FontBackgroundColor, FontColor, FontFamily, FontSize, GeneralHtmlSupport, Heading, Highlight, HorizontalLine, Image, ImageCaption, ImageInsert, ImageResize,
|
||||
ImageStyle, ImageToolbar, ImageUpload, Indent, IndentBlock, Italic, Link, LinkImage, List, MediaEmbed, MediaEmbedToolbar, Mention, Paragraph, PasteFromOffice,
|
||||
RemoveFormat, SelectAll, ShowBlocks, SourceEditing, SpecialCharacters, SpecialCharactersArrows, SpecialCharactersCurrency, SpecialCharactersEssentials, SpecialCharactersLatin,
|
||||
SpecialCharactersMathematical, SpecialCharactersText, Style, Subscript, Superscript, Table, TableCaption, TableCellProperties, TableColumnResize,
|
||||
TableProperties, TableToolbar, TextTransformation, TodoList, Underline, Undo, WordCount, UploadAdapterPlugin
|
||||
],
|
||||
toolbar: {shouldNotGroupWhenFull: true},
|
||||
placeholder: '',
|
||||
image: {
|
||||
styles: ['alignLeft', 'alignCenter', 'alignRight'],
|
||||
toolbar: ['imageTextAlternative', 'toggleImageCaption', '|', 'imageStyle:alignLeft', 'imageStyle:alignCenter', 'imageStyle:alignRight', '|', 'linkImage']
|
||||
},
|
||||
mediaEmbed: {
|
||||
providers: [
|
||||
{name: 'mp4', url: /\.(mp4|avi|mov|flv|wmv|mkv)$/i, html: match => {
|
||||
const url = match['input']
|
||||
return ('<video controls width="100%" height="100%" src="${url}"></video>')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
fontSize: {
|
||||
options: [
|
||||
10, 12, 14, 16, 18, 20, 22, 24, 26, 30, 32, 36
|
||||
]
|
||||
},
|
||||
style: {
|
||||
definitions: [
|
||||
{
|
||||
name: 'Article category',
|
||||
element: 'h3',
|
||||
classes: [ 'category' ]
|
||||
},
|
||||
{
|
||||
name: 'Info box',
|
||||
element: 'p',
|
||||
classes: [ 'info-box' ]
|
||||
},
|
||||
]
|
||||
},
|
||||
upload: {
|
||||
uploadUrl: this.$API.common.upload.url,
|
||||
withCredentials: false,
|
||||
extendData: {type: 'images'},
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + this.$TOOL.data.get('TOKEN')
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(newVal) {
|
||||
this.editorData = newVal ?? ''
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
placeholder: {
|
||||
handler(newVal) {
|
||||
this.editorConfig.placeholder = newVal
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
toolbar: {
|
||||
handler(newVal) {
|
||||
this.editorConfig.toolbar.items = this.toolbars[newVal]
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
height: {
|
||||
handler(newVal) {
|
||||
this.editorHeight = newVal
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
onBlur(event, editor) {
|
||||
this.editorData = this.editorData.replace(/<img[^>]*>/gi, (match) => {
|
||||
return match.replace(/width="[^"]*"/gi, '').replace(/height="[^"]*"/gi, '')
|
||||
})
|
||||
this.$emit('update:modelValue', this.editorData)
|
||||
},
|
||||
onFocus(event, editor) {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root{
|
||||
--ck-z-panel: 9999
|
||||
}
|
||||
.ck-content{
|
||||
height: var(--editor-height);
|
||||
}
|
||||
.ck-source-editing-area, .ck-source-editing-area textarea{height: var(--editor-height);}
|
||||
.ck-source-editing-area textarea{overflow-y: scroll !important;}
|
||||
</style>
|
||||
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div ref="aiEditor" :style="{height: editorHeight, width: '100%'}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {AiEditor} from "aieditor";
|
||||
import "aieditor/dist/style.css"
|
||||
|
||||
export default {
|
||||
name: 'scCkeditor',
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入内容……'
|
||||
},
|
||||
toolbar: {
|
||||
type: String,
|
||||
default: 'basic'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '400px'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
defaultValue: '',
|
||||
editorHeight: '300px',
|
||||
toolbars: {
|
||||
full: [
|
||||
"undo", "redo", "brush", "eraser",
|
||||
"|", "heading", "font-family", "font-size",
|
||||
"|", "bold", "italic", "underline", "strike", "link", "code", "subscript", "superscript", "hr", "todo", "emoji",
|
||||
"|", "highlight", "font-color",
|
||||
"|", "align", "line-height",
|
||||
"|", "bullet-list", "ordered-list", "indent-decrease", "indent-increase", "break",
|
||||
"|", "image", "video", "attachment", "quote", "code-block", "table",
|
||||
"|", "source-code", "printer", "fullscreen", "ai"
|
||||
],
|
||||
basic: [
|
||||
"undo", "redo", "brush", "eraser",
|
||||
"|", "heading", "font-family", "font-size",
|
||||
"|", "bold", "italic", "underline", "strike", "link", "code", "subscript", "superscript", "hr", "todo", "emoji",
|
||||
"|", "highlight", "font-color",
|
||||
"|", "align", "line-height",
|
||||
"|", "bullet-list", "ordered-list", "indent-decrease", "indent-increase", "break",
|
||||
"|", "image", "video", "attachment", "quote", "code-block", "table",
|
||||
],
|
||||
simple: ["undo", "redo",
|
||||
"|", "font-family", "font-size",
|
||||
"|", "bold", "italic", "underline", "strike", "emoji",
|
||||
"|", "highlight", "font-color",
|
||||
"|", "image", "link"]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(newValue) {
|
||||
if (newValue !== this.defaultValue) {
|
||||
this.defaultValue = newValue
|
||||
}
|
||||
},
|
||||
height: {
|
||||
handler(newValue) {
|
||||
if (indexOf(newValue, 'px') === -1){
|
||||
this.editorHeight = newValue > 200 ? newValue + 'px' : '200px'
|
||||
}else{
|
||||
newValue = newValue.replace('px', '')
|
||||
this.editorHeight = newValue > 200 ? newValue + 'px' : '200px'
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = new AiEditor({
|
||||
element: this.$refs.aiEditor,
|
||||
placeholder: this.placeholder,
|
||||
content: this.defaultValue,
|
||||
toolbarKeys: this.toolbars[this.toolbar],
|
||||
onChange: (editor) => {
|
||||
this.$emit('update:modelValue', editor.getHtml())
|
||||
},
|
||||
editable: !this.disabled,
|
||||
textSelectionBubbleMenu: {
|
||||
enable: true,
|
||||
items: ["Bold", "Italic", "Underline", "Strike", "code", "comment"],
|
||||
},
|
||||
image: {
|
||||
uploadUrl: this.$API.common.upload.url,
|
||||
uploadHeaders: {
|
||||
Authorization: 'Bearer ' + this.$TOOL.data.get('TOKEN')
|
||||
},
|
||||
uploadFormName: 'file',
|
||||
uploader: (file, uploadUrl, headers, formName) => {
|
||||
const formData = new FormData();
|
||||
formData.append(formName, file);
|
||||
formData.append('type', 'images');
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(uploadUrl, {
|
||||
method: "post",
|
||||
headers: {'Accept': 'application/json', ...headers},
|
||||
body: formData,
|
||||
}).then((resp) => resp.json())
|
||||
.then(json => {
|
||||
resolve({json});
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
})
|
||||
});
|
||||
},
|
||||
uploaderEvent: {
|
||||
onSuccess: (file, response) => {
|
||||
let res = response.json
|
||||
if (res.code === 1) {
|
||||
return {errorCode: 0, data: {
|
||||
src: res.data.url
|
||||
}}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
video: {
|
||||
uploadUrl: this.$API.common.upload.url,
|
||||
uploadHeaders: {
|
||||
Authorization: 'Bearer ' + this.$TOOL.data.get('TOKEN')
|
||||
},
|
||||
uploadFormName: 'file',
|
||||
uploader: (file, uploadUrl, headers, formName) => {
|
||||
const formData = new FormData();
|
||||
formData.append(formName, file);
|
||||
formData.append('type', 'video');
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(uploadUrl, {
|
||||
method: "post",
|
||||
headers: {'Accept': 'application/json', ...headers},
|
||||
body: formData,
|
||||
}).then((resp) => resp.json())
|
||||
.then(json => {
|
||||
resolve(json);
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
})
|
||||
});
|
||||
},
|
||||
uploaderEvent: {
|
||||
onSuccess: (file, response) => {
|
||||
let res = response.json
|
||||
if (res.code === 1) {
|
||||
return {errorCode: 0, data: {
|
||||
src: res.data.url
|
||||
}}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
attachment: {
|
||||
uploadUrl: this.$API.common.upload.url,
|
||||
uploadHeaders: {
|
||||
Authorization: 'Bearer ' + this.$TOOL.data.get('TOKEN')
|
||||
},
|
||||
uploadFormName: 'file',
|
||||
uploader: (file, uploadUrl, headers, formName) => {
|
||||
const formData = new FormData();
|
||||
formData.append(formName, file);
|
||||
formData.append('type', 'files');
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(uploadUrl, {
|
||||
method: "post",
|
||||
headers: {'Accept': 'application/json', ...headers},
|
||||
body: formData,
|
||||
}).then((resp) => resp.json())
|
||||
.then(json => {
|
||||
resolve(json);
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
})
|
||||
});
|
||||
},
|
||||
uploaderEvent: {
|
||||
onSuccess: (file, response) => {
|
||||
let res = response.json
|
||||
if (res.code === 1) {
|
||||
return {errorCode: 0, data: {
|
||||
src: res.data.url
|
||||
}}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
<template>
|
||||
<el-table ref="table" :data="columnData" row-key="prop" style="width: 100%" border>
|
||||
<el-table-column prop="" label="排序" width="60">
|
||||
<el-tag class="move" style="cursor: move;"><el-icon style="cursor: move;"><el-icon-d-caret/></el-icon></el-tag>
|
||||
</el-table-column>
|
||||
<el-table-column prop="label" label="列名">
|
||||
<template #default="scope">
|
||||
<el-tag round :effect="scope.row.hide?'light':'dark'" :type="scope.row.hide?'info':''">{{ scope.row.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hide" label="显示" width="60">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.hide" :active-value="false" :inactive-value="true"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
emits: ['success'],
|
||||
props: {
|
||||
column: { type: Array, default: () => [] }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnData: this.column
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.rowDrop()
|
||||
},
|
||||
methods: {
|
||||
rowDrop(){
|
||||
const _this = this
|
||||
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
|
||||
Sortable.create(tbody, {
|
||||
handle: ".move",
|
||||
animation: 200,
|
||||
ghostClass: "ghost",
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
const tableData = _this.columnData
|
||||
const currRow = tableData.splice(oldIndex, 1)[0]
|
||||
tableData.splice(newIndex, 0, currRow)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,199 @@
|
||||
<!--
|
||||
* @Descripttion: 文件导出
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2022年5月24日16:20:12
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年6月13日17:32:05
|
||||
-->
|
||||
|
||||
<template>
|
||||
<slot :open="open">
|
||||
<el-button type="primary" plain @click="open">导出</el-button>
|
||||
</slot>
|
||||
<el-drawer v-model="dialog" title="导出" :size="400" direction="rtl" append-to-body destroy-on-close>
|
||||
<el-main style="padding: 0 20px 20px 20px;">
|
||||
<div v-loading="downLoading" element-loading-text="正在处理中...">
|
||||
<div v-if="downLoading && progress" style="position: absolute;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;z-index: 3000;">
|
||||
<el-progress :text-inside="true" :stroke-width="20" :percentage="downLoadProgress" style="width: 100%;margin-bottom: 120px;"/>
|
||||
</div>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="常规" lazy>
|
||||
<el-form label-width="100px" label-position="left" style="margin: 10px 0 20px 0;">
|
||||
<el-form-item label="文件名">
|
||||
<el-input v-model="formData.fileName" placeholder="请输入文件名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型">
|
||||
<el-select v-model="formData.fileType" placeholder="请选择文件类型">
|
||||
<el-option v-for="item in fileTypes" :key="item" :label="'*.'+item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<slot name="form" :formData="formData"></slot>
|
||||
</el-form>
|
||||
<el-button v-if="async" type="primary" icon="el-icon-plus" style="width: 100%;" @click="download" :loading="asyncLoading">发起导出任务</el-button>
|
||||
<el-button v-else type="primary" icon="el-icon-download" style="width: 100%;" @click="download">下 载</el-button>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="列设置" v-if="columnData.length>0" lazy>
|
||||
<columnSet :column="columnData"></columnSet>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="其他参数" v-if="data && showData" lazy>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item v-for=" (val, key) in data" :key="key" :label="key">{{val}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import columnSet from './column'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
columnSet
|
||||
},
|
||||
props: {
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
fileName: { type: String, default: "" },
|
||||
fileTypes: { type: Array, default: () => ['xlsx'] },
|
||||
data: { type: Object, default: () => {} },
|
||||
showData: { type: Boolean, default: false },
|
||||
async: { type: Boolean, default: false },
|
||||
column: { type: Array, default: () => [] },
|
||||
blob: { type: Boolean, default: false },
|
||||
progress: { type: Boolean, default: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
formData: {
|
||||
fileName: this.fileName,
|
||||
fileType: this.fileTypes[0]
|
||||
},
|
||||
columnData: [],
|
||||
downLoading: false,
|
||||
downLoadProgress: 0,
|
||||
asyncLoading: false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
'formData.fileType'(val) {
|
||||
if(this.formData.fileName.includes(".")){
|
||||
this.formData.fileName = this.formData.fileName.substring(0, this.formData.fileName.lastIndexOf('.')) + "." + val
|
||||
}else{
|
||||
this.formData.fileName = this.formData.fileName + "." + val
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.dialog = true
|
||||
this.formData = {
|
||||
fileName: (this.fileName?this.fileName:(new Date().getTime()+'')) + "." + this.fileTypes[0],
|
||||
fileType: this.fileTypes[0]
|
||||
}
|
||||
this.columnData = JSON.parse(JSON.stringify(this.column))
|
||||
},
|
||||
close() {
|
||||
this.dialog = false
|
||||
},
|
||||
download() {
|
||||
let columnArr = {
|
||||
column: this.columnData.filter(n => !n.hide).map(n => n.prop).join(",")
|
||||
}
|
||||
let assignData = {...this.data, ...this.formData, ...columnArr}
|
||||
if(this.async){
|
||||
this.asyncDownload(this.apiObj, this.formData.fileName, assignData)
|
||||
}else if(this.blob){
|
||||
this.downloadFile(this.apiObj, this.formData.fileName, assignData)
|
||||
}else{
|
||||
this.linkFile(this.apiObj.url, this.formData.fileName, assignData)
|
||||
}
|
||||
},
|
||||
linkFile(url, fileName, data={}){
|
||||
let a = document.createElement("a")
|
||||
a.style = "display: none"
|
||||
a.target = "_blank"
|
||||
//a.download = fileName
|
||||
a.href = url + this.toQueryString(data)
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
},
|
||||
downloadFile(apiObj, fileName, data={}){
|
||||
this.downLoading = true
|
||||
var _this = this
|
||||
apiObj.get(data, {
|
||||
responseType: 'blob',
|
||||
onDownloadProgress(e){
|
||||
if(e.lengthComputable){
|
||||
_this.downLoadProgress = parseInt(e.loaded / e.total * 100)
|
||||
}
|
||||
}
|
||||
}).then(res => {
|
||||
this.downLoading = false
|
||||
this.downLoadProgress = 0
|
||||
// let url = URL.createObjectURL(res.data)
|
||||
let url = res.data
|
||||
let a = document.createElement("a")
|
||||
a.style = "display: none"
|
||||
a.target = "_blank"
|
||||
a.download = fileName
|
||||
a.href = url
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
// URL.revokeObjectURL(url)
|
||||
}).catch(err => {
|
||||
this.downLoading = false
|
||||
this.downLoadProgress = 0
|
||||
this.$notify.error({
|
||||
title: '下载文件失败',
|
||||
message: err
|
||||
})
|
||||
})
|
||||
},
|
||||
asyncDownload(apiObj, fileName, data={}){
|
||||
this.asyncLoading = true
|
||||
apiObj.get(data).then(res => {
|
||||
this.asyncLoading = false
|
||||
if(res.code == 1){
|
||||
this.dialog = false
|
||||
this.$msgbox({
|
||||
title: "成功发起任务",
|
||||
message: `<div><img style="height:200px" :src="'static/images/tasks-example.png'"/></div><p>已成功发起导出任务,您可以操作其他事务</p><p>稍后可在 <b>任务中心</b> 查看执行结果</p>`,
|
||||
type: "success",
|
||||
confirmButtonText: "知道了",
|
||||
dangerouslyUseHTMLString: true,
|
||||
center: true
|
||||
}).catch(() => {})
|
||||
}else{
|
||||
this.$alert(res.message || "未知错误", "发起任务失败", {
|
||||
type: "error",
|
||||
center: true
|
||||
}).catch(() => {})
|
||||
}
|
||||
}).catch(() => {
|
||||
this.asyncLoading = false
|
||||
})
|
||||
},
|
||||
toQueryString(obj){
|
||||
let arr = []
|
||||
for (var k in obj) {
|
||||
arr.push(`${k}=${obj[k]}`)
|
||||
}
|
||||
return (arr.length>0?"?":"") + arr.join('&')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<!--
|
||||
* @Descripttion: 文件导入
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2022年5月24日11:30:03
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<slot :open="open">
|
||||
<el-button type="primary" plain @click="open">导入</el-button>
|
||||
</slot>
|
||||
<el-dialog v-model="dialog" title="导入" :width="550" :close-on-click-modal="false" append-to-body destroy-on-close>
|
||||
<el-progress v-if="loading" :text-inside="true" :stroke-width="20" :percentage="percentage" style="margin-bottom: 15px;"/>
|
||||
<div v-loading="loading">
|
||||
<el-upload ref="uploader"
|
||||
drag
|
||||
:accept="accept"
|
||||
:maxSize="maxSize"
|
||||
:limit="1"
|
||||
:data="data"
|
||||
:show-file-list="false"
|
||||
:http-request="request"
|
||||
:before-upload="before"
|
||||
:on-progress="progress"
|
||||
:on-success="success"
|
||||
:on-error="error"
|
||||
>
|
||||
<slot name="uploader">
|
||||
<el-icon class="el-icon--upload"><el-icon-upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处或 <em>点击选择文件上传</em>
|
||||
</div>
|
||||
</slot>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
<template v-if="tip">{{tip}}</template>
|
||||
<template v-else>请上传小于或等于 {{maxSize}}M 的 {{accept}} 格式文件</template>
|
||||
<p v-if="templateUrl" style="margin-top: 7px;">
|
||||
<el-link :href="templateUrl" target="_blank" type="primary" :underline="false">下载导入模板</el-link>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<el-form v-if="$slots.form" inline label-width="100px" label-position="left" style="margin-top: 18px;">
|
||||
<slot name="form" :formData="formData"></slot>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emits: ['success'],
|
||||
props: {
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
data: { type: Object, default: () => {} },
|
||||
accept: { type: String, default: ".xls, .xlsx" },
|
||||
maxSize: { type: Number, default: 10 },
|
||||
tip: { type: String, default: "" },
|
||||
templateUrl: { type: String, default: "" }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
loading: false,
|
||||
percentage: 0,
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
open(){
|
||||
this.dialog = true
|
||||
this.formData = {}
|
||||
},
|
||||
close(){
|
||||
this.dialog = false
|
||||
},
|
||||
before(file){
|
||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||
if (!maxSize) {
|
||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
this.loading = true
|
||||
},
|
||||
progress(e){
|
||||
this.percentage = e.percent
|
||||
},
|
||||
success(res, file){
|
||||
this.$refs.uploader.handleRemove(file)
|
||||
this.$refs.uploader.clearFiles()
|
||||
this.loading = false
|
||||
this.percentage = 0
|
||||
this.$emit('success', res, this.close)
|
||||
},
|
||||
error(err){
|
||||
this.loading = false
|
||||
this.percentage = 0
|
||||
this.$notify.error({
|
||||
title: '上传文件未成功',
|
||||
message: err
|
||||
})
|
||||
},
|
||||
request(param){
|
||||
Object.assign(param.data, this.formData)
|
||||
const data = new FormData();
|
||||
data.append(param.filename, param.file);
|
||||
for (const key in param.data) {
|
||||
data.append(key, param.data[key]);
|
||||
}
|
||||
this.apiObj.post(data, {
|
||||
onUploadProgress: e => {
|
||||
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
|
||||
param.onProgress({percent: complete})
|
||||
}
|
||||
}).then(res => {
|
||||
param.onSuccess(res)
|
||||
}).catch(err => {
|
||||
param.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,283 @@
|
||||
<!--
|
||||
* @Descripttion: 资源文件选择器
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年10月11日16:01:40
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-file-select">
|
||||
<div class="sc-file-select__side" v-loading="menuLoading">
|
||||
<div class="sc-file-select__side-menu">
|
||||
<el-tree ref="group" class="menu" :data="menu" :node-key="treeProps.key" :props="treeProps" :current-node-key="menu.length>0?menu[0][treeProps.key]:''" highlight-current @node-click="groupClick">
|
||||
<template #default="{ node }">
|
||||
<span class="el-tree-node__label">
|
||||
<el-icon class="icon"><el-icon-folder /></el-icon>{{node.label}}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
<div class="sc-file-select__side-msg" v-if="multiple">
|
||||
已选择 <b>{{value.length}}</b> / <b>{{max}}</b> 项
|
||||
</div>
|
||||
</div>
|
||||
<div class="sc-file-select__files" v-loading="listLoading">
|
||||
<div class="sc-file-select__top">
|
||||
<div class="upload" v-if="!hideUpload">
|
||||
<el-upload class="sc-file-select__upload" action="" multiple :show-file-list="false" :accept="accept" :on-change="uploadChange" :before-upload="uploadBefore" :on-progress="uploadProcess" :on-success="uploadSuccess" :on-error="uploadError" :http-request="uploadRequest">
|
||||
<el-button type="primary" icon="el-icon-upload">本地上传</el-button>
|
||||
</el-upload>
|
||||
<span class="tips"><el-icon><el-icon-warning /></el-icon>大小不超过{{maxSize}}MB</span>
|
||||
</div>
|
||||
<div class="keyword">
|
||||
<el-input v-model="keyword" prefix-icon="el-icon-search" placeholder="文件名搜索" clearable @keyup.enter="search" @clear="search"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sc-file-select__list">
|
||||
<el-scrollbar ref="scrollbar">
|
||||
<el-empty v-if="fileList.length==0 && data.length==0" description="无数据" :image-size="80"></el-empty>
|
||||
<div v-for="(file, index) in fileList" :key="index" class="sc-file-select__item">
|
||||
<div class="sc-file-select__item__file">
|
||||
<div class="sc-file-select__item__upload">
|
||||
<el-progress type="circle" :percentage="file.progress" :width="70"></el-progress>
|
||||
</div>
|
||||
<el-image :src="file.tempImg" fit="contain"></el-image>
|
||||
</div>
|
||||
<p>{{file.name}}</p>
|
||||
</div>
|
||||
<div v-for="item in data" :key="item[fileProps.key]" class="sc-file-select__item" :class="{active: value.includes(item[fileProps.url]) }" @click="select(item)">
|
||||
<div class="sc-file-select__item__file">
|
||||
<div class="sc-file-select__item__checkbox" v-if="multiple">
|
||||
<el-icon><el-icon-check /></el-icon>
|
||||
</div>
|
||||
<div class="sc-file-select__item__select" v-else>
|
||||
<el-icon><el-icon-check /></el-icon>
|
||||
</div>
|
||||
<div class="sc-file-select__item__box"></div>
|
||||
<el-image v-if="_isImg(item[fileProps.url])" :src="item[fileProps.url]" fit="contain" lazy></el-image>
|
||||
<div v-else class="item-file item-file-doc">
|
||||
<i v-if="files[_getExt(item[fileProps.url])]" :class="files[_getExt(item[fileProps.url])].icon" :style="{color:files[_getExt(item[fileProps.url])].color}"></i>
|
||||
<i v-else class="sc-icon-file-list-fill" style="color: #999;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p :title="item[fileProps.fileName]">{{item[fileProps.fileName]}}</p>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="sc-file-select__pagination">
|
||||
<el-pagination small background layout="prev, pager, next" :total="total" :page-size="pageSize" v-model:currentPage="currentPage" @current-change="reload"></el-pagination>
|
||||
</div>
|
||||
<div class="sc-file-select__do">
|
||||
<slot name="do"></slot>
|
||||
<el-button type="primary" :disabled="value.length<=0" @click="submit">确 定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/fileSelect"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: null,
|
||||
hideUpload: { type: Boolean, default: false },
|
||||
multiple: { type: Boolean, default: false },
|
||||
max: {type: Number, default: config.max},
|
||||
onlyImage: { type: Boolean, default: false },
|
||||
maxSize: {type: Number, default: config.maxSize},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
keyword: null,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
data: [],
|
||||
menu: [],
|
||||
menuId: '',
|
||||
value: this.multiple ? [] : '',
|
||||
fileList: [],
|
||||
accept: this.onlyImage ? "image/gif, image/jpeg, image/png" : "",
|
||||
listLoading: false,
|
||||
menuLoading: false,
|
||||
treeProps: config.menuProps,
|
||||
fileProps: config.fileProps,
|
||||
files: config.files
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
multiple(){
|
||||
this.value = this.multiple ? [] : ''
|
||||
this.$emit('update:modelValue', JSON.parse(JSON.stringify(this.value)));
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getMenu()
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
//获取分类数据
|
||||
async getMenu(){
|
||||
this.menuLoading = true
|
||||
var res = await config.menuApiObj.get()
|
||||
this.menu = res.data
|
||||
this.menuLoading = false
|
||||
},
|
||||
//获取列表数据
|
||||
async getData(){
|
||||
this.listLoading = true
|
||||
var reqData = {
|
||||
[config.request.menuKey]: this.menuId,
|
||||
[config.request.page]: this.currentPage,
|
||||
[config.request.pageSize]: this.pageSize,
|
||||
[config.request.keyword]: this.keyword
|
||||
}
|
||||
if(this.onlyImage){
|
||||
reqData.type = 'image'
|
||||
}
|
||||
var res = await config.listApiObj.get(reqData)
|
||||
var parseData = config.listParseData(res)
|
||||
this.data = parseData.rows
|
||||
this.total = parseData.total
|
||||
this.listLoading = false
|
||||
this.$refs.scrollbar.setScrollTop(0)
|
||||
},
|
||||
//树点击事件
|
||||
groupClick(data){
|
||||
this.menuId = data.id
|
||||
this.currentPage = 1
|
||||
this.keyword = null
|
||||
this.getData()
|
||||
},
|
||||
//分页刷新表格
|
||||
reload(){
|
||||
this.getData()
|
||||
},
|
||||
search(){
|
||||
this.currentPage = 1
|
||||
this.getData()
|
||||
},
|
||||
select(item){
|
||||
const itemUrl = item[this.fileProps.url]
|
||||
if(this.multiple){
|
||||
if(this.value.includes(itemUrl)){
|
||||
this.value.splice(this.value.findIndex(f => f == itemUrl), 1)
|
||||
}else{
|
||||
this.value.push(itemUrl)
|
||||
}
|
||||
}else{
|
||||
if(this.value.includes(itemUrl)){
|
||||
this.value = ''
|
||||
}else{
|
||||
this.value = itemUrl
|
||||
}
|
||||
}
|
||||
},
|
||||
submit(){
|
||||
const value = JSON.parse(JSON.stringify(this.value))
|
||||
this.$emit('update:modelValue', value);
|
||||
this.$emit('submit', value);
|
||||
},
|
||||
//上传处理
|
||||
uploadChange(file, fileList){
|
||||
file.tempImg = URL.createObjectURL(file.raw);
|
||||
this.fileList = fileList
|
||||
},
|
||||
uploadBefore(file){
|
||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||
if (!maxSize) {
|
||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
uploadRequest(param){
|
||||
var apiObj = config.apiObj;
|
||||
const data = new FormData();
|
||||
data.append("file", param.file);
|
||||
data.append([config.request.menuKey], this.menuId);
|
||||
apiObj.post(data, {
|
||||
onUploadProgress: e => {
|
||||
param.onProgress(e)
|
||||
}
|
||||
}).then(res => {
|
||||
param.onSuccess(res)
|
||||
}).catch(err => {
|
||||
param.onError(err)
|
||||
})
|
||||
},
|
||||
uploadProcess(event, file){
|
||||
file.progress = Number((event.loaded / event.total * 100).toFixed(2))
|
||||
},
|
||||
uploadSuccess(res, file){
|
||||
this.fileList.splice(this.fileList.findIndex(f => f.uid == file.uid), 1)
|
||||
var response = config.uploadParseData(res);
|
||||
this.data.unshift({
|
||||
[this.fileProps.key]: response.id,
|
||||
[this.fileProps.fileName]: response.fileName,
|
||||
[this.fileProps.url]: response.url
|
||||
})
|
||||
if(!this.multiple){
|
||||
this.value = response.url
|
||||
}
|
||||
},
|
||||
uploadError(err){
|
||||
this.$notify.error({
|
||||
title: '上传文件错误',
|
||||
message: err
|
||||
})
|
||||
},
|
||||
//内置函数
|
||||
_isImg(fileUrl){
|
||||
const imgExt = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
|
||||
const fileExt = fileUrl.substring(fileUrl.lastIndexOf("."))
|
||||
return imgExt.indexOf(fileExt) != -1
|
||||
},
|
||||
_getExt(fileUrl){
|
||||
return fileUrl.substring(fileUrl.lastIndexOf(".") + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-file-select {display: flex;}
|
||||
.sc-file-select__files {flex: 1;}
|
||||
|
||||
.sc-file-select__list {height:400px;}
|
||||
.sc-file-select__item {display: inline-block;float: left;margin:0 15px 25px 0;width:110px;cursor: pointer;}
|
||||
.sc-file-select__item__file {width:110px;height:110px;position: relative;}
|
||||
.sc-file-select__item__file .el-image {width:110px;height:110px;}
|
||||
.sc-file-select__item__box {position: absolute;top:0;right:0;bottom:0;left:0;border: 2px solid var(--el-color-success);z-index: 1;display: none;}
|
||||
.sc-file-select__item__box::before {content: '';position: absolute;top:0;right:0;bottom:0;left:0;background: var(--el-color-success);opacity: 0.2;display: none;}
|
||||
.sc-file-select__item:hover .sc-file-select__item__box {display: block;}
|
||||
.sc-file-select__item.active .sc-file-select__item__box {display: block;}
|
||||
.sc-file-select__item.active .sc-file-select__item__box::before {display: block;}
|
||||
.sc-file-select__item p {margin-top: 10px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;-webkit-text-overflow:ellipsis;text-align: center;}
|
||||
.sc-file-select__item__checkbox {position: absolute;width: 20px;height: 20px;top:7px;right:7px;z-index: 2;background: rgba(0,0,0,0.2);border: 1px solid #fff;display: flex;flex-direction: column;align-items: center;justify-content: center;}
|
||||
.sc-file-select__item__checkbox i {font-size: 14px;color: #fff;font-weight: bold;display: none;}
|
||||
.sc-file-select__item__select {position: absolute;width: 20px;height: 20px;top:0px;right:0px;z-index: 2;background: var(--el-color-success);display: none;flex-direction: column;align-items: center;justify-content: center;}
|
||||
.sc-file-select__item__select i {font-size: 14px;color: #fff;font-weight: bold;}
|
||||
.sc-file-select__item.active .sc-file-select__item__checkbox {background: var(--el-color-success);}
|
||||
.sc-file-select__item.active .sc-file-select__item__checkbox i {display: block;}
|
||||
.sc-file-select__item.active .sc-file-select__item__select {display: flex;}
|
||||
.sc-file-select__item__file .item-file {width:110px;height:110px;display: flex;flex-direction: column;align-items: center;justify-content: center;}
|
||||
.sc-file-select__item__file .item-file i {font-size: 40px;}
|
||||
.sc-file-select__item__file .item-file.item-file-doc {color: #409eff;}
|
||||
|
||||
.sc-file-select__item__upload {position: absolute;top:0;right:0;bottom:0;left:0;z-index: 1;background: rgba(255,255,255,0.7);display: flex;flex-direction: column;align-items: center;justify-content: center;}
|
||||
|
||||
.sc-file-select__side {width: 200px;margin-right: 15px;border-right: 1px solid rgba(128,128,128,0.2);display: flex;flex-flow: column;}
|
||||
.sc-file-select__side-menu {flex: 1;}
|
||||
.sc-file-select__side-msg {height:32px;line-height: 32px;}
|
||||
|
||||
.sc-file-select__top {margin-bottom: 15px;display: flex;justify-content: space-between;}
|
||||
.sc-file-select__upload {display: inline-block;}
|
||||
.sc-file-select__top .tips {font-size: 12px;margin-left: 10px;color: #999;}
|
||||
.sc-file-select__top .tips i {font-size: 14px;margin-right: 5px;position: relative;bottom: -0.125em;}
|
||||
.sc-file-select__pagination {margin:15px 0;}
|
||||
|
||||
.sc-file-select__do {text-align: right;}
|
||||
</style>
|
||||
@@ -0,0 +1,310 @@
|
||||
<!--
|
||||
* @Descripttion: 过滤器V2
|
||||
* @version: 2.5
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月30日14:48:41
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月13日21:15:44
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-filterBar">
|
||||
<slot :filterLength="filterObjLength" :openFilter="openFilter">
|
||||
<el-badge :value="filterObjLength" type="danger" :hidden="filterObjLength<=0">
|
||||
<el-button icon="el-icon-filter" @click="openFilter"></el-button>
|
||||
</el-badge>
|
||||
</slot>
|
||||
|
||||
<el-drawer title="过滤器" v-model="drawer" :size="650" append-to-body>
|
||||
<el-container v-loading="saveLoading">
|
||||
<el-main style="padding:0">
|
||||
<el-tabs class="root">
|
||||
<el-tab-pane lazy>
|
||||
<template #label>
|
||||
<div class="tabs-label">过滤项</div>
|
||||
</template>
|
||||
<el-scrollbar>
|
||||
<div class="sc-filter-main">
|
||||
<h2>设置过滤条件</h2>
|
||||
<div v-if="filter.length<=0" class="nodata">
|
||||
没有默认过滤条件,请点击增加过滤项
|
||||
</div>
|
||||
<table v-else>
|
||||
<colgroup>
|
||||
<col width="50">
|
||||
<col width="140">
|
||||
<col v-if="showOperator" width="120">
|
||||
<col>
|
||||
<col width="40">
|
||||
</colgroup>
|
||||
<tr v-for="(item,index) in filter" :key="index">
|
||||
<td>
|
||||
<el-tag>{{index+1}}</el-tag>
|
||||
</td>
|
||||
<td>
|
||||
<py-select v-model="item.field" :options="fields" placeholder="过滤字段" filterable @change="fieldChange(item)">
|
||||
</py-select>
|
||||
</td>
|
||||
<td v-if="showOperator">
|
||||
<el-select v-model="item.operator" placeholder="运算符">
|
||||
<el-option v-for="ope in item.field.operators || operator" :key="ope.value" :label="ope.label" :value="ope.value"></el-option>
|
||||
</el-select>
|
||||
</td>
|
||||
<td>
|
||||
<el-input v-if="!item.field.type" v-model="item.value" placeholder="请选择过滤字段" disabled></el-input>
|
||||
<!-- 输入框 -->
|
||||
<el-input v-if="item.field.type=='text'" v-model="item.value" :placeholder="item.field.placeholder||'请输入'"></el-input>
|
||||
<!-- 下拉框 -->
|
||||
<el-select v-if="item.field.type=='select'" v-model="item.value" :placeholder="item.field.placeholder||'请选择'" filterable :multiple="item.field.extend.multiple" :loading="item.selectLoading" @visible-change="visibleChange($event, item)" :remote="item.field.extend.remote" :remote-method="(query)=>{remoteMethod(query, item)}">
|
||||
<el-option v-for="field in item.field.extend.data" :key="field.value" :label="field.label" :value="field.value"></el-option>
|
||||
</el-select>
|
||||
<!-- 日期 -->
|
||||
<el-date-picker v-if="item.field.type=='date'" v-model="item.value" type="date" value-format="YYYY-MM-DD" :placeholder="item.field.placeholder||'请选择日期'" style="width: 100%;"></el-date-picker>
|
||||
<!-- 日期范围 -->
|
||||
<el-date-picker v-if="item.field.type=='daterange'" v-model="item.value" type="daterange" value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100%;"></el-date-picker>
|
||||
<!-- 日期时间 -->
|
||||
<el-date-picker v-if="item.field.type=='datetime'" v-model="item.value" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" :placeholder="item.field.placeholder||'请选择日期'" style="width: 100%;"></el-date-picker>
|
||||
<!-- 日期时间范围 -->
|
||||
<el-date-picker v-if="item.field.type=='datetimerange'" v-model="item.value" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100%;"></el-date-picker>
|
||||
<!-- 开关 -->
|
||||
<el-switch v-if="item.field.type=='switch'" v-model="item.value" active-value="1" inactive-value="0"></el-switch>
|
||||
<!-- 标签 -->
|
||||
<el-select v-if="item.field.type=='tags'" v-model="item.value" multiple filterable allow-create default-first-option no-data-text="输入关键词后按回车确认" :placeholder="item.field.placeholder||'请输入'"></el-select>
|
||||
</td>
|
||||
<td>
|
||||
<el-icon class="del" @click="delFilter(index)"><el-icon-delete /></el-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<el-button type="primary" text icon="el-icon-plus" @click="addFilter">增加过滤项</el-button>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane lazy>
|
||||
<template #label>
|
||||
<div class="tabs-label">常用</div>
|
||||
</template>
|
||||
<el-scrollbar>
|
||||
<my ref="my" :data="myFilter" :filterName="filterName" @selectMyfilter="selectMyfilter"></my>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-main>
|
||||
<el-footer>
|
||||
<el-button type="primary" @click="ok" :disabled="filter.length<=0">立即过滤</el-button>
|
||||
<el-button type="primary" plain @click="saveMy" :disabled="filter.length<=0">另存为常用</el-button>
|
||||
<el-button @click="clear">清空过滤</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/filterBar"
|
||||
import pySelect from './pySelect'
|
||||
import my from './my'
|
||||
|
||||
export default {
|
||||
name: 'filterBar',
|
||||
components: {
|
||||
pySelect,
|
||||
my
|
||||
},
|
||||
props: {
|
||||
filterName: { type: String, default: "" },
|
||||
showOperator: { type: Boolean, default: true },
|
||||
options: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: false,
|
||||
operator: config.operator,
|
||||
fields: this.options,
|
||||
filter: [],
|
||||
myFilter: [],
|
||||
filterObjLength: 0,
|
||||
saveLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filterObj(){
|
||||
const obj = {}
|
||||
this.filter.forEach((item) => {
|
||||
obj[item.field.value] = this.showOperator ? `${item.value}${config.separator}${item.operator}` : `${item.value}`
|
||||
})
|
||||
return obj
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
//默认显示的过滤项
|
||||
this.fields.forEach((item) => {
|
||||
if(item.selected){
|
||||
this.filter.push({
|
||||
field: item,
|
||||
operator: item.operator || 'include',
|
||||
value: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
//打开过滤器
|
||||
openFilter(){
|
||||
this.drawer = true
|
||||
},
|
||||
//增加过滤项
|
||||
addFilter(){
|
||||
if(this.fields.length<=0){
|
||||
this.$message.warning('无过滤项');
|
||||
return false
|
||||
}
|
||||
const filterNum = this.fields[this.filter.length] || this.fields[0]
|
||||
this.filter.push({
|
||||
field: filterNum,
|
||||
operator: filterNum.operator || 'include',
|
||||
value: ''
|
||||
})
|
||||
},
|
||||
//删除过滤项
|
||||
delFilter(index){
|
||||
this.filter.splice(index, 1)
|
||||
},
|
||||
//过滤项字段变更事件
|
||||
fieldChange(tr){
|
||||
let oldType = tr.field.type
|
||||
tr.field.type = ''
|
||||
this.$nextTick(() => {
|
||||
tr.field.type = oldType
|
||||
})
|
||||
tr.operator = tr.field.operator || 'include'
|
||||
tr.value = ''
|
||||
},
|
||||
//下拉框显示事件处理异步
|
||||
async visibleChange(isopen, item){
|
||||
if(isopen && item.field.extend.request && !item.field.extend.remote){
|
||||
item.selectLoading = true;
|
||||
try {
|
||||
var data = await item.field.extend.request()
|
||||
}catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
item.field.extend.data = data;
|
||||
item.selectLoading = false;
|
||||
}
|
||||
},
|
||||
//下拉框显示事件处理异步搜索
|
||||
async remoteMethod(query, item){
|
||||
if(query !== ''){
|
||||
item.selectLoading = true;
|
||||
try {
|
||||
var data = await item.field.extend.request(query);
|
||||
}catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
item.field.extend.data = data;
|
||||
item.selectLoading = false;
|
||||
}else{
|
||||
item.field.extend.data = [];
|
||||
}
|
||||
},
|
||||
//选择常用过滤
|
||||
selectMyfilter(item){
|
||||
//常用过滤回显当前过滤项
|
||||
this.filter = []
|
||||
this.fields.forEach((field) => {
|
||||
var filterValue = item.filterObj[field.value]
|
||||
if(filterValue){
|
||||
var operator = filterValue.split("|")[1]
|
||||
var value = filterValue.split("|")[0]
|
||||
if(field.type=='select' && field.extend.multiple){
|
||||
value = value.split(",")
|
||||
}else if(field.type=='daterange'){
|
||||
value = value.split(",")
|
||||
}
|
||||
this.filter.push({
|
||||
field: field,
|
||||
operator: operator,
|
||||
value: value
|
||||
})
|
||||
}
|
||||
})
|
||||
this.filterObjLength = Object.keys(item.filterObj).length
|
||||
this.$emit('filterChange',item.filterObj)
|
||||
this.drawer = false
|
||||
},
|
||||
//立即过滤
|
||||
ok(){
|
||||
this.filterObjLength = this.filter.length
|
||||
this.$emit('filterChange',this.filterObj)
|
||||
this.drawer = false
|
||||
},
|
||||
//保存常用
|
||||
saveMy(){
|
||||
this.$prompt('常用过滤名称', '另存为常用', {
|
||||
inputPlaceholder: '请输入识别度较高的常用过滤名称',
|
||||
inputPattern: /\S/,
|
||||
inputErrorMessage: '名称不能为空'
|
||||
})
|
||||
.then(async ({ value }) => {
|
||||
this.saveLoading = true
|
||||
const saveObj = {
|
||||
title: value,
|
||||
filterObj: this.filterObj
|
||||
}
|
||||
try {
|
||||
var save = await config.saveMy(this.filterName, saveObj)
|
||||
}catch (error) {
|
||||
this.saveLoading = false
|
||||
console.log(error);
|
||||
return false
|
||||
}
|
||||
if(!save){
|
||||
return false
|
||||
}
|
||||
|
||||
this.myFilter.push(saveObj)
|
||||
this.$message.success(`${this.filterName} 保存常用成功`)
|
||||
this.saveLoading = false
|
||||
})
|
||||
.catch(() => {
|
||||
//
|
||||
})
|
||||
},
|
||||
//清空过滤
|
||||
clear(){
|
||||
this.filter = []
|
||||
this.filterObjLength = 0
|
||||
this.$emit('filterChange',this.filterObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tabs-label {padding:0 20px;}
|
||||
|
||||
.nodata {height:46px;line-height: 46px;margin:15px 0;border: 1px dashed #e6e6e6;color: #999;text-align: center;border-radius: 3px;}
|
||||
|
||||
.sc-filter-main {padding:20px;border-bottom: 1px solid #e6e6e6;background: #fff;}
|
||||
.sc-filter-main h2 {font-size: 12px;color: #999;font-weight: normal;}
|
||||
.sc-filter-main table {width: 100%;margin: 15px 0;}
|
||||
.sc-filter-main table tr {}
|
||||
.sc-filter-main table td {padding:5px 10px 5px 0;}
|
||||
.sc-filter-main table td:deep(.el-input .el-input__inner) {vertical-align: top;}
|
||||
.sc-filter-main table td .el-select {display: block;}
|
||||
.sc-filter-main table td .el-date-editor.el-input {display: block;width: 100%;}
|
||||
.sc-filter-main table td .del {background: #fff;color: #999;width: 32px;height: 32px;line-height: 32px;text-align: center;border-radius:50%;font-size: 12px;cursor: pointer;}
|
||||
.sc-filter-main table td .del:hover {background: #F56C6C;color: #fff;}
|
||||
|
||||
.root {display: flex;height: 100%;flex-direction: column}
|
||||
.root:deep(.el-tabs__header) {margin: 0;}
|
||||
.root:deep(.el-tabs__content) {flex: 1;background: #f6f8f9;}
|
||||
.root:deep(.el-tabs__content) .el-tab-pane{overflow: auto;height:100%;}
|
||||
|
||||
.dark .root:deep(.el-tabs__content) {background: var(--el-bg-color-overlay);}
|
||||
.dark .sc-filter-main {background: var(--el-bg-color);border-color:var(--el-border-color-light);}
|
||||
.dark .sc-filter-main table td .del {background: none;}
|
||||
.dark .sc-filter-main table td .del:hover {background: #F56C6C;}
|
||||
.dark .nodata {border-color:var(--el-border-color-light);}
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<!--
|
||||
* @Descripttion: 过滤器V2 常用组件
|
||||
* @version: 2.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月31日16:49:56
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-filter-my">
|
||||
<div v-if="loading" class="sc-filter-my-loading">
|
||||
<el-skeleton :rows="2" animated />
|
||||
</div>
|
||||
<template v-else>
|
||||
<el-empty v-if="myFilter.length<=0" :image-size="100">
|
||||
<template #description>
|
||||
<h2>没有常用的过滤</h2>
|
||||
<p style="margin-top: 10px;max-width: 300px;">常用过滤可以将多个过滤条件保存为一个集合,方便下次进行相同条件的过滤</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<ul v-else class="sc-filter-my-list">
|
||||
<h2>我的常用过滤</h2>
|
||||
<li v-for="(item, index) in myFilter" :key="index" @click="selectMyfilter(item)">
|
||||
<label>{{item.title}}</label>
|
||||
<el-popconfirm title="确认删除此常用过滤吗?" @confirm="closeMyfilter(item, index)">
|
||||
<template #reference>
|
||||
<el-icon class="del" @click.stop="()=>{}"><el-icon-delete /></el-icon>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/filterBar";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
filterName: { type: String, default: "" },
|
||||
data: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
myFilter: []
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
data: {
|
||||
handler(){
|
||||
this.myFilter = this.data
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.myFilter = this.data
|
||||
this.getMyfilter()
|
||||
},
|
||||
methods: {
|
||||
//选择常用过滤
|
||||
selectMyfilter(item){
|
||||
this.$emit('selectMyfilter', item)
|
||||
},
|
||||
//删除常用过滤
|
||||
async closeMyfilter(item, index){
|
||||
try {
|
||||
var del = await config.delMy(this.filterName)
|
||||
}catch (error) {
|
||||
return false
|
||||
}
|
||||
if(!del){
|
||||
return false
|
||||
}
|
||||
this.myFilter.splice(index, 1)
|
||||
this.$message.success('删除常用成功')
|
||||
},
|
||||
//远程获取我的常用
|
||||
async getMyfilter(){
|
||||
this.loading = true
|
||||
try {
|
||||
this.myFilter = await config.getMy(this.filterName)
|
||||
}catch (error) {
|
||||
return false
|
||||
}
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-filter-my {}
|
||||
.sc-filter-my-loading {padding:15px;}
|
||||
.sc-filter-my-list {list-style-type: none;background: #fff;border-bottom: 1px solid #e6e6e6;}
|
||||
.sc-filter-my-list h2 {font-size: 12px;color: #999;font-weight: normal;padding:20px;}
|
||||
.sc-filter-my-list li {padding:12px 20px;cursor: pointer;position: relative;color: #3c4a54;padding-right:80px;}
|
||||
.sc-filter-my-list li:hover {background: #ecf5ff;color: #409EFF;}
|
||||
.sc-filter-my-list li label {cursor: pointer;font-size: 14px;line-height: 1.8;}
|
||||
.sc-filter-my-list li label span {color: #999;margin-right: 10px;}
|
||||
.sc-filter-my-list li .del {position: absolute;right:20px;top:8px;border-radius:50%;width: 32px;height: 32px;display: flex;align-items: center;justify-content: center;color: #999;}
|
||||
.sc-filter-my-list li .del:hover {background: #F56C6C;color: #fff;}
|
||||
|
||||
[data-theme='dark'] .sc-filter-my .el-empty h2 {color: #fff;}
|
||||
[data-theme='dark'] .sc-filter-my-list {background: none;border-color:var(--el-border-color-base);}
|
||||
[data-theme='dark'] .sc-filter-my-list li {color: #d0d0d0;}
|
||||
[data-theme='dark'] .sc-filter-my-list li:hover {background: var(--el-color-white);}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
* @Descripttion: 二次封装el-select 支持拼音
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月31日22:26:56
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-select v-bind="$attrs" :filter-method="filterMethod" @visible-change="visibleChange">
|
||||
<el-option v-for="field in optionsList" :key="field.value" :label="field.label" :value="field"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pinyin from 'pinyin-match'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
options: { type: Array, default: () => [] }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
optionsList: [],
|
||||
optionsList_: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.optionsList = this.options
|
||||
this.optionsList_ = [...this.options]
|
||||
},
|
||||
methods: {
|
||||
filterMethod(keyword){
|
||||
if(keyword){
|
||||
this.optionsList = this.optionsList_
|
||||
this.optionsList = this.optionsList.filter((item) =>
|
||||
pinyin.match(item.label, keyword)
|
||||
);
|
||||
}else{
|
||||
this.optionsList = this.optionsList_
|
||||
}
|
||||
},
|
||||
visibleChange(isopen){
|
||||
if(isopen){
|
||||
this.optionsList = this.optionsList_
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,295 @@
|
||||
<!--
|
||||
* @Descripttion: 动态表单渲染器
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年9月22日09:26:25
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-skeleton v-if="renderLoading || Object.keys(form).length==0" animated />
|
||||
|
||||
<el-form v-else ref="form" :model="form" :label-width="config.labelWidth" :label-position="config.labelPosition" v-loading="loading" element-loading-text="Loading...">
|
||||
<el-row :gutter="15">
|
||||
<template v-for="(item, index) in config.formItems" :key="index">
|
||||
<el-col :span="item.span || 24" v-if="!hideHandle(item)">
|
||||
<sc-title v-if="item.type=='title'" :title="item.label"></sc-title>
|
||||
<el-form-item v-else :prop="item.name" :rules="rulesHandle(item)">
|
||||
<template #label>
|
||||
{{item.label}}
|
||||
<el-tooltip v-if="item.tips" :content="item.tips">
|
||||
<el-icon><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<!-- input -->
|
||||
<template v-if="item.type=='input' || item.type=='string'" >
|
||||
<el-input v-model="form[item.name]" :placeholder="item.options?.placeholder" clearable :maxlength="item.options?.maxlength" show-word-limit :disabled="item.disabled" />
|
||||
</template>
|
||||
<!-- input -->
|
||||
<template v-else-if="item.type=='textarea'" >
|
||||
<el-input type="textarea" v-model="form[item.name]" :placeholder="item.options?.placeholder" clearable :maxlength="item.options.maxlength" show-word-limit :disabled="item.disabled" />
|
||||
</template>
|
||||
<!-- checkbox -->
|
||||
<template v-else-if="item.type=='checkbox'" >
|
||||
<template v-if="item.name" >
|
||||
<el-checkbox v-model="form[item.name][_item.name]" :label="_item.label" v-for="(_item, _index) in item.options.items" :key="_index" :disabled="item.disabled"></el-checkbox>
|
||||
</template>
|
||||
<template v-else >
|
||||
<el-checkbox v-model="form[_item.name]" :label="_item.label" v-for="(_item, _index) in item.options.items" :key="_index" :disabled="item.disabled"></el-checkbox>
|
||||
</template>
|
||||
</template>
|
||||
<!-- checkboxGroup -->
|
||||
<template v-else-if="item.type=='checkboxGroup'" >
|
||||
<el-checkbox-group v-model="form[item.name]" :disabled="item.disabled">
|
||||
<el-checkbox v-for="_item in item.options.items" :key="_item.value" :label="_item.value">{{_item.label}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
<!-- upload -->
|
||||
<template v-else-if="item.type=='upload' || item.type == 'image'" >
|
||||
<el-form-item :prop="item.name">
|
||||
<sc-upload v-model="form[item.name]" :title="item.label"></sc-upload>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<!-- switch -->
|
||||
<template v-else-if="item.type=='switch' || item.type=='boolean'" >
|
||||
<el-switch v-model="form[item.name]" :active-value="1" :inactive-value="0" :disabled="item.disabled" />
|
||||
</template>
|
||||
<!-- select -->
|
||||
<template v-else-if="item.type=='select'" >
|
||||
<el-select v-model="form[item.name]" :multiple="item.options.multiple" :placeholder="item.options?.placeholder" clearable filterable style="width: 100%;" :disabled="item.disabled">
|
||||
<el-option v-for="option in item.options.items" :key="option.value" :label="option.label" :value="option.value"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else-if="item.type=='scSelect'" >
|
||||
<sc-select v-model="form[item.name]" :dic="item.options?.dic" :apiObj="item.options?.apiObj" :props="item.options?.props" style="min-width: 240px;" />
|
||||
</template>
|
||||
<template v-else-if="item.type=='scSelectTree'" >
|
||||
<sc-select-tree v-model="form[item.name]" :dic="item.options?.dic" :apiObj="item.options?.apiObj" :props="item.options?.props" style="min-width: 240px;" />
|
||||
</template>
|
||||
<!-- cascader -->
|
||||
<template v-else-if="item.type=='cascader'" >
|
||||
<el-cascader v-model="form[item.name]" :options="item.options.items" clearable></el-cascader>
|
||||
</template>
|
||||
<!-- date -->
|
||||
<template v-else-if="item.type=='date'" >
|
||||
<el-date-picker v-model="form[item.name]" :type="item.options.type" :shortcuts="item.options.shortcuts" :default-time="item.options.defaultTime" :value-format="item.options.valueFormat" :placeholder="item.options?.placeholder || '请选择'"></el-date-picker>
|
||||
</template>
|
||||
<!-- number -->
|
||||
<template v-else-if="item.type=='number'" >
|
||||
<el-input-number v-model="form[item.name]" controls-position="right" :placeholder="item.options?.placeholder" />
|
||||
</template>
|
||||
<!-- radio -->
|
||||
<template v-else-if="item.type=='radio'" >
|
||||
<el-radio-group v-model="form[item.name]">
|
||||
<el-radio v-for="_item in item.options.items" :key="_item.value" :label="_item.value">{{_item.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<!-- color -->
|
||||
<template v-else-if="item.type=='color'" >
|
||||
<el-color-picker v-model="form[item.name]" />
|
||||
</template>
|
||||
<!-- rate -->
|
||||
<template v-else-if="item.type=='rate'" >
|
||||
<el-rate style="margin-top: 6px;" v-model="form[item.name]"></el-rate>
|
||||
</template>
|
||||
<!-- slider -->
|
||||
<template v-else-if="item.type=='slider'" >
|
||||
<el-slider v-model="form[item.name]" :marks="item.options.marks"></el-slider>
|
||||
</template>
|
||||
<!-- tableselect -->
|
||||
<template v-else-if="item.type=='tableselect'" >
|
||||
<tableselect-render v-model="form[item.name]" :item="item"></tableselect-render>
|
||||
</template>
|
||||
<!-- editor -->
|
||||
<template v-else-if="item.type=='editor'" >
|
||||
<sc-editor v-model="form[item.name]" placeholder="请输入" :height="400"></sc-editor>
|
||||
</template>
|
||||
<!-- noComponent -->
|
||||
<template v-else>
|
||||
<el-tag type="danger">[{{item.type}}] Component not found</el-tag>
|
||||
</template>
|
||||
<div v-if="item.message" class="el-form-item-msg">{{item.message}}</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<el-col :span="24">
|
||||
<el-form-item>
|
||||
<slot>
|
||||
<el-button type="primary" @click="submit">提交</el-button>
|
||||
</slot>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import http from "@/utils/request"
|
||||
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const tableselectRender = defineAsyncComponent(() => import('./items/tableselect'))
|
||||
const scEditor = defineAsyncComponent(() => import('@/components/scEditor'))
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
config: { type: Object, default: () => {} },
|
||||
loading: { type: Boolean, default: false },
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
},
|
||||
components: {
|
||||
tableselectRender,
|
||||
scEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
renderLoading: false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(){
|
||||
if(this.hasConfig){
|
||||
this.deepMerge(this.form, this.modelValue)
|
||||
}
|
||||
},
|
||||
config(){
|
||||
this.render()
|
||||
},
|
||||
form:{
|
||||
handler(val){
|
||||
this.$emit("update:modelValue", val)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasConfig(){
|
||||
return Object.keys(this.config).length>0
|
||||
},
|
||||
hasValue(){
|
||||
return Object.keys(this.modelValue).length>0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
if(this.hasConfig){
|
||||
this.render()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//构建form对象
|
||||
render() {
|
||||
this.config.formItems.forEach((item) => {
|
||||
if(item.type == 'checkbox'){
|
||||
if(item.name){
|
||||
const value = {}
|
||||
item.options.items.forEach((option) => {
|
||||
value[option.name] = option.value
|
||||
})
|
||||
this.form[item.name] = value
|
||||
}else{
|
||||
item.options.items.forEach((option) => {
|
||||
this.form[option.name] = option.value
|
||||
})
|
||||
}
|
||||
}else if(item.type == 'upload'){
|
||||
if(item.name){
|
||||
const value = {}
|
||||
item.options.items.forEach((option) => {
|
||||
value[option.name] = option.value
|
||||
})
|
||||
this.form[item.name] = value
|
||||
}else{
|
||||
item.options.items.forEach((option) => {
|
||||
this.form[option.name] = option.value
|
||||
})
|
||||
}
|
||||
}else{
|
||||
this.form[item.name] = item.value
|
||||
}
|
||||
})
|
||||
if(this.hasValue){
|
||||
this.form = this.deepMerge(this.form, this.modelValue)
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
//处理远程选项数据
|
||||
getData() {
|
||||
this.renderLoading = true
|
||||
var remoteData = []
|
||||
this.config.formItems.forEach((item) => {
|
||||
if(item.options && item.options.remote){
|
||||
var req = http.get(item.options.remote.api, item.options.remote.data).then(res=>{
|
||||
item.options.items = res.data
|
||||
})
|
||||
remoteData.push(req)
|
||||
}
|
||||
})
|
||||
Promise.all(remoteData).then(()=>{
|
||||
this.renderLoading = false
|
||||
})
|
||||
},
|
||||
//合并深结构对象
|
||||
deepMerge(obj1, obj2) {
|
||||
let key;
|
||||
for (key in obj2) {
|
||||
obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" && (obj2[key] && obj2[key].toString() === "[object Object]") ? this.deepMerge(obj1[key], obj2[key]) : (obj1[key] = obj2[key])
|
||||
}
|
||||
return obj1
|
||||
//return JSON.parse(JSON.stringify(obj1))
|
||||
},
|
||||
//处理动态隐藏
|
||||
hideHandle(item){
|
||||
if(item.hideHandle){
|
||||
const exp = eval(item.hideHandle.replace(/\$/g,"this.form"))
|
||||
return exp
|
||||
}
|
||||
return false
|
||||
},
|
||||
//处理动态必填
|
||||
rulesHandle(item){
|
||||
if(item.requiredHandle){
|
||||
const exp = eval(item.requiredHandle.replace(/\$/g,"this.form"))
|
||||
var requiredRule = item.rules.find(t => 'required' in t)
|
||||
requiredRule.required = exp
|
||||
}
|
||||
return item.rules
|
||||
},
|
||||
//数据验证
|
||||
validate(valid, obj){
|
||||
return this.$refs.form.validate(valid, obj)
|
||||
},
|
||||
scrollToField(prop){
|
||||
return this.$refs.form.scrollToField(prop)
|
||||
},
|
||||
resetFields(){
|
||||
return this.$refs.form.resetFields()
|
||||
},
|
||||
//提交
|
||||
submit(){
|
||||
if(!this.apiObj){
|
||||
this.$emit("submit", this.form)
|
||||
}else{
|
||||
this.$refs.form.validate().then(async () => {
|
||||
let res = await this.apiObj.post(this.form)
|
||||
if(res.code == 1){
|
||||
this.$emit('update:modelValue', this.form)
|
||||
this.$emit('onSuccess', res)
|
||||
}else{
|
||||
this.$message.error(res.message)
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$message.error('请认真填写信息!')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<sc-table-select v-model="value" :apiObj="apiObj" :table-width="600" :multiple="item.options.multiple" :props="item.options.props" style="width: 100%;">
|
||||
<el-table-column v-for="(_item, _index) in item.options.column" :key="_index" :prop="_item.prop" :label="_item.label" :width="_item.width"></el-table-column>
|
||||
</sc-table-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'uploadRender',
|
||||
props: {
|
||||
modelValue: [String, Number, Boolean, Date, Object, Array],
|
||||
item: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: this.modelValue,
|
||||
apiObj: this.getApiObj()
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
value(val){
|
||||
this.$emit("update:modelValue", val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getApiObj(){
|
||||
return eval(`this.`+this.item.options.apiObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<!--
|
||||
* @Descripttion: 表单表格
|
||||
* @version: 1.3
|
||||
* @Author: sakuya
|
||||
* @Date: 2023年2月9日12:32:26
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2023年2月17日10:41:47
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-form-table" ref="scFormTable">
|
||||
<el-table :data="data" ref="table" border stripe>
|
||||
<el-table-column type="index" width="50" fixed="left">
|
||||
<template #header>
|
||||
<el-button v-if="!hideAdd" type="primary" icon="el-icon-plus" circle @click="rowAdd"></el-button>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div :class="['sc-form-table-handle', {'sc-form-table-handle-delete':!hideDelete}]">
|
||||
<span>{{scope.$index + 1}}</span>
|
||||
<el-button v-if="!hideDelete" type="danger" icon="el-icon-delete" plain circle @click="rowDel(scope.row, scope.$index)"></el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="" width="50" v-if="dragSort">
|
||||
<template #default>
|
||||
<div class="move" style="cursor: move;"><el-icon-d-caret style="width: 1em; height: 1em;"/></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<slot></slot>
|
||||
<template #empty>
|
||||
{{placeholder}}
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Array, default: () => [] },
|
||||
addTemplate: { type: Object, default: () => {} },
|
||||
placeholder: { type: String, default: "暂无数据" },
|
||||
dragSort: { type: Boolean, default: false },
|
||||
hideAdd: { type: Boolean, default: false },
|
||||
hideDelete: { type: Boolean, default: false }
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
data: []
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.data = this.modelValue
|
||||
if(this.dragSort){
|
||||
this.rowDrop()
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(){
|
||||
this.data = this.modelValue
|
||||
},
|
||||
data: {
|
||||
handler(){
|
||||
this.$emit('update:modelValue', this.data);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowDrop(){
|
||||
const _this = this
|
||||
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
|
||||
Sortable.create(tbody, {
|
||||
handle: ".move",
|
||||
animation: 300,
|
||||
ghostClass: "ghost",
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
_this.data.splice(newIndex, 0, _this.data.splice(oldIndex, 1)[0])
|
||||
const newArray = _this.data.slice(0)
|
||||
const tmpHeight = _this.$refs.scFormTable.offsetHeight
|
||||
_this.$refs.scFormTable.style.setProperty('height', tmpHeight + 'px')
|
||||
_this.data = []
|
||||
_this.$nextTick(() => {
|
||||
_this.data = newArray
|
||||
_this.$nextTick(() => {
|
||||
_this.$refs.scFormTable.style.removeProperty('height')
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
rowAdd(){
|
||||
const temp = JSON.parse(JSON.stringify(this.addTemplate))
|
||||
this.data.push(temp)
|
||||
},
|
||||
rowDel(row, index){
|
||||
this.data.splice(index, 1)
|
||||
},
|
||||
//插入行
|
||||
pushRow(row){
|
||||
const temp = row || JSON.parse(JSON.stringify(this.addTemplate))
|
||||
this.data.push(temp)
|
||||
},
|
||||
//根据index删除
|
||||
deleteRow(index){
|
||||
this.data.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-form-table {width: 100%;}
|
||||
.sc-form-table .sc-form-table-handle {text-align: center;}
|
||||
.sc-form-table .sc-form-table-handle span {display: inline-block;}
|
||||
.sc-form-table .sc-form-table-handle button {display: none;}
|
||||
.sc-form-table .hover-row .sc-form-table-handle-delete span {display: none;}
|
||||
.sc-form-table .hover-row .sc-form-table-handle-delete button {display: inline-block;}
|
||||
.sc-form-table .move {text-align: center;font-size: 14px;margin-top: 3px;}
|
||||
</style>
|
||||
@@ -0,0 +1,128 @@
|
||||
<!--
|
||||
* @Descripttion: 图标选择器组件
|
||||
* @version: 2.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月27日10:02:46
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年6月6日13:48:49
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-icon-select">
|
||||
<div class="sc-icon-select__wrapper" :class="{'hasValue':value}" @click="open">
|
||||
<el-input :prefix-icon="value||'el-icon-plus'" v-model="value" :disabled="disabled" readonly></el-input>
|
||||
</div>
|
||||
<el-dialog title="图标选择器" v-model="dialogVisible" :width="760" destroy-on-close append-to-body>
|
||||
<div class="sc-icon-select__dialog" style="margin: -20px 0 -10px 0;">
|
||||
<el-form :rules="{}">
|
||||
<el-form-item prop="searchText">
|
||||
<el-input class="sc-icon-select__search-input" prefix-icon="el-icon-search" v-model="searchText" placeholder="搜索" clearable/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs>
|
||||
<el-tab-pane v-for="item in data" :key="item.name" lazy>
|
||||
<template #label>
|
||||
{{item.name}} <el-tag type="info">{{item.icons.length}}</el-tag>
|
||||
</template>
|
||||
<div class="sc-icon-select__list">
|
||||
<el-scrollbar>
|
||||
<ul @click="selectIcon">
|
||||
<el-empty v-if="item.icons.length==0" :image-size="100" description="未查询到相关图标" />
|
||||
<li v-for="icon in item.icons" :key="icon">
|
||||
<span :data-icon="icon"></span>
|
||||
<el-icon><component :is="icon" /></el-icon>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="clear" text>清除</el-button>
|
||||
<el-button @click="dialogVisible=false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/iconSelect"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: String, default: "" },
|
||||
disabled: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: "",
|
||||
dialogVisible: false,
|
||||
data: [],
|
||||
searchText: ""
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
this.value = val
|
||||
},
|
||||
value(val){
|
||||
this.$emit('update:modelValue', val)
|
||||
},
|
||||
searchText(val){
|
||||
this.search(val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.value = this.modelValue
|
||||
this.data.push(...config.icons)
|
||||
},
|
||||
methods: {
|
||||
open(){
|
||||
if(this.disabled){
|
||||
return false
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
selectIcon(e){
|
||||
if(e.target.tagName != 'SPAN'){
|
||||
return false
|
||||
}
|
||||
this.value = e.target.dataset.icon
|
||||
this.dialogVisible = false
|
||||
},
|
||||
clear(){
|
||||
this.value = ""
|
||||
this.dialogVisible = false
|
||||
},
|
||||
search(text){
|
||||
if(text){
|
||||
const filterData = JSON.parse(JSON.stringify(config.icons))
|
||||
filterData.forEach(t => {
|
||||
t.icons = t.icons.filter(n => n.includes(text))
|
||||
})
|
||||
this.data = filterData
|
||||
}else{
|
||||
this.data = JSON.parse(JSON.stringify(config.icons))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-icon-select {display: inline-flex;}
|
||||
.sc-icon-select__wrapper {cursor: pointer;display: inline-flex;}
|
||||
.sc-icon-select__wrapper:deep(.el-input__wrapper).is-focus {box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset;}
|
||||
.sc-icon-select__wrapper:deep(.el-input__inner) {flex-grow:0;width: 0;}
|
||||
.sc-icon-select__wrapper:deep(.el-input__icon) {margin: 0;font-size: 16px;}
|
||||
.sc-icon-select__wrapper.hasValue:deep(.el-input__icon) {color: var(--el-text-color-regular);}
|
||||
|
||||
.sc-icon-select__list {height:270px;overflow: auto;}
|
||||
.sc-icon-select__list ul {}
|
||||
.sc-icon-select__list li {display: inline-block;width:80px;height:80px;margin:5px;vertical-align: top;transition: all 0.1s;border-radius: 4px;position: relative;}
|
||||
.sc-icon-select__list li span {position: absolute;top:0;left:0;right:0;bottom:0;z-index: 1;cursor: pointer;}
|
||||
.sc-icon-select__list li i {display: inline-block;width: 100%;height:100%;font-size: 26px;color: #6d7882;display: flex;justify-content: center;align-items: center;border-radius: 4px;}
|
||||
.sc-icon-select__list li:hover {box-shadow: 0 0 1px 4px var(--el-color-primary);background: var(--el-color-primary-light-9);}
|
||||
.sc-icon-select__list li:hover i {color: var(--el-color-primary);}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-bind="$attrs" v-model="defaultValue" placeholder="请输入关键字" style="width: 100%;" readonly @foucs="showMap" @click="showMap" />
|
||||
<el-dialog title="地图" v-model="visible" width="800px">
|
||||
<template #default="scope">
|
||||
<b-map ref="map" width="100%" height="500px" ak="Mnx3XVAmyovK9wohstKt94nRfDpTy8qd" :center="point.lng ? point : '南昌'" :zoom="zoom" :minZoom="3" :mapType="'BMAP_NORMAL_MAP'" :enableDragging="true" :enableScrollWheelZoom="true" @dblclick="mapClick">
|
||||
<BCityList :offset="{ x: 20, y: 20 }" />
|
||||
<b-zoom :offset="{ x: 22, y: 40 }" />
|
||||
|
||||
<template v-if="point.lng != ''">
|
||||
<BMarker :position="point"></BMarker>
|
||||
<BCircle strokeStyle="solid" strokeColor="#0099ff" :strokeOpacity="0.8" fillColor="#0099ff" :fillOpacity="0.5" :center="point" />
|
||||
</template>
|
||||
</b-map>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BMap, BZoom, BCityList, BMarker, BCircle } from 'vue3-baidu-map-gl'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
point: {lng: '', lat: ''},
|
||||
zoom: 12,
|
||||
defaultValue: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.defaultValue = this.modelValue
|
||||
},
|
||||
watch: {
|
||||
modelValue:{
|
||||
handler(val) {
|
||||
this.defaultValue = val
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BMap,
|
||||
BZoom,
|
||||
BCityList,
|
||||
BMarker,
|
||||
BCircle
|
||||
},
|
||||
methods: {
|
||||
showMap(){
|
||||
this.visible = true
|
||||
if(this.defaultValue) {
|
||||
let arr = this.defaultValue.split(',')
|
||||
this.point = {
|
||||
lng: arr[1],
|
||||
lat: arr[0]
|
||||
}
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.zoom = 20
|
||||
this.$refs.map.resetCenter()
|
||||
})
|
||||
},
|
||||
mapClick(e) {
|
||||
this.$emit('update:modelValue', e.latlng.lat+','+e.latlng.lng)
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
* @Descripttion: 状态指示器
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年11月11日09:30:12
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<span class="sc-state" :class="[{'sc-status-processing':pulse}, 'sc-state-bg--'+type]"></span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
type: { type: String, default: "primary" },
|
||||
pulse: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-state {display: inline-block;background: #000;width: 8px;height: 8px;border-radius: 50%;vertical-align: middle;}
|
||||
.sc-status-processing {position: relative;}
|
||||
.sc-status-processing:after {position: absolute;top:0px;left:0px;width: 100%;height: 100%;border-radius: 50%;background: inherit;content: '';animation: warn 1.2s ease-in-out infinite;}
|
||||
|
||||
.sc-state-bg--primary {background: var(--el-color-primary);}
|
||||
.sc-state-bg--success {background: var(--el-color-success);}
|
||||
.sc-state-bg--warning {background: var(--el-color-warning);}
|
||||
.sc-state-bg--danger {background: var(--el-color-danger);}
|
||||
.sc-state-bg--info {background: var(--el-color-info);}
|
||||
|
||||
@keyframes warn {
|
||||
0% {
|
||||
transform: scale(0.5);
|
||||
opacity: 1;
|
||||
}
|
||||
30% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!--
|
||||
* @Descripttion: 趋势标记
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年11月11日11:07:10
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<span class="sc-trend" :class="'sc-trend--'+type">
|
||||
<el-icon v-if="iconType=='P'" class="sc-trend-icon"><el-icon-top /></el-icon>
|
||||
<el-icon v-if="iconType=='N'" class="sc-trend-icon"><el-icon-bottom /></el-icon>
|
||||
<el-icon v-if="iconType=='Z'" class="sc-trend-icon"><el-icon-right /></el-icon>
|
||||
<em class="sc-trend-prefix">{{prefix}}</em>
|
||||
<em class="sc-trend-value">{{modelValue}}</em>
|
||||
<em class="sc-trend-suffix">{{suffix}}</em>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Number, default: 0 },
|
||||
prefix: { type: String, default: "" },
|
||||
suffix: { type: String, default: "" },
|
||||
reverse: { type: Boolean, default: false }
|
||||
},
|
||||
computed: {
|
||||
absValue(){
|
||||
return Math.abs(this.modelValue);
|
||||
},
|
||||
iconType(v){
|
||||
if(this.modelValue == 0){
|
||||
v = 'Z'
|
||||
}else if(this.modelValue < 0){
|
||||
v = 'N'
|
||||
}else if(this.modelValue > 0){
|
||||
v = 'P'
|
||||
}
|
||||
return v
|
||||
},
|
||||
type(v){
|
||||
if(this.modelValue == 0){
|
||||
v = 'Z'
|
||||
}else if(this.modelValue < 0){
|
||||
v = this.reverse?'P':'N'
|
||||
}else if(this.modelValue > 0){
|
||||
v = this.reverse?'N':'P'
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-trend {display: flex;align-items: center;}
|
||||
.sc-trend-icon {margin-right: 2px;}
|
||||
.sc-trend em {font-style: normal;}
|
||||
.sc-trend-prefix {margin-right: 2px;}
|
||||
.sc-trend-suffix {margin-left: 2px;}
|
||||
.sc-trend--P {color: #f56c6c;}
|
||||
.sc-trend--N {color: #67c23a;}
|
||||
.sc-trend--Z {color: #555;}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--
|
||||
* @Descripttion: 页面头部样式组件
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年7月20日08:49:07
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-page-header">
|
||||
<div v-if="icon" class="sc-page-header__icon">
|
||||
<span>
|
||||
<el-icon><component :is="icon" /></el-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sc-page-header__title">
|
||||
<h2>{{ title }}</h2>
|
||||
<p v-if="description || $slots.default">
|
||||
<slot>
|
||||
{{ description }}
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="$slots.main" class="sc-page-header__main">
|
||||
<slot name="main"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: { type: String, required: true, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
icon: { type: String, default: "" },
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-page-header {background: #fff;border-bottom: 1px solid #e6e6e6;padding:20px 25px;display: flex;}
|
||||
.sc-page-header__icon {width: 50px;}
|
||||
.sc-page-header__icon span {display: inline-block;width: 30px;height: 30px;background: #409EFF;border-radius: 40%;display: flex;align-items: center;justify-content: center;}
|
||||
.sc-page-header__icon span i {color: #fff;font-size: 14px;}
|
||||
.sc-page-header__title {flex: 1;}
|
||||
.sc-page-header__title h2 {font-size: 17px;color: #3c4a54;font-weight: bold;margin-top: 3px;}
|
||||
.sc-page-header__title p {font-size: 13px;color: #999;margin-top: 15px;}
|
||||
|
||||
[data-theme='dark'] .sc-page-header {background:#2b2b2b ;border-color:var(--el-border-color-base);}
|
||||
[data-theme='dark'] .sc-page-header__title h2 {color: #d0d0d0;}
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<!--
|
||||
* @Descripttion: 密码强度检测
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2022年6月2日15:36:01
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-password-strength">
|
||||
<div class="sc-password-strength-bar" :class="`sc-password-strength-level-${level}`"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: String, default: "" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
level: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.strength(this.modelValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.strength(this.modelValue)
|
||||
},
|
||||
methods: {
|
||||
strength(v){
|
||||
var _level = 0
|
||||
//长度
|
||||
var has_length = v.length >= 6
|
||||
//包含数字
|
||||
var has_number = /\d/.test(v)
|
||||
//包含小写英文
|
||||
var has_lovercase = /[a-z]/.test(v)
|
||||
//包含大写英文
|
||||
var has_uppercase = /[A-Z]/.test(v)
|
||||
//没有连续的字符3位
|
||||
var no_continuity = !/(\w)\1{2}/.test(v)
|
||||
//包含特殊字符
|
||||
var has_special = /[`~!@#$%^&*()_+<>?:"{},./;'[\]]/.test(v)
|
||||
|
||||
if(v.length <= 0){
|
||||
_level = 0
|
||||
this.level = _level
|
||||
return false
|
||||
}
|
||||
if(!has_length){
|
||||
_level = 1
|
||||
this.level = _level
|
||||
return false
|
||||
}
|
||||
if(has_number){
|
||||
_level += 1
|
||||
}
|
||||
if(has_lovercase){
|
||||
_level += 1
|
||||
}
|
||||
if(has_uppercase){
|
||||
_level += 1
|
||||
}
|
||||
if(no_continuity){
|
||||
_level += 1
|
||||
}
|
||||
if(has_special){
|
||||
_level += 1
|
||||
}
|
||||
this.level = _level
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-password-strength {height: 5px;width: 100%;background: var(--el-color-info-light-5);border-radius: 5px;position: relative;margin:10px 0;}
|
||||
.sc-password-strength:before {left: 20%;}
|
||||
.sc-password-strength:after {right: 20%;}
|
||||
.sc-password-strength:before, .sc-password-strength:after {position: absolute;content: "";display: block;width: 20%;height: inherit;border: 5px solid var(--el-bg-color-overlay);border-top: 0;border-bottom: 0;z-index: 1;background-color: transparent;box-sizing: border-box;}
|
||||
.sc-password-strength-bar {position: absolute;height: inherit;width: 0%;border-radius: inherit;transition: width .5s ease-in-out,background .25s;background: transparent;}
|
||||
.sc-password-strength-level-1 {width: 20%;background-color: var(--el-color-error);}
|
||||
.sc-password-strength-level-2 {width: 40%;background-color: var(--el-color-error);}
|
||||
.sc-password-strength-level-3 {width: 60%;background-color: var(--el-color-warning);}
|
||||
.sc-password-strength-level-4 {width: 80%;background-color: var(--el-color-success);}
|
||||
.sc-password-strength-level-5 {width: 100%;background-color: var(--el-color-success);}
|
||||
</style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<!--
|
||||
* @Descripttion: 生成二维码组件
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年12月20日14:22:20
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<img ref="img"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QRcode from "./qrcode"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
text: { type: String, required: true, default: "" },
|
||||
size: { type: Number, default: 100 },
|
||||
logo: { type: String, default: "" },
|
||||
logoSize: { type: Number, default: 30 },
|
||||
logoPadding: { type: Number, default: 5 },
|
||||
colorDark: { type: String, default: "#000000" },
|
||||
colorLight: { type: String, default: "#ffffff" },
|
||||
correctLevel: { type: Number, default: 2 },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: null
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
text(){
|
||||
this.draw()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.draw()
|
||||
},
|
||||
methods: {
|
||||
//创建原始二维码DOM
|
||||
async create(){
|
||||
return new Promise((resolve) => {
|
||||
var element = document.createElement("div");
|
||||
new QRcode(element, {
|
||||
text: this.text,
|
||||
width: this.size,
|
||||
height: this.size,
|
||||
colorDark: this.colorDark,
|
||||
colorLight: this.colorLight,
|
||||
correctLevel: this.correctLevel
|
||||
})
|
||||
if (element.getElementsByTagName("canvas")[0]) {
|
||||
this.qrcode = element
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
},
|
||||
//绘制LOGO
|
||||
async drawLogo(){
|
||||
return new Promise((resolve) => {
|
||||
var logo = new Image()
|
||||
logo.src = this.logo
|
||||
const logoPos = (this.size - this.logoSize) / 2
|
||||
const rectSize = this.logoSize + this.logoPadding
|
||||
const rectPos = (this.size - rectSize) / 2
|
||||
var ctx = this.qrcode.getElementsByTagName("canvas")[0].getContext("2d")
|
||||
logo.onload = ()=>{
|
||||
ctx.fillRect(rectPos, rectPos, rectSize, rectSize)
|
||||
ctx.drawImage(logo, logoPos, logoPos, this.logoSize, this.logoSize)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
},
|
||||
async draw(){
|
||||
await this.create()
|
||||
if(this.logo){
|
||||
await this.drawLogo()
|
||||
}
|
||||
this.$refs.img.src = this.qrcode.getElementsByTagName("canvas")[0].toDataURL("image/png")
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,618 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* - Using the 'QRCode for Javascript library'
|
||||
* - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
|
||||
* - this library has no dependencies.
|
||||
* - source page: https://github.com/makevoid/qrcodejs
|
||||
*
|
||||
*
|
||||
* @author davidshimjs
|
||||
* @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
|
||||
* @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
|
||||
*/
|
||||
var QRCode;
|
||||
|
||||
(function () {
|
||||
//---------------------------------------------------------------------
|
||||
// QRCode for JavaScript
|
||||
//
|
||||
// Copyright (c) 2009 Kazuhiko Arase
|
||||
//
|
||||
// URL: http://www.d-project.com/
|
||||
//
|
||||
// Licensed under the MIT license:
|
||||
// http://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
// The word "QR Code" is registered trademark of
|
||||
// DENSO WAVE INCORPORATED
|
||||
// http://www.denso-wave.com/qrcode/faqpatent-e.html
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
function QR8bitByte(data) {
|
||||
this.mode = QRMode.MODE_8BIT_BYTE;
|
||||
this.data = data;
|
||||
this.parsedData = [];
|
||||
|
||||
// Added to support UTF-8 Characters
|
||||
for (var i = 0, l = this.data.length; i < l; i++) {
|
||||
var byteArray = [];
|
||||
var code = this.data.charCodeAt(i);
|
||||
|
||||
if (code > 0x10000) {
|
||||
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
|
||||
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
|
||||
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
|
||||
byteArray[3] = 0x80 | (code & 0x3F);
|
||||
} else if (code > 0x800) {
|
||||
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
|
||||
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
|
||||
byteArray[2] = 0x80 | (code & 0x3F);
|
||||
} else if (code > 0x80) {
|
||||
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
|
||||
byteArray[1] = 0x80 | (code & 0x3F);
|
||||
} else {
|
||||
byteArray[0] = code;
|
||||
}
|
||||
|
||||
this.parsedData.push(byteArray);
|
||||
}
|
||||
|
||||
this.parsedData = Array.prototype.concat.apply([], this.parsedData);
|
||||
|
||||
if (this.parsedData.length != this.data.length) {
|
||||
this.parsedData.unshift(191);
|
||||
this.parsedData.unshift(187);
|
||||
this.parsedData.unshift(239);
|
||||
}
|
||||
}
|
||||
|
||||
QR8bitByte.prototype = {
|
||||
getLength: function (buffer) {
|
||||
return this.parsedData.length;
|
||||
},
|
||||
write: function (buffer) {
|
||||
for (var i = 0, l = this.parsedData.length; i < l; i++) {
|
||||
buffer.put(this.parsedData[i], 8);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function QRCodeModel(typeNumber, errorCorrectLevel) {
|
||||
this.typeNumber = typeNumber;
|
||||
this.errorCorrectLevel = errorCorrectLevel;
|
||||
this.modules = null;
|
||||
this.moduleCount = 0;
|
||||
this.dataCache = null;
|
||||
this.dataList = [];
|
||||
}
|
||||
|
||||
QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
|
||||
return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
|
||||
this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
|
||||
if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
|
||||
this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
|
||||
return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
|
||||
return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
|
||||
this.modules[r][6]=(r%2==0);}
|
||||
for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
|
||||
this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
|
||||
for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
|
||||
for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
|
||||
for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
|
||||
this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
|
||||
var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
|
||||
this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
|
||||
row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
|
||||
var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
|
||||
if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
|
||||
+buffer.getLengthInBits()
|
||||
+">"
|
||||
+totalDataCount*8
|
||||
+")");}
|
||||
if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
|
||||
while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
|
||||
while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
|
||||
buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
|
||||
buffer.put(QRCodeModel.PAD1,8);}
|
||||
return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
|
||||
offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
|
||||
var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
|
||||
var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
|
||||
for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
|
||||
return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
|
||||
return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
|
||||
return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
|
||||
return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
|
||||
return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
|
||||
for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
|
||||
if(r==0&&c==0){continue;}
|
||||
if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
|
||||
if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
|
||||
for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
|
||||
for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
|
||||
for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
|
||||
var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
|
||||
var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
|
||||
return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
|
||||
while(n>=256){n-=255;}
|
||||
return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
|
||||
for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
|
||||
for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
|
||||
function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
|
||||
var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
|
||||
this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
|
||||
QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
|
||||
return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
|
||||
var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
|
||||
for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
|
||||
return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
|
||||
QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,errorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
|
||||
var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
|
||||
return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
|
||||
QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
|
||||
if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
|
||||
this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
|
||||
|
||||
function _isSupportCanvas() {
|
||||
return typeof CanvasRenderingContext2D != "undefined";
|
||||
}
|
||||
|
||||
// android 2.x doesn't support Data-URI spec
|
||||
function _getAndroid() {
|
||||
var android = false;
|
||||
var sAgent = navigator.userAgent;
|
||||
|
||||
if (/android/i.test(sAgent)) { // android
|
||||
android = true;
|
||||
var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i);
|
||||
|
||||
if (aMat && aMat[1]) {
|
||||
android = parseFloat(aMat[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return android;
|
||||
}
|
||||
|
||||
var svgDrawer = (function() {
|
||||
|
||||
var Drawing = function (el, htOption) {
|
||||
this._el = el;
|
||||
this._htOption = htOption;
|
||||
};
|
||||
|
||||
Drawing.prototype.draw = function (oQRCode) {
|
||||
var _htOption = this._htOption;
|
||||
var _el = this._el;
|
||||
var nCount = oQRCode.getModuleCount();
|
||||
var nWidth = Math.floor(_htOption.width / nCount);
|
||||
var nHeight = Math.floor(_htOption.height / nCount);
|
||||
|
||||
this.clear();
|
||||
|
||||
function makeSVG(tag, attrs) {
|
||||
var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||
for (var k in attrs)
|
||||
if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
|
||||
return el;
|
||||
}
|
||||
|
||||
var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight});
|
||||
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
_el.appendChild(svg);
|
||||
|
||||
svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"}));
|
||||
svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"}));
|
||||
|
||||
for (var row = 0; row < nCount; row++) {
|
||||
for (var col = 0; col < nCount; col++) {
|
||||
if (oQRCode.isDark(row, col)) {
|
||||
var child = makeSVG("use", {"x": String(col), "y": String(row)});
|
||||
child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template")
|
||||
svg.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Drawing.prototype.clear = function () {
|
||||
while (this._el.hasChildNodes())
|
||||
this._el.removeChild(this._el.lastChild);
|
||||
};
|
||||
return Drawing;
|
||||
})();
|
||||
|
||||
var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
|
||||
|
||||
// Drawing in DOM by using Table tag
|
||||
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
|
||||
var Drawing = function (el, htOption) {
|
||||
this._el = el;
|
||||
this._htOption = htOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the QRCode
|
||||
*
|
||||
* @param {QRCode} oQRCode
|
||||
*/
|
||||
Drawing.prototype.draw = function (oQRCode) {
|
||||
var _htOption = this._htOption;
|
||||
var _el = this._el;
|
||||
var nCount = oQRCode.getModuleCount();
|
||||
var nWidth = Math.floor(_htOption.width / nCount);
|
||||
var nHeight = Math.floor(_htOption.height / nCount);
|
||||
var aHTML = ['<table style="border:0;border-collapse:collapse;">'];
|
||||
|
||||
for (var row = 0; row < nCount; row++) {
|
||||
aHTML.push('<tr>');
|
||||
|
||||
for (var col = 0; col < nCount; col++) {
|
||||
aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
|
||||
}
|
||||
|
||||
aHTML.push('</tr>');
|
||||
}
|
||||
|
||||
aHTML.push('</table>');
|
||||
_el.innerHTML = aHTML.join('');
|
||||
|
||||
// Fix the margin values as real size.
|
||||
var elTable = _el.childNodes[0];
|
||||
var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
|
||||
var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
|
||||
|
||||
if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
|
||||
elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the QRCode
|
||||
*/
|
||||
Drawing.prototype.clear = function () {
|
||||
this._el.innerHTML = '';
|
||||
};
|
||||
|
||||
return Drawing;
|
||||
})() : (function () { // Drawing in Canvas
|
||||
function _onMakeImage() {
|
||||
this._elImage.src = this._elCanvas.toDataURL("image/png");
|
||||
this._elImage.style.display = "block";
|
||||
this._elCanvas.style.display = "none";
|
||||
}
|
||||
|
||||
// Android 2.1 bug workaround
|
||||
// http://code.google.com/p/android/issues/detail?id=5141
|
||||
if (this && this._android && this._android <= 2.1) {
|
||||
var factor = 1 / window.devicePixelRatio;
|
||||
var drawImage = CanvasRenderingContext2D.prototype.drawImage;
|
||||
CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
||||
if (("nodeName" in image) && /img/i.test(image.nodeName)) {
|
||||
for (var i = arguments.length - 1; i >= 1; i--) {
|
||||
arguments[i] = arguments[i] * factor;
|
||||
}
|
||||
} else if (typeof dw == "undefined") {
|
||||
arguments[1] *= factor;
|
||||
arguments[2] *= factor;
|
||||
arguments[3] *= factor;
|
||||
arguments[4] *= factor;
|
||||
}
|
||||
|
||||
drawImage.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user's browser supports Data URI or not
|
||||
*
|
||||
* @private
|
||||
* @param {Function} fSuccess Occurs if it supports Data URI
|
||||
* @param {Function} fFail Occurs if it doesn't support Data URI
|
||||
*/
|
||||
function _safeSetDataURI(fSuccess, fFail) {
|
||||
var self = this;
|
||||
self._fFail = fFail;
|
||||
self._fSuccess = fSuccess;
|
||||
|
||||
// Check it just once
|
||||
if (self._bSupportDataURI === null) {
|
||||
var el = document.createElement("img");
|
||||
var fOnError = function() {
|
||||
self._bSupportDataURI = false;
|
||||
|
||||
if (self._fFail) {
|
||||
self._fFail.call(self);
|
||||
}
|
||||
};
|
||||
var fOnSuccess = function() {
|
||||
self._bSupportDataURI = true;
|
||||
|
||||
if (self._fSuccess) {
|
||||
self._fSuccess.call(self);
|
||||
}
|
||||
};
|
||||
|
||||
el.onabort = fOnError;
|
||||
el.onerror = fOnError;
|
||||
el.onload = fOnSuccess;
|
||||
el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
|
||||
return;
|
||||
} else if (self._bSupportDataURI === true && self._fSuccess) {
|
||||
self._fSuccess.call(self);
|
||||
} else if (self._bSupportDataURI === false && self._fFail) {
|
||||
self._fFail.call(self);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawing QRCode by using canvas
|
||||
*
|
||||
* @constructor
|
||||
* @param {HTMLElement} el
|
||||
* @param {Object} htOption QRCode Options
|
||||
*/
|
||||
var Drawing = function (el, htOption) {
|
||||
this._bIsPainted = false;
|
||||
this._android = _getAndroid();
|
||||
|
||||
this._htOption = htOption;
|
||||
this._elCanvas = document.createElement("canvas");
|
||||
this._elCanvas.width = htOption.width;
|
||||
this._elCanvas.height = htOption.height;
|
||||
el.appendChild(this._elCanvas);
|
||||
this._el = el;
|
||||
this._oContext = this._elCanvas.getContext("2d");
|
||||
this._bIsPainted = false;
|
||||
this._elImage = document.createElement("img");
|
||||
this._elImage.alt = "Scan me!";
|
||||
this._elImage.style.display = "none";
|
||||
this._el.appendChild(this._elImage);
|
||||
this._bSupportDataURI = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the QRCode
|
||||
*
|
||||
* @param {QRCode} oQRCode
|
||||
*/
|
||||
Drawing.prototype.draw = function (oQRCode) {
|
||||
var _elImage = this._elImage;
|
||||
var _oContext = this._oContext;
|
||||
var _htOption = this._htOption;
|
||||
|
||||
var nCount = oQRCode.getModuleCount();
|
||||
var nWidth = _htOption.width / nCount;
|
||||
var nHeight = _htOption.height / nCount;
|
||||
var nRoundedWidth = Math.round(nWidth);
|
||||
var nRoundedHeight = Math.round(nHeight);
|
||||
|
||||
_elImage.style.display = "none";
|
||||
this.clear();
|
||||
|
||||
for (var row = 0; row < nCount; row++) {
|
||||
for (var col = 0; col < nCount; col++) {
|
||||
var bIsDark = oQRCode.isDark(row, col);
|
||||
var nLeft = col * nWidth;
|
||||
var nTop = row * nHeight;
|
||||
_oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
|
||||
_oContext.lineWidth = 1;
|
||||
_oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
|
||||
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
|
||||
|
||||
// 안티 앨리어싱 방지 처리
|
||||
_oContext.strokeRect(
|
||||
Math.floor(nLeft) + 0.5,
|
||||
Math.floor(nTop) + 0.5,
|
||||
nRoundedWidth,
|
||||
nRoundedHeight
|
||||
);
|
||||
|
||||
_oContext.strokeRect(
|
||||
Math.ceil(nLeft) - 0.5,
|
||||
Math.ceil(nTop) - 0.5,
|
||||
nRoundedWidth,
|
||||
nRoundedHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this._bIsPainted = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the image from Canvas if the browser supports Data URI.
|
||||
*/
|
||||
Drawing.prototype.makeImage = function () {
|
||||
if (this._bIsPainted) {
|
||||
_safeSetDataURI.call(this, _onMakeImage);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return whether the QRCode is painted or not
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Drawing.prototype.isPainted = function () {
|
||||
return this._bIsPainted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the QRCode
|
||||
*/
|
||||
Drawing.prototype.clear = function () {
|
||||
this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
|
||||
this._bIsPainted = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Number} nNumber
|
||||
*/
|
||||
Drawing.prototype.round = function (nNumber) {
|
||||
if (!nNumber) {
|
||||
return nNumber;
|
||||
}
|
||||
|
||||
return Math.floor(nNumber * 1000) / 1000;
|
||||
};
|
||||
|
||||
return Drawing;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Get the type by string length
|
||||
*
|
||||
* @private
|
||||
* @param {String} sText
|
||||
* @param {Number} nCorrectLevel
|
||||
* @return {Number} type
|
||||
*/
|
||||
function _getTypeNumber(sText, nCorrectLevel) {
|
||||
var nType = 1;
|
||||
var length = _getUTF8Length(sText);
|
||||
|
||||
for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
|
||||
var nLimit = 0;
|
||||
|
||||
switch (nCorrectLevel) {
|
||||
case QRErrorCorrectLevel.L :
|
||||
nLimit = QRCodeLimitLength[i][0];
|
||||
break;
|
||||
case QRErrorCorrectLevel.M :
|
||||
nLimit = QRCodeLimitLength[i][1];
|
||||
break;
|
||||
case QRErrorCorrectLevel.Q :
|
||||
nLimit = QRCodeLimitLength[i][2];
|
||||
break;
|
||||
case QRErrorCorrectLevel.H :
|
||||
nLimit = QRCodeLimitLength[i][3];
|
||||
break;
|
||||
}
|
||||
|
||||
if (length <= nLimit) {
|
||||
break;
|
||||
} else {
|
||||
nType++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nType > QRCodeLimitLength.length) {
|
||||
throw new Error("Too long data");
|
||||
}
|
||||
|
||||
return nType;
|
||||
}
|
||||
|
||||
function _getUTF8Length(sText) {
|
||||
var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
|
||||
return replacedText.length + (replacedText.length != sText ? 3 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class QRCode
|
||||
* @constructor
|
||||
* @example
|
||||
* new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
|
||||
*
|
||||
* @example
|
||||
* var oQRCode = new QRCode("test", {
|
||||
* text : "http://naver.com",
|
||||
* width : 128,
|
||||
* height : 128
|
||||
* });
|
||||
*
|
||||
* oQRCode.clear(); // Clear the QRCode.
|
||||
* oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
|
||||
*
|
||||
* @param {HTMLElement|String} el target element or 'id' attribute of element.
|
||||
* @param {Object|String} vOption
|
||||
* @param {String} vOption.text QRCode link data
|
||||
* @param {Number} [vOption.width=256]
|
||||
* @param {Number} [vOption.height=256]
|
||||
* @param {String} [vOption.colorDark="#000000"]
|
||||
* @param {String} [vOption.colorLight="#ffffff"]
|
||||
* @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
|
||||
*/
|
||||
QRCode = function (el, vOption) {
|
||||
this._htOption = {
|
||||
width : 256,
|
||||
height : 256,
|
||||
typeNumber : 4,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
correctLevel : QRErrorCorrectLevel.H
|
||||
};
|
||||
|
||||
if (typeof vOption === 'string') {
|
||||
vOption = {
|
||||
text : vOption
|
||||
};
|
||||
}
|
||||
|
||||
// Overwrites options
|
||||
if (vOption) {
|
||||
for (var i in vOption) {
|
||||
this._htOption[i] = vOption[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof el == "string") {
|
||||
el = document.getElementById(el);
|
||||
}
|
||||
|
||||
if (this._htOption.useSVG) {
|
||||
Drawing = svgDrawer;
|
||||
}
|
||||
|
||||
this._android = _getAndroid();
|
||||
this._el = el;
|
||||
this._oQRCode = null;
|
||||
this._oDrawing = new Drawing(this._el, this._htOption);
|
||||
|
||||
if (this._htOption.text) {
|
||||
this.makeCode(this._htOption.text);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the QRCode
|
||||
*
|
||||
* @param {String} sText link data
|
||||
*/
|
||||
QRCode.prototype.makeCode = function (sText) {
|
||||
this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
|
||||
this._oQRCode.addData(sText);
|
||||
this._oQRCode.make();
|
||||
this._el.title = sText;
|
||||
this._oDrawing.draw(this._oQRCode);
|
||||
this.makeImage();
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the Image from Canvas element
|
||||
* - It occurs automatically
|
||||
* - Android below 3 doesn't support Data-URI spec.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
QRCode.prototype.makeImage = function () {
|
||||
if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) {
|
||||
this._oDrawing.makeImage();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the QRCode
|
||||
*/
|
||||
QRCode.prototype.clear = function () {
|
||||
this._oDrawing.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* @name QRCode.CorrectLevel
|
||||
*/
|
||||
QRCode.CorrectLevel = QRErrorCorrectLevel;
|
||||
})();
|
||||
|
||||
export default QRCode;
|
||||
@@ -0,0 +1,94 @@
|
||||
<!--
|
||||
* @Descripttion: 异步选择器
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年8月3日15:53:37
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2023年2月23日15:17:24
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-select">
|
||||
<div v-if="initloading" class="sc-select-loading">
|
||||
<el-icon class="is-loading"><el-icon-loading /></el-icon>
|
||||
</div>
|
||||
<el-select v-bind="$attrs" :loading="loading" @visible-change="visibleChange">
|
||||
<el-option v-for="item in options" :key="item[props.value]" :label="item[props.label]" :value="objValueType ? item : item[props.value]">
|
||||
<slot name="option" :data="item"></slot>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/select";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
dic: { type: String, default: "" },
|
||||
objValueType: { type: Boolean, default: false },
|
||||
params: { type: Object, default: () => ({}) },
|
||||
props: { type: Object, default: () => ({
|
||||
label: config.props.label,
|
||||
value: config.props.value
|
||||
}) }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dicParams: this.params,
|
||||
loading: false,
|
||||
options: [],
|
||||
initloading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
//如果有默认值就去请求接口获取options
|
||||
if(this.hasValue()){
|
||||
this.initloading = true
|
||||
this.getRemoteData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//选项显示隐藏事件
|
||||
visibleChange(ispoen){
|
||||
if(ispoen && this.options.length==0 && (this.dic || this.apiObj)){
|
||||
this.getRemoteData()
|
||||
}
|
||||
},
|
||||
//获取数据
|
||||
async getRemoteData(){
|
||||
this.loading = true
|
||||
this.dicParams[config.request.name] = this.dic
|
||||
var res = {}
|
||||
if(this.apiObj){
|
||||
res = await this.apiObj.get(this.params)
|
||||
}else if(this.dic){
|
||||
res = await config.dicApiObj.get(this.params)
|
||||
}
|
||||
var response = config.parseData(res)
|
||||
this.options = response.data
|
||||
this.loading = false
|
||||
this.initloading = false
|
||||
},
|
||||
//判断是否有回显默认值
|
||||
hasValue(){
|
||||
if(Array.isArray(this.$attrs.modelValue) && this.$attrs.modelValue.length <= 0){
|
||||
return false
|
||||
}else if(this.$attrs.modelValue){
|
||||
return true
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-select {display: inline-block;position: relative;}
|
||||
.sc-select-loading {position: absolute;top:0;left:0;right:0;bottom:0;background: #fff;z-index: 100;border-radius: 5px;border: 1px solid #EBEEF5;display: flex;align-items: center;padding-left:10px;}
|
||||
.sc-select-loading i {font-size: 14px;}
|
||||
|
||||
.dark .sc-select-loading {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<!--
|
||||
* @Descripttion: 分类筛选器
|
||||
* @version: 1.0
|
||||
* @Author: sakuya
|
||||
* @Date: 2022年5月26日15:59:52
|
||||
* @LastEditors:
|
||||
* @LastEditTime:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-select-filter">
|
||||
<div v-if="data.length<=0" class="sc-select-filter__no-data">
|
||||
暂无数据
|
||||
</div>
|
||||
<div v-for="item in data" :key="item.key" class="sc-select-filter__item">
|
||||
<div class="sc-select-filter__item-title" :style="{'width':labelWidth+'px'}"><label>{{item.title}}:</label></div>
|
||||
<div class="sc-select-filter__item-options">
|
||||
<ul>
|
||||
<li :class="{'active':selected[item.key]&&selected[item.key].includes(option.value)}" v-for="option in item.options" :key="option.value" @click="select(option, item)">
|
||||
<el-icon v-if="option.icon"><component :is="option.icon" /></el-icon>
|
||||
<span>{{option.label}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: { type: Array, default: () => [] },
|
||||
selectedValues: { type: Object, default: () => { return {} } },
|
||||
labelWidth: {type: Number, default: 80},
|
||||
outputValueTypeToArray: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: {}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
data(val) {
|
||||
val.forEach(item => {
|
||||
this.selected[item.key] = this.selectedValues[item.key] ||
|
||||
(Array.isArray(item.options) && item.options.length) ? [item.options[0].value] : []
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedString() {
|
||||
var outputData = JSON.parse(JSON.stringify(this.selected))
|
||||
for (var key in outputData) {
|
||||
outputData[key] = outputData[key].join(",")
|
||||
}
|
||||
return outputData
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
//默认赋值
|
||||
this.data.forEach(item => {
|
||||
this.selected[item.key] = this.selectedValues[item.key] ||
|
||||
(Array.isArray(item.options) && item.options.length) ? [item.options[0].value] : []
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
select(option, item){
|
||||
//判断单选多选
|
||||
if(item.multiple){
|
||||
//如果多选选择的第一个
|
||||
if(option.value === item.options[0].value){
|
||||
//就赋值第一个的值
|
||||
this.selected[item.key] = [option.value]
|
||||
}else{
|
||||
//如果选择的值已有
|
||||
if(this.selected[item.key].includes(option.value)){
|
||||
//删除选择的值
|
||||
this.selected[item.key].splice(this.selected[item.key].findIndex(s => s === option.value), 1)
|
||||
//当全删光时,把第一个选中
|
||||
if(this.selected[item.key].length == 0){
|
||||
this.selected[item.key] = [item.options[0].value]
|
||||
}
|
||||
}else{
|
||||
//未有值的时候,追加选中值
|
||||
this.selected[item.key].push(option.value)
|
||||
//当含有第一个的值的时候,把第一个删除
|
||||
if(this.selected[item.key].includes(item.options[0].value)){
|
||||
this.selected[item.key].splice(this.selected[item.key].findIndex(s => s === item.options[0].value), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//单选时,如果点击了已有值就赋值
|
||||
if(!this.selected[item.key].includes(option.value)){
|
||||
this.selected[item.key] = [option.value]
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
this.change()
|
||||
},
|
||||
change(){
|
||||
if(this.outputValueTypeToArray){
|
||||
this.$emit('onChange', this.selected)
|
||||
}else{
|
||||
this.$emit('onChange', this.selectedString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-select-filter {width: 100%;}
|
||||
.sc-select-filter__item {display: flex;}
|
||||
.sc-select-filter__item-title {width: 80px;}
|
||||
.sc-select-filter__item-title label {font-size: 14px;padding-top:13px;display: inline-block;color: #999;}
|
||||
.sc-select-filter__item-options {flex: 1;border-bottom: 1px dashed var(--el-border-color-light);}
|
||||
.sc-select-filter__item-options ul {display: flex;flex-wrap: wrap;padding-top: 10px;}
|
||||
.sc-select-filter__item-options li {list-style: none;cursor: pointer;height:28px;padding:0 15px;border-radius:32px;margin: 0 10px 10px 0;display: flex;align-items: center;background: var(--el-color-primary-light-9);}
|
||||
.sc-select-filter__item-options li .el-icon {margin-right: 3px;font-size: 16px;}
|
||||
.sc-select-filter__item-options li:hover {color: var(--el-color-primary);}
|
||||
.sc-select-filter__item-options li.active {background: var(--el-color-primary);color: #fff;font-weight: bold;}
|
||||
.sc-select-filter__item:last-of-type .sc-select-filter__item-options {border: 0;}
|
||||
|
||||
.sc-select-filter__no-data {color: #999;}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<el-select v-bind="$attrs" remote-show-suffix clearable remote :remote-method="querySearchAsync" :loading="loading">
|
||||
<el-option v-for="item in options" :key="item[props.value]" :label="item[props.label]" :value="item[props.value]"/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default{
|
||||
props: {
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
props: { type: Object, default: () => {return {label: 'name', value: 'id'}} }
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
loading: false,
|
||||
options: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
//如果有默认值就去请求接口获取options
|
||||
if(this.hasValue()){
|
||||
this.initloading = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async querySearchAsync(queryString){
|
||||
let params = {}
|
||||
params[this.props.value] = queryString
|
||||
let res = await this.apiObj.get(params)
|
||||
if(res.code == 1){
|
||||
this.options = res.data
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
//判断是否有回显默认值
|
||||
hasValue(){
|
||||
if(Array.isArray(this.$attrs.modelValue) && this.$attrs.modelValue.length <= 0){
|
||||
return false
|
||||
}else if(this.$attrs.modelValue){
|
||||
this.querySearchAsync(this.$attrs.modelValue)
|
||||
return true
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tree-select v-bind="$attrs" :data="treeData" check-strictly :render-after-expand="false" :default-expand-all="true" :props="defaultProps" clearable style="min-width: 240px;" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from '@/config/select.js'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
props: { type: Object, default: () => {} },
|
||||
hasTop: { type: Boolean, default: false }
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
treeData: [],
|
||||
defaultProps: {label: config.props.label, value: config.props.value}
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.defaultProps = Object.assign(this.defaultProps, this.props);
|
||||
this.$nextTick(() => {
|
||||
this.getData()
|
||||
})
|
||||
},
|
||||
methods:{
|
||||
async getData(){
|
||||
let data = {}
|
||||
data[this.defaultProps.label] = '顶级'
|
||||
data[this.defaultProps.value] = 0
|
||||
let res = await this.apiObj.get({is_tree: 1})
|
||||
if(res.code == 1){
|
||||
this.treeData = this.hasTop ? [data, ...res.data] : res.data
|
||||
}else{
|
||||
this.treeData = this.hasTop ? [data] : []
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--
|
||||
* @Descripttion: 统计数值组件
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年6月23日13:11:32
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月14日19:55:09
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-statistic">
|
||||
<div class="sc-statistic-title">
|
||||
{{ title }}
|
||||
<el-tooltip v-if="tips" effect="light">
|
||||
<template #content>
|
||||
<div style="width: 200px;line-height: 2;">
|
||||
{{ tips }}
|
||||
</div>
|
||||
</template>
|
||||
<el-icon class="sc-statistic-tips"><el-icon-question-filled/></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="sc-statistic-content">
|
||||
<span v-if="prefix" class="sc-statistic-content-prefix">{{ prefix }}</span>
|
||||
<span class="sc-statistic-content-value">{{ cmtValue }}</span>
|
||||
<span v-if="suffix" class="sc-statistic-content-suffix">{{ suffix }}</span>
|
||||
</div>
|
||||
<div v-if="description || $slots.default" class="sc-statistic-description">
|
||||
<slot>
|
||||
{{ description }}
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: { type: String, required: true, default: "" },
|
||||
value: { type: String, required: true, default: "" },
|
||||
prefix: { type: String, default: "" },
|
||||
suffix: { type: String, default: "" },
|
||||
description: { type: String, default: "" },
|
||||
tips: { type: String, default: "" },
|
||||
groupSeparator: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cmtValue(){
|
||||
return this.groupSeparator ? this.$TOOL.groupSeparator(this.value) : this.value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-statistic-title {font-size: 12px;color: #999;margin-bottom: 10px;display: flex;align-items: center;}
|
||||
.sc-statistic-tips {margin-left: 5px;}
|
||||
.sc-statistic-content {font-size: 20px;color: #333;}
|
||||
.sc-statistic-content-value {font-weight: bold;}
|
||||
.sc-statistic-content-prefix {margin-right: 5px;}
|
||||
.sc-statistic-content-suffix {margin-left: 5px;font-size: 12px;}
|
||||
.sc-statistic-description {margin-top: 10px;color: #999;}
|
||||
|
||||
.dark .sc-statistic-content {color: #d0d0d0;}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { h, resolveComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
render() {
|
||||
return h (
|
||||
resolveComponent("el-table-column"),
|
||||
{
|
||||
index: this.index,
|
||||
...this.$attrs
|
||||
},
|
||||
this.$slots
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
index(index){
|
||||
if(this.$attrs.type=="index"){
|
||||
let page = this.$parent.$parent.currentPage
|
||||
let pageSize = this.$parent.$parent.pageSize
|
||||
return (page - 1) * pageSize + index + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div v-if="usercolumn.length>0" class="setting-column" v-loading="isSave">
|
||||
<div class="setting-column__title">
|
||||
<span class="move_b"></span>
|
||||
<span class="show_b">显示</span>
|
||||
<span class="name_b">名称</span>
|
||||
<span class="width_b">宽度</span>
|
||||
<span class="sortable_b">排序</span>
|
||||
<span class="fixed_b">固定</span>
|
||||
</div>
|
||||
<div class="setting-column__list" ref="list">
|
||||
<ul>
|
||||
<li v-for="item in usercolumn" :key="item.prop">
|
||||
<span class="move_b">
|
||||
<el-tag class="move" style="cursor: move;"><el-icon-d-caret style="width: 1em; height: 1em;"/></el-tag>
|
||||
</span>
|
||||
<span class="show_b">
|
||||
<el-switch v-model="item.hide" :active-value="false" :inactive-value="true"></el-switch>
|
||||
</span>
|
||||
<span class="name_b" :title="item.prop">{{ item.label }}</span>
|
||||
<span class="width_b">
|
||||
<el-input v-model="item.width" placeholder="auto"></el-input>
|
||||
</span>
|
||||
<span class="sortable_b">
|
||||
<el-switch v-model="item.sortable"></el-switch>
|
||||
</span>
|
||||
<span class="fixed_b">
|
||||
<el-switch v-model="item.fixed"></el-switch>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-column__bottom">
|
||||
<el-button @click="backDefaul" :disabled="isSave">重置</el-button>
|
||||
<el-button @click="save" type="primary">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无可配置的列" :image-size="80"></el-empty>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sortable
|
||||
},
|
||||
props: {
|
||||
column: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isSave: false,
|
||||
usercolumn: JSON.parse(JSON.stringify(this.column||[]))
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
usercolumn: {
|
||||
handler(){
|
||||
this.$emit('userChange', this.usercolumn)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.usercolumn.length>0 && this.rowDrop()
|
||||
},
|
||||
methods: {
|
||||
rowDrop(){
|
||||
const _this = this
|
||||
const tbody = this.$refs.list.querySelector('ul')
|
||||
Sortable.create(tbody, {
|
||||
handle: ".move",
|
||||
animation: 300,
|
||||
ghostClass: "ghost",
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
const tableData = _this.usercolumn
|
||||
const currRow = tableData.splice(oldIndex, 1)[0]
|
||||
tableData.splice(newIndex, 0, currRow)
|
||||
}
|
||||
})
|
||||
},
|
||||
backDefaul(){
|
||||
this.$emit('back', this.usercolumn)
|
||||
},
|
||||
save(){
|
||||
this.$emit('save', this.usercolumn)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.setting-column {}
|
||||
|
||||
.setting-column__title {border-bottom: 1px solid #EBEEF5;padding-bottom:15px;}
|
||||
.setting-column__title span {display: inline-block;font-weight: bold;color: #909399;font-size: 12px;}
|
||||
.setting-column__title span.move_b {width: 30px;margin-right:15px;}
|
||||
.setting-column__title span.show_b {width: 60px;}
|
||||
.setting-column__title span.name_b {width: 140px;}
|
||||
.setting-column__title span.width_b {width: 60px;margin-right:15px;}
|
||||
.setting-column__title span.sortable_b {width: 60px;}
|
||||
.setting-column__title span.fixed_b {width: 60px;}
|
||||
|
||||
.setting-column__list {max-height:314px;overflow: auto;}
|
||||
.setting-column__list li {list-style: none;margin:10px 0;display: flex;align-items: center;}
|
||||
.setting-column__list li>span {display: inline-block;font-size: 12px;}
|
||||
.setting-column__list li span.move_b {width: 30px;margin-right:15px;}
|
||||
.setting-column__list li span.show_b {width: 60px;}
|
||||
.setting-column__list li span.name_b {width: 140px;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;cursor:default;}
|
||||
.setting-column__list li span.width_b {width: 60px;margin-right:15px;}
|
||||
.setting-column__list li span.sortable_b {width: 60px;}
|
||||
.setting-column__list li span.fixed_b {width: 60px;}
|
||||
.setting-column__list li.ghost {opacity: 0.3;}
|
||||
|
||||
.setting-column__bottom {border-top: 1px solid #EBEEF5;padding-top:15px;text-align: right;}
|
||||
|
||||
.dark .setting-column__title {border-color: var(--el-border-color-light);}
|
||||
.dark .setting-column__bottom {border-color: var(--el-border-color-light);}
|
||||
</style>
|
||||
@@ -0,0 +1,409 @@
|
||||
<!--
|
||||
* @Descripttion: 数据表格组件
|
||||
* @version: 1.10
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年11月29日21:51:15
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年6月4日17:35:26
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="scTable" :style="{'height':_height}" ref="scTableMain" v-loading="loading">
|
||||
<div class="scTable-table" :style="{'height':_table_height}">
|
||||
<el-table v-bind="$attrs" :data="tableData" :row-key="rowKey" :key="toggleIndex" ref="scTable" :height="height=='auto'?null:'100%'" :size="config.size" :border="config.border" :stripe="config.stripe" :summary-method="remoteSummary?remoteSummaryMethod:summaryMethod" @sort-change="sortChange" @filter-change="filterChange">
|
||||
<slot></slot>
|
||||
<template v-for="(item, index) in userColumn" :key="index">
|
||||
<el-table-column v-if="!item.hide" :column-key="item.prop" :label="item.label" :prop="item.prop" :width="item.width" :sortable="item.sortable" :fixed="item.fixed" :filters="item.filters" :filter-method="remoteFilter||!item.filters?null:filterHandler" :show-overflow-tooltip="item.showOverflowTooltip">
|
||||
<template #default="scope">
|
||||
<slot :name="item.prop" v-bind="scope">
|
||||
{{scope.row[item.prop]}}
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column min-width="1"></el-table-column>
|
||||
<template #empty>
|
||||
<el-empty :description="emptyText" :image-size="100"></el-empty>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="scTable-page" v-if="!hidePagination || !hideDo">
|
||||
<div class="scTable-pagination">
|
||||
<el-pagination v-if="!hidePagination" background :layout="paginationLayout" :total="total" :page-size="scPageSize" :page-sizes="pageSizes" v-model:currentPage="currentPage" @current-change="paginationChange" @update:page-size="pageSizeChange"></el-pagination>
|
||||
</div>
|
||||
<div class="scTable-do" v-if="!hideDo">
|
||||
<el-button v-if="!hideRefresh" @click="refresh" icon="el-icon-refresh" circle style="margin-left:15px"></el-button>
|
||||
<el-popover v-if="column" placement="top" title="列设置" :width="500" trigger="click" :hide-after="0" @show="customColumnShow=true" @after-leave="customColumnShow=false">
|
||||
<template #reference>
|
||||
<el-button icon="el-icon-set-up" circle style="margin-left:15px"></el-button>
|
||||
</template>
|
||||
<columnSetting v-if="customColumnShow" ref="columnSetting" @userChange="columnSettingChange" @save="columnSettingSave" @back="columnSettingBack" :column="userColumn"></columnSetting>
|
||||
</el-popover>
|
||||
<el-popover v-if="!hideSetting" placement="top" title="表格设置" :width="400" trigger="click" :hide-after="0">
|
||||
<template #reference>
|
||||
<el-button icon="el-icon-setting" circle style="margin-left:15px"></el-button>
|
||||
</template>
|
||||
<el-form label-width="80px" label-position="left">
|
||||
<el-form-item label="表格尺寸">
|
||||
<el-radio-group v-model="config.size" @change="configSizeChange">
|
||||
<el-radio-button value="large">大</el-radio-button>
|
||||
<el-radio-button value="default">正常</el-radio-button>
|
||||
<el-radio-button value="small">小</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="样式">
|
||||
<el-checkbox v-model="config.border" label="纵向边框"></el-checkbox>
|
||||
<el-checkbox v-model="config.stripe" label="斑马纹"></el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/table";
|
||||
import columnSetting from './columnSetting'
|
||||
|
||||
export default {
|
||||
name: 'scTable',
|
||||
components: {
|
||||
columnSetting
|
||||
},
|
||||
props: {
|
||||
tableName: { type: String, default: "" },
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
params: { type: Object, default: () => ({}) },
|
||||
data: { type: Object, default: () => {} },
|
||||
height: { type: [String,Number], default: "100%" },
|
||||
size: { type: String, default: "small" },
|
||||
border: { type: Boolean, default: true },
|
||||
stripe: { type: Boolean, default: false },
|
||||
pageSize: { type: Number, default: config.pageSize },
|
||||
pageSizes: { type: Array, default: config.pageSizes },
|
||||
rowKey: { type: String, default: "" },
|
||||
summaryMethod: { type: Function, default: null },
|
||||
column: { type: Object, default: () => {} },
|
||||
remoteSort: { type: Boolean, default: false },
|
||||
remoteFilter: { type: Boolean, default: false },
|
||||
remoteSummary: { type: Boolean, default: false },
|
||||
hidePagination: { type: Boolean, default: false },
|
||||
hideDo: { type: Boolean, default: false },
|
||||
hideRefresh: { type: Boolean, default: false },
|
||||
hideSetting: { type: Boolean, default: false },
|
||||
paginationLayout: { type: String, default: config.paginationLayout },
|
||||
},
|
||||
watch: {
|
||||
//监听从props里拿到值了
|
||||
data(){
|
||||
this.tableData = this.data;
|
||||
this.total = this.tableData.length;
|
||||
},
|
||||
apiObj(){
|
||||
this.tableParams = this.params;
|
||||
this.refresh();
|
||||
},
|
||||
column(){
|
||||
this.userColumn=this.column;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
_height() {
|
||||
return Number(this.height)?Number(this.height)+'px':this.height
|
||||
},
|
||||
_table_height() {
|
||||
return this.hidePagination && this.hideDo ? "100%" : "calc(100% - 50px)"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
scPageSize: this.pageSize,
|
||||
isActivat: true,
|
||||
emptyText: "暂无数据",
|
||||
toggleIndex: 0,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
prop: null,
|
||||
order: null,
|
||||
loading: false,
|
||||
tableHeight:'100%',
|
||||
tableParams: this.params,
|
||||
userColumn: [],
|
||||
customColumnShow: false,
|
||||
summary: {},
|
||||
config: {
|
||||
size: this.size,
|
||||
border: this.border,
|
||||
stripe: this.stripe
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
//判断是否开启自定义列
|
||||
if(this.column){
|
||||
this.getCustomColumn()
|
||||
}else{
|
||||
this.userColumn = this.column
|
||||
}
|
||||
//判断是否静态数据
|
||||
if(this.apiObj){
|
||||
this.getData();
|
||||
}else if(this.data){
|
||||
this.tableData = this.data;
|
||||
this.total = this.tableData.length
|
||||
}
|
||||
},
|
||||
activated(){
|
||||
if(!this.isActivat){
|
||||
this.$refs.scTable.doLayout()
|
||||
}
|
||||
},
|
||||
deactivated(){
|
||||
this.isActivat = false;
|
||||
},
|
||||
methods: {
|
||||
//获取列
|
||||
async getCustomColumn(){
|
||||
const userColumn = await config.columnSettingGet(this.tableName, this.column)
|
||||
this.userColumn = userColumn
|
||||
},
|
||||
//获取数据
|
||||
async getData(){
|
||||
this.tableData = []
|
||||
this.loading = true;
|
||||
var reqData = {
|
||||
[config.request.page]: this.currentPage,
|
||||
[config.request.pageSize]: this.scPageSize,
|
||||
[config.request.prop]: this.prop,
|
||||
[config.request.order]: this.order
|
||||
}
|
||||
if(this.hidePagination){
|
||||
delete reqData[config.request.page]
|
||||
delete reqData[config.request.pageSize]
|
||||
}
|
||||
Object.assign(reqData, this.tableParams)
|
||||
|
||||
try {
|
||||
var res = await this.apiObj.get(reqData);
|
||||
}catch(error){
|
||||
this.loading = false;
|
||||
this.emptyText = error.statusText;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var response = config.parseData(res);
|
||||
}catch(error){
|
||||
this.loading = false;
|
||||
this.emptyText = "数据格式错误";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(response.code != config.successCode){
|
||||
this.loading = false;
|
||||
this.emptyText = response.msg;
|
||||
}else{
|
||||
this.emptyText = "暂无数据";
|
||||
if(this.hidePagination){
|
||||
this.tableData = response.data || [];
|
||||
}else{
|
||||
this.tableData = response.rows || [];
|
||||
}
|
||||
this.total = response.total || 0;
|
||||
this.summary = response.summary || {};
|
||||
this.loading = false;
|
||||
}
|
||||
// this.$refs.scTable.setScrollTop(0)
|
||||
this.$emit('dataChange', res, this.tableData)
|
||||
},
|
||||
//分页点击
|
||||
paginationChange(){
|
||||
this.getData();
|
||||
},
|
||||
//条数变化
|
||||
pageSizeChange(size){
|
||||
this.scPageSize = size
|
||||
this.getData();
|
||||
},
|
||||
//刷新数据
|
||||
refresh(){
|
||||
this.$refs.scTable.clearSelection();
|
||||
this.getData();
|
||||
},
|
||||
//更新数据 合并上一次params
|
||||
upData(params, page=1){
|
||||
this.currentPage = page;
|
||||
this.$refs.scTable.clearSelection();
|
||||
Object.assign(this.tableParams, params || {})
|
||||
this.getData()
|
||||
},
|
||||
//重载数据 替换params
|
||||
reload(params, page=1){
|
||||
this.currentPage = page;
|
||||
this.tableParams = params || {}
|
||||
this.$refs.scTable.clearSelection();
|
||||
this.$refs.scTable.clearSort()
|
||||
this.$refs.scTable.clearFilter()
|
||||
this.getData()
|
||||
},
|
||||
//自定义变化事件
|
||||
columnSettingChange(userColumn){
|
||||
this.userColumn = userColumn;
|
||||
this.toggleIndex += 1;
|
||||
},
|
||||
//自定义列保存
|
||||
async columnSettingSave(userColumn){
|
||||
this.$refs.columnSetting.isSave = true
|
||||
try {
|
||||
await config.columnSettingSave(this.tableName, userColumn)
|
||||
}catch(error){
|
||||
this.$message.error('保存失败')
|
||||
this.$refs.columnSetting.isSave = false
|
||||
}
|
||||
this.$message.success('保存成功')
|
||||
this.$refs.columnSetting.isSave = false
|
||||
},
|
||||
//自定义列重置
|
||||
async columnSettingBack(){
|
||||
this.$refs.columnSetting.isSave = true
|
||||
try {
|
||||
const column = await config.columnSettingReset(this.tableName, this.column)
|
||||
this.userColumn = column
|
||||
this.$refs.columnSetting.usercolumn = JSON.parse(JSON.stringify(this.userColumn||[]))
|
||||
}catch(error){
|
||||
this.$message.error('重置失败')
|
||||
this.$refs.columnSetting.isSave = false
|
||||
}
|
||||
this.$refs.columnSetting.isSave = false
|
||||
},
|
||||
//排序事件
|
||||
sortChange(obj){
|
||||
if(!this.remoteSort){
|
||||
return false
|
||||
}
|
||||
if(obj.column && obj.prop){
|
||||
this.prop = obj.prop
|
||||
this.order = obj.order
|
||||
}else{
|
||||
this.prop = null
|
||||
this.order = null
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
//本地过滤
|
||||
filterHandler(value, row, column){
|
||||
const property = column.property;
|
||||
return row[property] === value;
|
||||
},
|
||||
//过滤事件
|
||||
filterChange(filters){
|
||||
if(!this.remoteFilter){
|
||||
return false
|
||||
}
|
||||
Object.keys(filters).forEach(key => {
|
||||
filters[key] = filters[key].join(',')
|
||||
})
|
||||
this.upData(filters)
|
||||
},
|
||||
//远程合计行处理
|
||||
remoteSummaryMethod(param){
|
||||
const {columns} = param
|
||||
const sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if(index === 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
const values = this.summary[column.property]
|
||||
if(values){
|
||||
sums[index] = values
|
||||
}else{
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
},
|
||||
configSizeChange(){
|
||||
this.$refs.scTable.doLayout()
|
||||
},
|
||||
//插入行 unshiftRow
|
||||
unshiftRow(row){
|
||||
this.tableData.unshift(row)
|
||||
},
|
||||
//插入行 pushRow
|
||||
pushRow(row){
|
||||
this.tableData.push(row)
|
||||
},
|
||||
//根据key覆盖数据
|
||||
updateKey(row, rowKey=this.rowKey){
|
||||
this.tableData.filter(item => item[rowKey]===row[rowKey] ).forEach(item => {
|
||||
Object.assign(item, row)
|
||||
})
|
||||
},
|
||||
//根据index覆盖数据
|
||||
updateIndex(row, index){
|
||||
Object.assign(this.tableData[index], row)
|
||||
},
|
||||
//根据index删除
|
||||
removeIndex(index){
|
||||
this.tableData.splice(index, 1)
|
||||
},
|
||||
//根据index批量删除
|
||||
removeIndexes(indexes=[]){
|
||||
indexes.forEach(index => {
|
||||
this.tableData.splice(index, 1)
|
||||
})
|
||||
},
|
||||
//根据key删除
|
||||
removeKey(key, rowKey=this.rowKey){
|
||||
this.tableData.splice(this.tableData.findIndex(item => item[rowKey]===key), 1)
|
||||
},
|
||||
//根据keys批量删除
|
||||
removeKeys(keys=[], rowKey=this.rowKey){
|
||||
keys.forEach(key => {
|
||||
this.tableData.splice(this.tableData.findIndex(item => item[rowKey]===key), 1)
|
||||
})
|
||||
},
|
||||
//原生方法转发
|
||||
clearSelection(){
|
||||
this.$refs.scTable.clearSelection()
|
||||
},
|
||||
toggleRowSelection(row, selected){
|
||||
this.$refs.scTable.toggleRowSelection(row, selected)
|
||||
},
|
||||
toggleAllSelection(){
|
||||
this.$refs.scTable.toggleAllSelection()
|
||||
},
|
||||
toggleRowExpansion(row, expanded){
|
||||
this.$refs.scTable.toggleRowExpansion(row, expanded)
|
||||
},
|
||||
setCurrentRow(row){
|
||||
this.$refs.scTable.setCurrentRow(row)
|
||||
},
|
||||
clearSort(){
|
||||
this.$refs.scTable.clearSort()
|
||||
},
|
||||
clearFilter(columnKey){
|
||||
this.$refs.scTable.clearFilter(columnKey)
|
||||
},
|
||||
doLayout(){
|
||||
this.$refs.scTable.doLayout()
|
||||
},
|
||||
sort(prop, order){
|
||||
this.$refs.scTable.sort(prop, order)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scTable {}
|
||||
.scTable-table {height: calc(100% - 50px);}
|
||||
.scTable-page {height:50px;display: flex;align-items: center;justify-content: space-between;padding:0 15px;}
|
||||
.scTable-do {white-space: nowrap;}
|
||||
.scTable:deep(.el-table__footer) .cell {font-weight: bold;}
|
||||
.scTable:deep(.el-table__body-wrapper) .el-scrollbar__bar.is-horizontal {height: 12px;border-radius: 12px;}
|
||||
.scTable:deep(.el-table__body-wrapper) .el-scrollbar__bar.is-vertical {width: 12px;border-radius: 12px;}
|
||||
</style>
|
||||
@@ -0,0 +1,233 @@
|
||||
<!--
|
||||
* @Descripttion: 表格选择器组件
|
||||
* @version: 1.3
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年6月10日10:04:07
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年6月6日21:50:36
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-select ref="select" v-model="defaultLable" :size="size" :clearable="clearable" :multiple="multiple" :collapse-tags="collapseTags" :collapse-tags-tooltip="collapseTagsTooltip" :filterable="filterable" :placeholder="placeholder" :disabled="disabled" :filter-method="filterMethod" @remove-tag="removeTag" @visible-change="visibleChange" @clear="clear">
|
||||
<template #header>
|
||||
<slot name="header" :form="formData" :submit="formSubmit"></slot>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div :style="{width: tableWidth+'px'}" v-loading="loading">
|
||||
<el-table ref="table" :data="tableData" :height="245" :highlight-current-row="!multiple" @row-click="click" @select="select" @select-all="selectAll">
|
||||
<el-table-column v-if="multiple" type="selection" width="45"></el-table-column>
|
||||
<el-table-column v-else type="index" width="45">
|
||||
<template #default="scope"><span>{{scope.$index+(currentPage - 1) * pageSize + 1}}</span></template>
|
||||
</el-table-column>
|
||||
<slot></slot>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-pagination small background layout="prev, pager, next" :total="total" :page-size="pageSize" v-model:currentPage="currentPage" @current-change="reload"></el-pagination>
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/tableSelect";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: null,
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
params: { type: Object, default: () => {} },
|
||||
placeholder: { type: String, default: "请选择" },
|
||||
size: { type: String, default: "default" },
|
||||
clearable: { type: Boolean, default: false },
|
||||
multiple: { type: Boolean, default: false },
|
||||
filterable: { type: Boolean, default: false },
|
||||
collapseTags: { type: Boolean, default: false },
|
||||
collapseTagsTooltip: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
tableWidth: {type: Number, default: 400},
|
||||
mode: { type: String, default: "popover" },
|
||||
props: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
keyword: null,
|
||||
defaultValue: [],
|
||||
defaultLable: [],
|
||||
tableData: [],
|
||||
pageSize: config.pageSize,
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
defaultProps: {
|
||||
label: config.props.label,
|
||||
value: config.props.value,
|
||||
page: config.request.page,
|
||||
pageSize: config.request.pageSize,
|
||||
keyword: config.request.keyword
|
||||
},
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
modelValue:{
|
||||
handler(){
|
||||
this.defaultValue = this.modelValue
|
||||
this.autoCurrentLabel()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.defaultProps = Object.assign(this.defaultProps, this.props);
|
||||
this.defaultValue = this.modelValue
|
||||
this.autoCurrentLabel()
|
||||
},
|
||||
methods: {
|
||||
//表格显示隐藏回调
|
||||
visibleChange(visible){
|
||||
if(visible){
|
||||
this.currentPage = 1
|
||||
this.keyword = null
|
||||
this.formData = {}
|
||||
this.getData()
|
||||
}else{
|
||||
this.autoCurrentLabel()
|
||||
}
|
||||
},
|
||||
//获取表格数据
|
||||
async getData(){
|
||||
this.loading = true;
|
||||
var reqData = {
|
||||
[this.defaultProps.page]: this.currentPage,
|
||||
[this.defaultProps.pageSize]: this.pageSize,
|
||||
[this.defaultProps.keyword]: this.keyword
|
||||
}
|
||||
Object.assign(reqData, this.params, this.formData)
|
||||
var res = await this.apiObj.get(reqData);
|
||||
var parseData = config.parseData(res)
|
||||
this.tableData = parseData.rows;
|
||||
this.total = parseData.total;
|
||||
this.loading = false;
|
||||
//表格默认赋值
|
||||
this.$nextTick(() => {
|
||||
if(this.multiple){
|
||||
this.defaultValue.forEach(row => {
|
||||
var setrow = this.tableData.filter(item => item[this.defaultProps.value]===row[this.defaultProps.value] )
|
||||
if(setrow.length > 0){
|
||||
this.$refs.table.toggleRowSelection(setrow[0], true);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
var setrow = this.tableData.filter(item => item[this.defaultProps.value]===this.defaultValue[this.defaultProps.value] )
|
||||
this.$refs.table.setCurrentRow(setrow[0]);
|
||||
}
|
||||
this.$refs.table.setScrollTop(0)
|
||||
})
|
||||
},
|
||||
//插糟表单提交
|
||||
formSubmit(){
|
||||
this.currentPage = 1
|
||||
this.keyword = null
|
||||
this.getData()
|
||||
},
|
||||
//分页刷新表格
|
||||
reload(){
|
||||
this.getData()
|
||||
},
|
||||
//自动模拟options赋值
|
||||
autoCurrentLabel(){
|
||||
this.$nextTick(() => {
|
||||
if(this.multiple){
|
||||
this.defaultValue.map(item => {
|
||||
this.defaultLable.push(item[this.props.label])
|
||||
})
|
||||
}else{
|
||||
this.defaultLable = this.defaultValue[this.defaultProps.label]
|
||||
}
|
||||
})
|
||||
},
|
||||
//表格勾选事件
|
||||
select(rows, row){
|
||||
var isSelect = rows.length && rows.indexOf(row) !== -1
|
||||
if(isSelect){
|
||||
this.defaultValue.push(row)
|
||||
}else{
|
||||
this.defaultValue.splice(this.defaultValue.findIndex(item => item[this.defaultProps.value] == row[this.defaultProps.value]), 1)
|
||||
}
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
},
|
||||
//表格全选事件
|
||||
selectAll(rows){
|
||||
var isAllSelect = rows.length > 0
|
||||
if(isAllSelect){
|
||||
rows.forEach(row => {
|
||||
var isHas = this.defaultValue.find(item => item[this.defaultProps.value] == row[this.defaultProps.value])
|
||||
if(!isHas){
|
||||
this.defaultValue.push(row)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
this.tableData.forEach(row => {
|
||||
var isHas = this.defaultValue.find(item => item[this.defaultProps.value] == row[this.defaultProps.value])
|
||||
if(isHas){
|
||||
this.defaultValue.splice(this.defaultValue.findIndex(item => item[this.defaultProps.value] == row[this.defaultProps.value]), 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
},
|
||||
click(row){
|
||||
if(this.multiple){
|
||||
//处理多选点击行
|
||||
}else{
|
||||
this.defaultValue = row
|
||||
this.$refs.select.blur()
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
}
|
||||
},
|
||||
//tags删除后回调
|
||||
removeTag(tag){
|
||||
var row = this.findRowByKey(tag[this.defaultProps.value])
|
||||
this.$refs.table.toggleRowSelection(row, false);
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
},
|
||||
//清空后的回调
|
||||
clear(){
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
},
|
||||
// 关键值查询表格数据行
|
||||
findRowByKey (value) {
|
||||
return this.tableData.find(item => item[this.defaultProps.value] === value)
|
||||
},
|
||||
filterMethod(keyword){
|
||||
if(!keyword){
|
||||
this.keyword = null;
|
||||
return false;
|
||||
}
|
||||
this.keyword = keyword;
|
||||
this.getData()
|
||||
},
|
||||
// 触发select隐藏
|
||||
blur(){
|
||||
this.$refs.select.blur();
|
||||
},
|
||||
// 触发select显示
|
||||
focus(){
|
||||
this.$refs.select.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="sc-title">
|
||||
<span class="title">{{title}}</span>
|
||||
<span><slot name="right"></slot></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: { type: String, required: true, default: "" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-title {border-bottom: 1px solid #eee;padding: 5px; margin: 15px 0 5px 0; display: flex;justify-content: space-between;}
|
||||
.sc-title span.title{font-size: 14px; color: #3c4a54;font-weight: bold;}
|
||||
</style>
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="sc-upload-file">
|
||||
<el-upload
|
||||
:disabled="disabled"
|
||||
:auto-upload="autoUpload"
|
||||
:action="action"
|
||||
:name="name"
|
||||
:data="data"
|
||||
:http-request="request"
|
||||
v-model:file-list="defaultFileList"
|
||||
:show-file-list="showFileList"
|
||||
:drag="drag"
|
||||
:accept="accept"
|
||||
:multiple="multiple"
|
||||
:limit="limit"
|
||||
:before-upload="before"
|
||||
:on-success="success"
|
||||
:on-error="error"
|
||||
:on-preview="handlePreview"
|
||||
:on-exceed="handleExceed">
|
||||
<slot>
|
||||
<el-button type="primary" :disabled="disabled">点击上传</el-button>
|
||||
</slot>
|
||||
<template #tip>
|
||||
<div v-if="tip" class="el-upload__tip">{{tip}}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<span style="display:none!important"><el-input v-model="value"></el-input></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/upload"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: [String, Array], default: "" },
|
||||
tip: { type: String, default: "" },
|
||||
action: { type: String, default: "" },
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
name: { type: String, default: config.filename },
|
||||
data: { type: Object, default: () => {} },
|
||||
accept: { type: String, default: "" },
|
||||
maxSize: { type: Number, default: config.maxSizeFile },
|
||||
limit: { type: Number, default: 0 },
|
||||
autoUpload: { type: Boolean, default: true },
|
||||
showFileList: { type: Boolean, default: true },
|
||||
drag: { type: Boolean, default: false },
|
||||
multiple: { type: Boolean, default: true },
|
||||
disabled: { type: Boolean, default: false },
|
||||
onSuccess: { type: Function, default: () => { return true } }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: "",
|
||||
defaultFileList: []
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
if(Array.isArray(val)){
|
||||
if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
|
||||
this.defaultFileList = val
|
||||
this.value = val
|
||||
}
|
||||
}else{
|
||||
if (val != this.toStr(this.defaultFileList)) {
|
||||
this.defaultFileList = this.toArr(val)
|
||||
this.value = val
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultFileList: {
|
||||
handler(val){
|
||||
this.$emit('update:modelValue', Array.isArray(this.modelValue) ? this.formatArr(val) : this.toStr(val))
|
||||
this.value = this.toStr(val)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.defaultFileList = Array.isArray(this.modelValue) ? this.modelValue : this.toArr(this.modelValue)
|
||||
this.value = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
//默认值转换为数组
|
||||
toArr(str){
|
||||
if(!str){
|
||||
return []
|
||||
}
|
||||
var _arr = []
|
||||
var arr = str.split(",")
|
||||
arr.forEach(item => {
|
||||
if(item){
|
||||
var urlArr = item.split('/');
|
||||
var fileName = urlArr[urlArr.length - 1]
|
||||
_arr.push({
|
||||
name: fileName,
|
||||
url: item
|
||||
})
|
||||
}
|
||||
})
|
||||
return _arr
|
||||
},
|
||||
//数组转换为原始值
|
||||
toStr(arr){
|
||||
return arr.map(v => v.url).join(",")
|
||||
},
|
||||
//格式化数组值
|
||||
formatArr(arr){
|
||||
var _arr = []
|
||||
arr.forEach(item => {
|
||||
if(item){
|
||||
_arr.push({
|
||||
name: item.name,
|
||||
url: item.url
|
||||
})
|
||||
}
|
||||
})
|
||||
return _arr
|
||||
},
|
||||
before(file){
|
||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||
if (!maxSize) {
|
||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
success(res, file){
|
||||
var os = this.onSuccess(res, file)
|
||||
if(os!=undefined && os==false){
|
||||
return false
|
||||
}
|
||||
var response = config.parseData(res)
|
||||
file.name = response.fileName
|
||||
file.url = response.src
|
||||
},
|
||||
error(err){
|
||||
this.$notify.error({
|
||||
title: '上传文件未成功',
|
||||
message: err
|
||||
})
|
||||
},
|
||||
beforeRemove(uploadFile){
|
||||
return this.$confirm(`是否移除 ${uploadFile.name} ?`, '提示', {
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
return true
|
||||
}).catch(() => {
|
||||
return false
|
||||
})
|
||||
},
|
||||
handleExceed(){
|
||||
this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`)
|
||||
},
|
||||
handlePreview(uploadFile){
|
||||
window.open(uploadFile.url)
|
||||
},
|
||||
request(param){
|
||||
var apiObj = config.apiObjFile;
|
||||
if(this.apiObj){
|
||||
apiObj = this.apiObj;
|
||||
}
|
||||
const data = new FormData();
|
||||
data.append(param.filename, param.file);
|
||||
for (const key in param.data) {
|
||||
data.append(key, param.data[key]);
|
||||
}
|
||||
apiObj.post(data, {
|
||||
onUploadProgress: e => {
|
||||
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
|
||||
param.onProgress({percent: complete})
|
||||
}
|
||||
}).then(res => {
|
||||
var response = config.parseData(res);
|
||||
if(response.code == config.successCode){
|
||||
param.onSuccess(res)
|
||||
}else{
|
||||
param.onError(response.msg || "未知错误")
|
||||
}
|
||||
}).catch(err => {
|
||||
param.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item.is-error .sc-upload-file:deep(.el-upload-dragger) {border-color: var(--el-color-danger);}
|
||||
.sc-upload-file {width: 100%;}
|
||||
.sc-upload-file:deep(.el-upload-list__item) {transition: none !important;}
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="sc-upload" :class="{'sc-upload-round':round}" :style="style">
|
||||
<div v-if="file && file.status != 'success'" class="sc-upload__uploading">
|
||||
<div class="sc-upload__progress">
|
||||
<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16"/>
|
||||
</div>
|
||||
<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
|
||||
</div>
|
||||
<div v-if="file && file.status=='success'" class="sc-upload__img">
|
||||
<el-image class="image" :src="file.url" :preview-src-list="[file.url]" fit="cover" hide-on-click-modal append-to-body :z-index="9999">
|
||||
<template #placeholder>
|
||||
<div class="sc-upload__img-slot">
|
||||
Loading...
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="sc-upload__img-actions" v-if="!disabled">
|
||||
<span class="del" @click="handleRemove()"><el-icon><el-icon-delete /></el-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload v-if="!file" class="uploader" ref="uploader"
|
||||
:auto-upload="cropper?false:autoUpload"
|
||||
:disabled="disabled"
|
||||
:show-file-list="showFileList"
|
||||
:action="action"
|
||||
:name="name"
|
||||
:data="data"
|
||||
:accept="accept"
|
||||
:limit="1"
|
||||
:http-request="request"
|
||||
:on-change="change"
|
||||
:before-upload="before"
|
||||
:on-success="success"
|
||||
:on-error="error"
|
||||
:on-exceed="handleExceed"
|
||||
drag>
|
||||
<slot>
|
||||
<div class="el-upload--picture-card">
|
||||
<div class="file-empty">
|
||||
<el-icon><component :is="icon" /></el-icon>
|
||||
<h4 v-if="title">{{title}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</el-upload>
|
||||
<span style="display:none!important"><el-input v-model="value"></el-input></span>
|
||||
<el-dialog title="剪裁" draggable v-model="cropperDialogVisible" :width="680" @closed="cropperClosed" destroy-on-close>
|
||||
<sc-cropper :src="cropperFile.tempCropperFile" :compress="compress" :aspectRatio="aspectRatio" ref="cropper"></sc-cropper>
|
||||
<template #footer>
|
||||
<el-button @click="cropperDialogVisible=false" >取 消</el-button>
|
||||
<el-button type="primary" @click="cropperSave">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { genFileId } from 'element-plus'
|
||||
const scCropper = defineAsyncComponent(() => import('@/components/scCropper'))
|
||||
import config from "@/config/upload"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: String, default: "" },
|
||||
height: {type: Number, default: 148},
|
||||
width: {type: Number, default: 148},
|
||||
title: { type: String, default: "" },
|
||||
icon: { type: String, default: "el-icon-plus" },
|
||||
action: { type: String, default: "" },
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
name: { type: String, default: config.filename },
|
||||
data: { type: Object, default: () => {} },
|
||||
accept: { type: String, default: "image/gif, image/jpeg, image/png" },
|
||||
maxSize: { type: Number, default: config.maxSizeFile },
|
||||
limit: { type: Number, default: 1 },
|
||||
autoUpload: { type: Boolean, default: true },
|
||||
showFileList: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
round: { type: Boolean, default: false },
|
||||
onSuccess: { type: Function, default: () => { return true } },
|
||||
|
||||
cropper: { type: Boolean, default: false },
|
||||
compress: {type: Number, default: 1},
|
||||
aspectRatio: {type: Number, default: NaN}
|
||||
},
|
||||
components: {
|
||||
scCropper
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: "",
|
||||
file: null,
|
||||
style: {
|
||||
width: this.width + "px",
|
||||
height: this.height + "px"
|
||||
},
|
||||
cropperDialogVisible: false,
|
||||
cropperFile: null
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
this.value = val
|
||||
this.newFile(val)
|
||||
},
|
||||
value(val){
|
||||
this.$emit('update:modelValue', val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.value = this.modelValue
|
||||
this.newFile(this.modelValue)
|
||||
},
|
||||
methods: {
|
||||
newFile(url){
|
||||
if(url){
|
||||
this.file = {
|
||||
status: "success",
|
||||
url: url
|
||||
}
|
||||
}else{
|
||||
this.file = null
|
||||
}
|
||||
},
|
||||
cropperSave(){
|
||||
this.$refs.cropper.getCropFile(file => {
|
||||
|
||||
file.uid = this.cropperFile.uid
|
||||
this.cropperFile.raw = file
|
||||
|
||||
this.file = this.cropperFile
|
||||
this.file.tempFile = URL.createObjectURL(this.file.raw)
|
||||
this.$refs.uploader.submit()
|
||||
|
||||
}, this.cropperFile.name, this.cropperFile.type)
|
||||
this.cropperDialogVisible = false
|
||||
},
|
||||
cropperClosed(){
|
||||
URL.revokeObjectURL(this.cropperFile.tempCropperFile)
|
||||
delete this.cropperFile.tempCropperFile
|
||||
},
|
||||
handleRemove(){
|
||||
this.clearFiles()
|
||||
},
|
||||
clearFiles(){
|
||||
URL.revokeObjectURL(this.file.tempFile)
|
||||
this.value = ""
|
||||
this.file = null
|
||||
this.$nextTick(()=>{
|
||||
this.$refs.uploader.clearFiles()
|
||||
})
|
||||
},
|
||||
change(file,files){
|
||||
if(files.length > 1){
|
||||
files.splice(0, 1)
|
||||
}
|
||||
if(this.cropper && file.status=='ready'){
|
||||
const acceptIncludes = ["image/gif", "image/jpeg", "image/png"].includes(file.raw.type)
|
||||
if(!acceptIncludes){
|
||||
this.$notify.warning({
|
||||
title: '上传文件警告',
|
||||
message: '选择的文件非图像类文件'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.cropperFile = file
|
||||
this.cropperFile.tempCropperFile = URL.createObjectURL(file.raw)
|
||||
this.cropperDialogVisible = true
|
||||
return false
|
||||
}
|
||||
this.file = file
|
||||
if(file.status=='ready'){
|
||||
file.tempFile = URL.createObjectURL(file.raw)
|
||||
}
|
||||
},
|
||||
before(file){
|
||||
const acceptIncludes = this.accept.replace(/\s/g,"").split(",").includes(file.type)
|
||||
if(!acceptIncludes){
|
||||
this.$notify.warning({
|
||||
title: '上传文件警告',
|
||||
message: '选择的文件非图像类文件'
|
||||
})
|
||||
this.clearFiles()
|
||||
return false
|
||||
}
|
||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||
if (!maxSize) {
|
||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||
this.clearFiles()
|
||||
return false
|
||||
}
|
||||
},
|
||||
handleExceed(files){
|
||||
const file = files[0]
|
||||
file.uid = genFileId()
|
||||
this.$refs.uploader.handleStart(file)
|
||||
},
|
||||
success(res, file){
|
||||
//释放内存删除blob
|
||||
URL.revokeObjectURL(file.tempFile)
|
||||
delete file.tempFile
|
||||
var os = this.onSuccess(res, file)
|
||||
if(os!=undefined && os==false){
|
||||
this.$nextTick(() => {
|
||||
this.file = null
|
||||
this.value = ""
|
||||
})
|
||||
return false
|
||||
}
|
||||
var response = config.parseData(res)
|
||||
file.url = response.src
|
||||
this.value = file.url
|
||||
},
|
||||
error(err){
|
||||
this.$nextTick(()=>{
|
||||
this.clearFiles()
|
||||
})
|
||||
this.$notify.error({
|
||||
title: '上传文件未成功',
|
||||
message: err
|
||||
})
|
||||
},
|
||||
request(param){
|
||||
var apiObj = config.apiObj;
|
||||
if(this.apiObj){
|
||||
apiObj = this.apiObj;
|
||||
}
|
||||
const data = new FormData();
|
||||
data.append(param.filename, param.file);
|
||||
for (const key in param.data) {
|
||||
data.append(key, param.data[key]);
|
||||
}
|
||||
apiObj.post(data, {
|
||||
onUploadProgress: e => {
|
||||
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
|
||||
param.onProgress({percent: complete})
|
||||
}
|
||||
}).then(res => {
|
||||
var response = config.parseData(res);
|
||||
if(response.code == config.successCode){
|
||||
param.onSuccess(res)
|
||||
}else{
|
||||
param.onError(response.msg || "未知错误")
|
||||
}
|
||||
}).catch(err => {
|
||||
param.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item.is-error .sc-upload .el-upload--picture-card {border-color: var(--el-color-danger);}
|
||||
.sc-upload .el-upload--picture-card {border-radius: 0;}
|
||||
|
||||
.sc-upload .uploader,.sc-upload:deep(.el-upload) {width: 100%;height: 100%;}
|
||||
|
||||
.sc-upload__img {width: 100%;height: 100%;position: relative;}
|
||||
.sc-upload__img .image {width: 100%;height: 100%;}
|
||||
.sc-upload__img-actions {position: absolute;top:0;right: 0;display: none;}
|
||||
.sc-upload__img-actions span {display: flex;justify-content: center;align-items: center;width: 25px;height:25px;cursor: pointer;color: #fff;}
|
||||
.sc-upload__img-actions span i {font-size: 12px;}
|
||||
.sc-upload__img-actions .del {background: #F56C6C;}
|
||||
.sc-upload__img:hover .sc-upload__img-actions {display: block;}
|
||||
.sc-upload__img-slot {display: flex;justify-content: center;align-items: center;width: 100%;height: 100%;font-size: 12px;background-color: var(--el-fill-color-lighter);}
|
||||
|
||||
.sc-upload__uploading {width: 100%;height: 100%;position: relative;}
|
||||
.sc-upload__progress {position: absolute;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: var(--el-overlay-color-lighter);z-index: 1;padding:10px;}
|
||||
.sc-upload__progress .el-progress {width: 100%;}
|
||||
.sc-upload__uploading .image {width: 100%;height: 100%;}
|
||||
|
||||
.sc-upload .file-empty {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;flex-direction: column;}
|
||||
.sc-upload .file-empty i {font-size: 28px;}
|
||||
.sc-upload .file-empty h4 {font-size: 12px;font-weight: normal;color: #8c939d;margin-top: 8px;}
|
||||
|
||||
.sc-upload.sc-upload-round {border-radius: 50%;overflow: hidden;}
|
||||
.sc-upload.sc-upload-round .el-upload--picture-card {border-radius: 50%;}
|
||||
.sc-upload.sc-upload-round .sc-upload__img-actions {top: auto;left: 0;right: 0;bottom: 0;}
|
||||
.sc-upload.sc-upload-round .sc-upload__img-actions span {width: 100%;}
|
||||
</style>
|
||||
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<div class="sc-upload-multiple">
|
||||
<el-upload ref="uploader" list-type="picture-card"
|
||||
:auto-upload="autoUpload"
|
||||
:disabled="disabled"
|
||||
:action="action"
|
||||
:name="name"
|
||||
:data="data"
|
||||
:http-request="request"
|
||||
v-model:file-list="defaultFileList"
|
||||
:show-file-list="showFileList"
|
||||
:accept="accept"
|
||||
:multiple="multiple"
|
||||
:limit="limit"
|
||||
:before-upload="before"
|
||||
:on-success="success"
|
||||
:on-error="error"
|
||||
:on-preview="handlePreview"
|
||||
:on-exceed="handleExceed">
|
||||
<slot>
|
||||
<el-icon><el-icon-plus/></el-icon>
|
||||
</slot>
|
||||
<template #tip>
|
||||
<div v-if="tip" class="el-upload__tip">{{tip}}</div>
|
||||
</template>
|
||||
<template #file="{ file }">
|
||||
<div class="sc-upload-list-item">
|
||||
<el-image class="el-upload-list__item-thumbnail" :src="file.url" fit="cover" :preview-src-list="preview" :initial-index="preview.findIndex(n=>n==file.url)" hide-on-click-modal append-to-body :z-index="9999">
|
||||
<template #placeholder>
|
||||
<div class="sc-upload-multiple-image-slot">
|
||||
Loading...
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div v-if="!disabled && file.status=='success'" class="sc-upload__item-actions">
|
||||
<span class="del" @click="handleRemove(file)"><el-icon><el-icon-delete /></el-icon></span>
|
||||
</div>
|
||||
<div v-if="file.status=='ready' || file.status=='uploading'" class="sc-upload__item-progress">
|
||||
<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<span style="display:none!important"><el-input v-model="value"></el-input></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/upload"
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: [String, Array], default: "" },
|
||||
tip: { type: String, default: "" },
|
||||
action: { type: String, default: "" },
|
||||
apiObj: { type: Object, default: () => {} },
|
||||
name: { type: String, default: config.filename },
|
||||
data: { type: Object, default: () => {} },
|
||||
accept: { type: String, default: "image/gif, image/jpeg, image/png" },
|
||||
maxSize: { type: Number, default: config.maxSizeFile },
|
||||
limit: { type: Number, default: 0 },
|
||||
autoUpload: { type: Boolean, default: true },
|
||||
showFileList: { type: Boolean, default: true },
|
||||
multiple: { type: Boolean, default: true },
|
||||
disabled: { type: Boolean, default: false },
|
||||
draggable: { type: Boolean, default: false },
|
||||
onSuccess: { type: Function, default: () => { return true } }
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
value: "",
|
||||
defaultFileList: []
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
if(Array.isArray(val)){
|
||||
if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
|
||||
this.defaultFileList = val
|
||||
this.value = val
|
||||
}
|
||||
}else{
|
||||
if (val != this.toStr(this.defaultFileList)) {
|
||||
this.defaultFileList = this.toArr(val)
|
||||
this.value = val
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultFileList: {
|
||||
handler(val){
|
||||
this.$emit('update:modelValue', Array.isArray(this.modelValue) ? this.formatArr(val) : this.toStr(val))
|
||||
this.value = this.toStr(val)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
preview(){
|
||||
return this.defaultFileList.map(v => v.url)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.defaultFileList = Array.isArray(this.modelValue) ? this.modelValue : this.toArr(this.modelValue)
|
||||
this.value = this.modelValue
|
||||
if(!this.disabled && this.draggable){
|
||||
this.rowDrop()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//默认值转换为数组
|
||||
toArr(str){
|
||||
var _arr = [];
|
||||
var arr = str.split(",");
|
||||
arr.forEach(item => {
|
||||
if(item){
|
||||
var urlArr = item.split('/');
|
||||
var fileName = urlArr[urlArr.length - 1]
|
||||
_arr.push({
|
||||
name: fileName,
|
||||
url: item
|
||||
})
|
||||
}
|
||||
})
|
||||
return _arr;
|
||||
},
|
||||
//数组转换为原始值
|
||||
toStr(arr){
|
||||
return arr.map(v => v.url).join(",")
|
||||
},
|
||||
//格式化数组值
|
||||
formatArr(arr){
|
||||
var _arr = []
|
||||
arr.forEach(item => {
|
||||
if(item){
|
||||
_arr.push({
|
||||
name: item.name,
|
||||
url: item.url
|
||||
})
|
||||
}
|
||||
})
|
||||
return _arr
|
||||
},
|
||||
//拖拽
|
||||
rowDrop(){
|
||||
const _this = this
|
||||
const itemBox = this.$refs.uploader.$el.querySelector('.el-upload-list')
|
||||
Sortable.create(itemBox, {
|
||||
handle: ".el-upload-list__item",
|
||||
animation: 200,
|
||||
ghostClass: "ghost",
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
const tableData = _this.defaultFileList
|
||||
const currRow = tableData.splice(oldIndex, 1)[0]
|
||||
tableData.splice(newIndex, 0, currRow)
|
||||
}
|
||||
})
|
||||
},
|
||||
before(file){
|
||||
if(!['image/jpeg','image/png','image/gif'].includes(file.type)){
|
||||
this.$message.warning(`选择的文件类型 ${file.type} 非图像类文件`);
|
||||
return false;
|
||||
}
|
||||
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
||||
if (!maxSize) {
|
||||
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
success(res, file){
|
||||
var os = this.onSuccess(res, file)
|
||||
if(os!=undefined && os==false){
|
||||
return false
|
||||
}
|
||||
var response = config.parseData(res)
|
||||
file.name = response.fileName
|
||||
file.url = response.src
|
||||
},
|
||||
error(err){
|
||||
this.$notify.error({
|
||||
title: '上传文件未成功',
|
||||
message: err
|
||||
})
|
||||
},
|
||||
beforeRemove(uploadFile){
|
||||
return this.$confirm(`是否移除 ${uploadFile.name} ?`, '提示', {
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
return true
|
||||
}).catch(() => {
|
||||
return false
|
||||
})
|
||||
},
|
||||
handleRemove(file){
|
||||
this.$refs.uploader.handleRemove(file)
|
||||
//this.defaultFileList.splice(this.defaultFileList.findIndex(item => item.uid===file.uid), 1)
|
||||
},
|
||||
handleExceed(){
|
||||
this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`)
|
||||
},
|
||||
handlePreview(uploadFile){
|
||||
window.open(uploadFile.url)
|
||||
},
|
||||
request(param){
|
||||
var apiObj = config.apiObj;
|
||||
if(this.apiObj){
|
||||
apiObj = this.apiObj;
|
||||
}
|
||||
const data = new FormData();
|
||||
data.append(param.filename, param.file);
|
||||
for (const key in param.data) {
|
||||
data.append(key, param.data[key]);
|
||||
}
|
||||
apiObj.post(data, {
|
||||
onUploadProgress: e => {
|
||||
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
|
||||
param.onProgress({percent: complete})
|
||||
}
|
||||
}).then(res => {
|
||||
var response = config.parseData(res);
|
||||
if(response.code == config.successCode){
|
||||
param.onSuccess(res)
|
||||
}else{
|
||||
param.onError(response.msg || "未知错误")
|
||||
}
|
||||
}).catch(err => {
|
||||
param.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item.is-error .sc-upload-multiple:deep(.el-upload--picture-card) {border-color: var(--el-color-danger);}
|
||||
:deep(.el-upload-list__item) {transition:none;border-radius: 0;}
|
||||
.sc-upload-multiple:deep(.el-upload-list__item.el-list-leave-active) {position: static!important;}
|
||||
.sc-upload-multiple:deep(.el-upload--picture-card) {border-radius: 0;}
|
||||
.sc-upload-list-item {width: 100%;height: 100%;position: relative;}
|
||||
.sc-upload-multiple .el-image {display: block;}
|
||||
.sc-upload-multiple .el-image:deep(img) {-webkit-user-drag: none;}
|
||||
.sc-upload-multiple-image-slot {display: flex;justify-content: center;align-items: center;width: 100%;height: 100%;font-size: 12px;}
|
||||
.sc-upload-multiple .el-upload-list__item:hover .sc-upload__item-actions{display: block;}
|
||||
.sc-upload__item-actions {position: absolute;top:0;right: 0;display: none;}
|
||||
.sc-upload__item-actions span {display: flex;justify-content: center;align-items: center;;width: 25px;height:25px;cursor: pointer;color: #fff;}
|
||||
.sc-upload__item-actions span i {font-size: 12px;}
|
||||
.sc-upload__item-actions .del {background: #F56C6C;}
|
||||
.sc-upload__item-progress {position: absolute;width: 100%;height: 100%;top: 0;left: 0;background-color: var(--el-overlay-color-lighter);}
|
||||
</style>
|
||||
@@ -0,0 +1,265 @@
|
||||
<!--
|
||||
* @Descripttion: 表格选择器组件
|
||||
* @version: 1.2
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年6月10日10:04:07
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年2月28日09:39:03
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-select ref="select" v-model="defaultValue" clearable :multiple="multiple" filterable :placeholder="placeholder" :disabled="disabled" :filter-method="filterMethod" @remove-tag="removeTag" @visible-change="visibleChange" @clear="clear">
|
||||
<template #empty>
|
||||
<div class="sc-table-select__table" :style="{width: tableWidth+'px'}" v-loading="loading">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<el-form :inline="true" :model="formData">
|
||||
<el-form-item>
|
||||
<el-input v-model="formData.name" placeholder="姓名" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="formSubmit">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside width="210px">
|
||||
<div style="height:300px">
|
||||
<el-tree :props="departmentProps" :data="department" node-key="id" @node-click="groupClick"/>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<el-table ref="table" :data="tableData" :height="245" :highlight-current-row="!multiple" @row-click="click" @select="select" @select-all="selectAll">
|
||||
<el-table-column v-if="multiple" type="selection" width="45"></el-table-column>
|
||||
<el-table-column prop="uid" label="ID" width="80"></el-table-column>
|
||||
<el-table-column prop="username" label="用户名" width="150"></el-table-column>
|
||||
<el-table-column prop="nickname" label="姓名" width="150"></el-table-column>
|
||||
</el-table>
|
||||
<div class="sc-table-select__page">
|
||||
<el-pagination small background layout="prev, pager, next" :total="total" :page-size="pageSize" :page-sizes="[100, 200, 300, 400]" v-model:currentPage="currentPage" @current-change="reload"></el-pagination>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "@/config/tableSelect";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: null,
|
||||
params: { type: Object, default: () => {} },
|
||||
placeholder: { type: String, default: "请选择" },
|
||||
multiple: { type: Boolean, default: true },
|
||||
disabled: { type: Boolean, default: false },
|
||||
tableWidth: {type: Number, default: 620},
|
||||
mode: { type: String, default: "popover" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
apiObj: this.$API.user.list,
|
||||
props: {
|
||||
label: 'nickname',
|
||||
value: 'uid',
|
||||
keyword: "name"
|
||||
},
|
||||
//所需数据选项
|
||||
department: [],
|
||||
departmentProps: {
|
||||
value: "id",
|
||||
label: "title"
|
||||
},
|
||||
loading: false,
|
||||
keyword: null,
|
||||
defaultValue: [],
|
||||
tableData: [],
|
||||
pageSize: config.pageSize,
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
defaultProps: {
|
||||
label: config.props.label,
|
||||
value: config.props.value,
|
||||
page: config.request.page,
|
||||
pageSize: config.request.pageSize,
|
||||
keyword: config.request.keyword
|
||||
},
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
modelValue:{
|
||||
handler(){
|
||||
this.defaultValue = this.modelValue
|
||||
this.autoCurrentLabel()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.defaultProps = Object.assign(this.defaultProps, this.props);
|
||||
this.defaultValue = this.modelValue
|
||||
this.autoCurrentLabel()
|
||||
this.getDepartment();
|
||||
},
|
||||
methods: {
|
||||
async getDepartment(){
|
||||
var res = await this.$API.user.department.list.get();
|
||||
this.department = res.data;
|
||||
},
|
||||
groupClick(data){
|
||||
this.formData.department_id = data.id;
|
||||
this.getData();
|
||||
},
|
||||
//表格显示隐藏回调
|
||||
visibleChange(visible){
|
||||
if(visible){
|
||||
this.currentPage = 1
|
||||
this.keyword = null
|
||||
this.formData = {}
|
||||
this.getData()
|
||||
}else{
|
||||
this.autoCurrentLabel()
|
||||
}
|
||||
},
|
||||
//获取表格数据
|
||||
async getData(){
|
||||
this.loading = true;
|
||||
var reqData = {
|
||||
[this.defaultProps.page]: this.currentPage,
|
||||
[this.defaultProps.pageSize]: this.pageSize,
|
||||
[this.defaultProps.keyword]: this.keyword
|
||||
}
|
||||
Object.assign(reqData, this.params, this.formData)
|
||||
var res = await this.apiObj.get(reqData);
|
||||
var parseData = config.parseData(res)
|
||||
this.tableData = parseData.rows;
|
||||
this.total = parseData.total;
|
||||
this.loading = false;
|
||||
//表格默认赋值
|
||||
this.$nextTick(() => {
|
||||
if(this.multiple){
|
||||
this.defaultValue.forEach(row => {
|
||||
var setrow = this.tableData.filter(item => item[this.defaultProps.value]===row[this.defaultProps.value] )
|
||||
if(setrow.length > 0){
|
||||
this.$refs.table.toggleRowSelection(setrow[0], true);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
var setrow = this.tableData.filter(item => item[this.defaultProps.value]===this.defaultValue[this.defaultProps.value] )
|
||||
this.$refs.table.setCurrentRow(setrow[0]);
|
||||
}
|
||||
this.$refs.table.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
|
||||
})
|
||||
},
|
||||
//插糟表单提交
|
||||
formSubmit(){
|
||||
this.currentPage = 1
|
||||
this.keyword = null
|
||||
this.getData()
|
||||
},
|
||||
//分页刷新表格
|
||||
reload(){
|
||||
this.getData()
|
||||
},
|
||||
//自动模拟options赋值
|
||||
autoCurrentLabel(){
|
||||
this.$nextTick(() => {
|
||||
if(this.multiple){
|
||||
this.$refs.select.selected.forEach(item => {
|
||||
item.currentLabel = item.value[this.defaultProps.label]
|
||||
})
|
||||
}else{
|
||||
this.$refs.select.selectedLabel = this.defaultValue[this.defaultProps.label]
|
||||
}
|
||||
})
|
||||
},
|
||||
//表格勾选事件
|
||||
select(rows, row){
|
||||
var isSelect = rows.length && rows.indexOf(row) !== -1
|
||||
if(isSelect){
|
||||
this.defaultValue.push(row)
|
||||
}else{
|
||||
this.defaultValue.splice(this.defaultValue.findIndex(item => item[this.defaultProps.value] == row[this.defaultProps.value]), 1)
|
||||
}
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
},
|
||||
//表格全选事件
|
||||
selectAll(rows){
|
||||
var isAllSelect = rows.length > 0
|
||||
if(isAllSelect){
|
||||
rows.forEach(row => {
|
||||
var isHas = this.defaultValue.find(item => item[this.defaultProps.value] == row[this.defaultProps.value])
|
||||
if(!isHas){
|
||||
this.defaultValue.push(row)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
this.tableData.forEach(row => {
|
||||
var isHas = this.defaultValue.find(item => item[this.defaultProps.value] == row[this.defaultProps.value])
|
||||
if(isHas){
|
||||
this.defaultValue.splice(this.defaultValue.findIndex(item => item[this.defaultProps.value] == row[this.defaultProps.value]), 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
},
|
||||
click(row){
|
||||
if(this.multiple){
|
||||
//处理多选点击行
|
||||
}else{
|
||||
this.defaultValue = row
|
||||
this.$refs.select.blur()
|
||||
this.autoCurrentLabel()
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
this.$emit('change', this.defaultValue);
|
||||
}
|
||||
},
|
||||
//tags删除后回调
|
||||
removeTag(tag){
|
||||
var row = this.findRowByKey(tag[this.defaultProps.value])
|
||||
this.$refs.table.toggleRowSelection(row, false);
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
},
|
||||
//清空后的回调
|
||||
clear(){
|
||||
this.$emit('update:modelValue', this.defaultValue);
|
||||
},
|
||||
// 关键值查询表格数据行
|
||||
findRowByKey (value) {
|
||||
return this.tableData.find(item => item[this.defaultProps.value] === value)
|
||||
},
|
||||
filterMethod(keyword){
|
||||
if(!keyword){
|
||||
this.keyword = null;
|
||||
return false;
|
||||
}
|
||||
this.keyword = keyword;
|
||||
this.getData()
|
||||
},
|
||||
// 触发select隐藏
|
||||
blur(){
|
||||
this.$refs.select.blur();
|
||||
},
|
||||
// 触发select显示
|
||||
focus(){
|
||||
this.$refs.select.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-table-select__table {padding:12px;}
|
||||
.sc-table-select__page {padding-top: 12px;}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
* @Descripttion: xgplayer二次封装
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年11月29日12:10:06
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月30日21:02:50
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-video" ref="scVideo"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Player from 'xgplayer'
|
||||
import HlsPlayer from 'xgplayer-hls'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
src: { type: String, required: true, default: "" },
|
||||
autoplay: { type: Boolean, default: false },
|
||||
controls: { type: Boolean, default: true },
|
||||
loop: { type: Boolean, default: false },
|
||||
isLive: { type: Boolean, default: false },
|
||||
options: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
player: null
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
src(val){
|
||||
if(this.player.hasStart){
|
||||
this.player.src = val
|
||||
}else{
|
||||
this.player.start(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.isLive){
|
||||
this.initHls()
|
||||
}else{
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init(){
|
||||
this.player = new Player({
|
||||
el: this.$refs.scVideo,
|
||||
url: this.src,
|
||||
autoplay: this.autoplay,
|
||||
loop: this.loop,
|
||||
controls: this.controls,
|
||||
fluid: true,
|
||||
lang: 'zh-cn',
|
||||
...this.options
|
||||
})
|
||||
},
|
||||
initHls(){
|
||||
this.player = new HlsPlayer({
|
||||
el: this.$refs.scVideo,
|
||||
url: this.src,
|
||||
autoplay: this.autoplay,
|
||||
loop: this.loop,
|
||||
controls: this.controls,
|
||||
fluid: true,
|
||||
isLive: true,
|
||||
ignores: ['time','progress'],
|
||||
lang: 'zh-cn',
|
||||
...this.options
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-video:deep(.danmu) > * {color: #fff;font-size:20px;font-weight:bold;text-shadow:1px 1px 0 #000,-1px -1px 0 #000,-1px 1px 0 #000,1px -1px 0 #000;}
|
||||
.sc-video:deep(.xgplayer-controls) {background-image: linear-gradient(180deg, transparent, rgba(0,0,0,0.3));}
|
||||
.sc-video:deep(.xgplayer-progress-tip) {border:0;color: #fff;background: rgba(0,0,0,.5);line-height: 25px;padding: 0 10px;border-radius: 25px;}
|
||||
.sc-video:deep(.xgplayer-enter-spinner) {width: 50px;height: 50px;}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!--
|
||||
* @Descripttion: 局部水印组件
|
||||
* @version: 1.1
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年12月18日12:16:16
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年1月5日09:52:59
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-water-mark" ref="scWaterMark">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
text: { type: String, required: true, default: "" },
|
||||
subtext: { type: String, default: "" },
|
||||
color: { type: String, default: "rgba(128,128,128,0.2)" }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.create()
|
||||
},
|
||||
methods: {
|
||||
create(){
|
||||
this.clear()
|
||||
//创建画板
|
||||
var canvas = document.createElement('canvas')
|
||||
canvas.width = 150
|
||||
canvas.height = 150
|
||||
canvas.style.display = 'none'
|
||||
//绘制文字
|
||||
var text = canvas.getContext('2d')
|
||||
text.rotate(-45 * Math.PI / 180)
|
||||
text.translate(-75, 25)
|
||||
text.fillStyle = this.color
|
||||
text.font = "bold 20px SimHei"
|
||||
text.textAlign = "center"
|
||||
text.fillText(this.text, canvas.width / 2, canvas.height / 2)
|
||||
text.font = "14px Microsoft YaHei"
|
||||
text.fillText(this.subtext, canvas.width / 2, canvas.height / 2 + 20)
|
||||
//创建水印容器
|
||||
var watermark = document.createElement('div')
|
||||
watermark.setAttribute('class', 'watermark')
|
||||
const styleStr = `position:absolute;top:0;left:0;right:0;bottom:0;z-index:99;pointer-events:none;background-repeat:repeat;background-image:url('${canvas.toDataURL("image/png")}');`
|
||||
watermark.setAttribute('style', styleStr);
|
||||
this.$refs.scWaterMark.appendChild(watermark)
|
||||
},
|
||||
clear(){
|
||||
var wmDom = this.$refs.scWaterMark.querySelector('.watermark')
|
||||
wmDom && wmDom.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sc-water-mark {position: relative;display: inherit;width: 100%;height: 100%;}
|
||||
</style>
|
||||
@@ -0,0 +1,154 @@
|
||||
<!--
|
||||
* @Descripttion: 仿钉钉流程设计器
|
||||
* @version: 1.3
|
||||
* @Author: sakuya
|
||||
* @Date: 2021年9月14日08:38:35
|
||||
* @LastEditors: sakuya
|
||||
* @LastEditTime: 2022年5月14日19:43:46
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="sc-workflow-design">
|
||||
<div class="box-scale">
|
||||
<node-wrap v-if="nodeConfig" v-model="nodeConfig"></node-wrap>
|
||||
<div class="end-node">
|
||||
<div class="end-node-circle"></div>
|
||||
<div class="end-node-text">流程结束</div>
|
||||
</div>
|
||||
</div>
|
||||
<use-select v-if="selectVisible" ref="useselect" @closed="selectVisible=false"></use-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import nodeWrap from './nodeWrap'
|
||||
import useSelect from './select'
|
||||
|
||||
export default {
|
||||
provide(){
|
||||
return {
|
||||
select: this.selectHandle
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} }
|
||||
},
|
||||
components: {
|
||||
nodeWrap,
|
||||
useSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeConfig: this.modelValue,
|
||||
selectVisible: false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
this.nodeConfig = val
|
||||
},
|
||||
nodeConfig(val){
|
||||
this.$emit("update:modelValue", val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
selectHandle(type, data){
|
||||
this.selectVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.useselect.open(type, data)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.sc-workflow-design {width: 100%;}
|
||||
.sc-workflow-design .box-scale {display: inline-block;position: relative;width: 100%;padding: 54.5px 0px;align-items: flex-start;justify-content: center;flex-wrap: wrap;min-width: min-content;}
|
||||
|
||||
.sc-workflow-design {
|
||||
.node-wrap {display: inline-flex;width: 100%;flex-flow: column wrap;justify-content: flex-start;align-items: center;padding: 0px 50px;position: relative;z-index: 1;}
|
||||
.node-wrap-box {display: inline-flex;flex-direction: column;position: relative;width: 220px;min-height: 72px;flex-shrink: 0;background: rgb(255, 255, 255);border-radius: 4px;cursor: pointer;box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);}
|
||||
.node-wrap-box::before {content: "";position: absolute;top: -12px;left: 50%;transform: translateX(-50%);width: 0px;border-style: solid;border-width: 8px 6px 4px;border-color: rgb(202, 202, 202) transparent transparent;background: #f6f8f9;}
|
||||
.node-wrap-box.start-node:before {content: none}
|
||||
.node-wrap-box .title {height:24px;line-height: 24px;color: #fff;padding-left: 16px;padding-right: 30px;border-radius: 4px 4px 0 0;position: relative;display: flex;align-items: center;}
|
||||
.node-wrap-box .title .icon {margin-right: 5px;}
|
||||
.node-wrap-box .title .close {font-size: 15px;position: absolute;top:50%;transform: translateY(-50%);right:10px;display: none;}
|
||||
.node-wrap-box .content {position: relative;padding: 15px;}
|
||||
.node-wrap-box .content .placeholder {color: #999;}
|
||||
.node-wrap-box:hover .close {display: block;}
|
||||
.add-node-btn-box {width: 240px;display: inline-flex;flex-shrink: 0;position: relative;z-index: 1;}
|
||||
.add-node-btn-box:before {content: "";position: absolute;top: 0px;left: 0px;right: 0px;bottom: 0px;z-index: -1;margin: auto;width: 2px;height: 100%;background-color: rgb(202, 202, 202);}
|
||||
.add-node-btn {user-select: none;width: 240px;padding: 20px 0px 32px;display: flex;justify-content: center;flex-shrink: 0;flex-grow: 1;}
|
||||
.add-node-btn span {}
|
||||
.add-branch {justify-content: center;padding: 0px 10px;position: absolute;top: -16px;left: 50%;transform: translateX(-50%);transform-origin: center center;z-index: 1;display: inline-flex;align-items: center;}
|
||||
.branch-wrap {display: inline-flex;width: 100%;}
|
||||
.branch-box-wrap {display: flex;flex-flow: column wrap;align-items: center;min-height: 270px;width: 100%;flex-shrink: 0;}
|
||||
.col-box {display: inline-flex;flex-direction: column;align-items: center;position: relative;background: #f6f8f9;}
|
||||
.branch-box {display: flex;overflow: visible;min-height: 180px;height: auto;border-bottom: 2px solid #ccc;border-top: 2px solid #ccc;position: relative;margin-top: 15px;}
|
||||
.branch-box .col-box::before {content: "";position: absolute;top: 0px;left: 0px;right: 0px;bottom: 0px;z-index: 0;margin: auto;width: 2px;height: 100%;background-color: rgb(202, 202, 202);}
|
||||
.condition-node {display: inline-flex;flex-direction: column;min-height: 220px;}
|
||||
.condition-node-box {padding-top: 30px;padding-right: 50px;padding-left: 50px;justify-content: center;align-items: center;flex-grow: 1;position: relative;display: inline-flex;flex-direction: column;}
|
||||
.condition-node-box::before {content: "";position: absolute;top: 0px;left: 0px;right: 0px;bottom: 0px;margin: auto;width: 2px;height: 100%;background-color: rgb(202, 202, 202);}
|
||||
.auto-judge {position: relative;width: 220px;min-height: 72px;background: rgb(255, 255, 255);border-radius: 4px;padding: 15px 15px;cursor: pointer;box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);}
|
||||
.auto-judge::before {content: "";position: absolute;top: -12px;left: 50%;transform: translateX(-50%);width: 0px;border-style: solid;border-width: 8px 6px 4px;border-color: rgb(202, 202, 202) transparent transparent;background: rgb(245, 245, 247);}
|
||||
.auto-judge .title {line-height: 16px;}
|
||||
.auto-judge .title .node-title {color: #15BC83;}
|
||||
.auto-judge .title .close {font-size: 15px;position: absolute;top:15px;right:15px;color: #999;display: none;}
|
||||
.auto-judge .title .priority-title {position: absolute;top:15px;right:15px;color: #999;}
|
||||
.auto-judge .content {position: relative;padding-top: 15px;}
|
||||
.auto-judge .content .placeholder {color: #999;}
|
||||
.auto-judge:hover {
|
||||
.close {display: block;}
|
||||
.priority-title {display: none;}
|
||||
}
|
||||
.top-left-cover-line, .top-right-cover-line {position: absolute;height: 3px;width: 50%;background-color: #f6f8f9;top: -2px;}
|
||||
.bottom-left-cover-line, .bottom-right-cover-line {position: absolute;height: 3px;width: 50%;background-color: #f6f8f9;bottom: -2px;}
|
||||
.top-left-cover-line {left: -1px;}
|
||||
.top-right-cover-line {right: -1px;}
|
||||
.bottom-left-cover-line {left: -1px;}
|
||||
.bottom-right-cover-line {right: -1px;}
|
||||
.end-node {border-radius: 50%;font-size: 14px;color: rgba(25,31,37,.4);text-align: left;}
|
||||
.end-node-circle {width: 10px;height: 10px;margin: auto;border-radius: 50%;background: #dbdcdc;}
|
||||
.end-node-text {margin-top: 5px;text-align: center;}
|
||||
.auto-judge:hover {
|
||||
.sort-left {display: flex;}
|
||||
.sort-right {display: flex;}
|
||||
}
|
||||
.auto-judge .sort-left {position: absolute;top: 0;bottom: 0;z-index: 1;left: 0;display: none;justify-content: center;align-items: center;flex-direction: column;}
|
||||
.auto-judge .sort-right {position: absolute;top: 0;bottom: 0;z-index: 1;right: 0;display: none;justify-content: center;align-items: center;flex-direction: column;}
|
||||
.auto-judge .sort-left:hover, .auto-judge .sort-right:hover {background: #eee;}
|
||||
.auto-judge:after {pointer-events: none;content: "";position: absolute;top:0;bottom:0;left:0;right:0;z-index: 2;border-radius: 4px;transition: all .1s;}
|
||||
.auto-judge:hover:after {border: 1px solid #3296fa;box-shadow: 0 0 6px 0 rgba(50,150,250,.3);}
|
||||
.node-wrap-box:after {pointer-events: none;content: "";position: absolute;top:0;bottom:0;left:0;right:0;z-index: 2;border-radius: 4px;transition: all .1s;}
|
||||
.node-wrap-box:hover:after {border: 1px solid #3296fa;box-shadow: 0 0 6px 0 rgba(50,150,250,.3);}
|
||||
}
|
||||
|
||||
.tags-list {margin-top: 15px;width: 100%;}
|
||||
.add-node-popover-body {}
|
||||
.add-node-popover-body li {display: inline-block;width: 80px;text-align: center;padding:10px 0;}
|
||||
.add-node-popover-body li i {border: 1px solid var(--el-border-color-light);width:40px;height:40px;border-radius: 50%;text-align: center;line-height: 38px;font-size: 18px;cursor: pointer;}
|
||||
.add-node-popover-body li i:hover {border: 1px solid #3296fa;background: #3296fa;color: #fff!important;}
|
||||
.add-node-popover-body li p {font-size: 12px;margin-top: 5px;}
|
||||
.node-wrap-drawer__title {padding-right:40px;}
|
||||
.node-wrap-drawer__title label {cursor: pointer;}
|
||||
.node-wrap-drawer__title label:hover {border-bottom: 1px dashed #409eff;}
|
||||
.node-wrap-drawer__title .node-wrap-drawer__title-edit {color: #409eff;margin-left: 10px;vertical-align: middle;}
|
||||
|
||||
.dark .sc-workflow-design {
|
||||
.node-wrap-box,.auto-judge {background: #2b2b2b;}
|
||||
.col-box {background: var(--el-bg-color);}
|
||||
.top-left-cover-line,
|
||||
.top-right-cover-line,
|
||||
.bottom-left-cover-line,
|
||||
.bottom-right-cover-line {background-color: var(--el-bg-color);}
|
||||
.node-wrap-box::before,.auto-judge::before {background-color: var(--el-bg-color);}
|
||||
.branch-box .add-branch {background: var(--el-bg-color);}
|
||||
.end-node .end-node-text {color: #d0d0d0;}
|
||||
.auto-judge .sort-left:hover, .auto-judge .sort-right:hover {background: var(--el-bg-color);}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<promoter v-if="nodeConfig.type==0" v-model="nodeConfig"></promoter>
|
||||
|
||||
<approver v-if="nodeConfig.type==1" v-model="nodeConfig"></approver>
|
||||
|
||||
<send v-if="nodeConfig.type==2" v-model="nodeConfig"></send>
|
||||
|
||||
<branch v-if="nodeConfig.type==4" v-model="nodeConfig">
|
||||
<template v-slot="slot">
|
||||
<node-wrap v-if="slot.node" v-model="slot.node.childNode"></node-wrap>
|
||||
</template>
|
||||
</branch>
|
||||
|
||||
<node-wrap v-if="nodeConfig.childNode" v-model="nodeConfig.childNode"></node-wrap>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import approver from './nodes/approver'
|
||||
import promoter from './nodes/promoter'
|
||||
import branch from './nodes/branch'
|
||||
import send from './nodes/send'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} }
|
||||
},
|
||||
components: {
|
||||
approver,
|
||||
promoter,
|
||||
branch,
|
||||
send
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeConfig: {},
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(val){
|
||||
this.nodeConfig = val
|
||||
},
|
||||
nodeConfig(val){
|
||||
this.$emit("update:modelValue", val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.nodeConfig = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="add-node-btn-box">
|
||||
<div class="add-node-btn">
|
||||
<el-popover placement="right-start" :width="270" trigger="click" :hide-after="0" :show-after="0">
|
||||
<template #reference>
|
||||
<el-button type="primary" icon="el-icon-plus" circle></el-button>
|
||||
</template>
|
||||
<div class="add-node-popover-body">
|
||||
<ul>
|
||||
<li>
|
||||
<el-icon style="color: #ff943e;" @click="addType(1)"><el-icon-user-filled /></el-icon>
|
||||
<p>审批节点</p>
|
||||
</li>
|
||||
<li>
|
||||
<el-icon style="color: #3296fa;" @click="addType(2)"><el-icon-promotion /></el-icon>
|
||||
<p>抄送节点</p>
|
||||
</li>
|
||||
<li>
|
||||
<el-icon style="color: #15BC83;" @click="addType(4)"><el-icon-share /></el-icon>
|
||||
<p>条件分支</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
addType(type){
|
||||
var node = {}
|
||||
if (type == 1) {
|
||||
node = {
|
||||
nodeName: "审核人",
|
||||
type: 1, //节点类型
|
||||
setType: 1, //审核人类型
|
||||
nodeUserList: [], //审核人成员
|
||||
nodeRoleList: [], //审核角色
|
||||
examineLevel: 1, //指定主管层级
|
||||
directorLevel: 1, //自定义连续主管审批层级
|
||||
selectMode: 1, //发起人自选类型
|
||||
termAuto: false, //审批期限超时自动审批
|
||||
term: 0, //审批期限
|
||||
termMode: 1, //审批期限超时后执行类型
|
||||
examineMode: 1, //多人审批时审批方式
|
||||
directorMode: 0, //连续主管审批方式
|
||||
childNode: this.modelValue
|
||||
}
|
||||
}else if(type == 2){
|
||||
node = {
|
||||
nodeName: "抄送人",
|
||||
type: 2,
|
||||
userSelectFlag: true,
|
||||
nodeUserList: [],
|
||||
childNode: this.modelValue
|
||||
}
|
||||
|
||||
}else if(type == 4){
|
||||
node = {
|
||||
nodeName: "条件路由",
|
||||
type: 4,
|
||||
conditionNodes: [
|
||||
{
|
||||
nodeName: "条件1",
|
||||
type: 3,
|
||||
priorityLevel: 1,
|
||||
conditionMode: 1,
|
||||
conditionList: []
|
||||
},
|
||||
{
|
||||
nodeName: "条件2",
|
||||
type: 3,
|
||||
priorityLevel: 2,
|
||||
conditionMode: 1,
|
||||
conditionList: []
|
||||
}
|
||||
],
|
||||
childNode: this.modelValue
|
||||
}
|
||||
|
||||
}
|
||||
this.$emit("update:modelValue", node)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="node-wrap">
|
||||
<div class="node-wrap-box" @click="show">
|
||||
<div class="title" style="background: #ff943e;">
|
||||
<el-icon class="icon"><el-icon-user-filled /></el-icon>
|
||||
<span>{{ nodeConfig.nodeName }}</span>
|
||||
<el-icon class="close" @click.stop="delNode()"><el-icon-close /></el-icon>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span v-if="toText(nodeConfig)">{{ toText(nodeConfig) }}</span>
|
||||
<span v-else class="placeholder">请选择</span>
|
||||
</div>
|
||||
</div>
|
||||
<add-node v-model="nodeConfig.childNode"></add-node>
|
||||
<el-drawer title="审批人设置" v-model="drawer" destroy-on-close append-to-body :size="500">
|
||||
<template #header>
|
||||
<div class="node-wrap-drawer__title">
|
||||
<label @click="editTitle" v-if="!isEditTitle">{{form.nodeName}}<el-icon class="node-wrap-drawer__title-edit"><el-icon-edit /></el-icon></label>
|
||||
<el-input v-if="isEditTitle" ref="nodeTitle" v-model="form.nodeName" clearable @blur="saveTitle" @keyup.enter="saveTitle"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
<el-container>
|
||||
<el-main style="padding:0 20px 20px 20px">
|
||||
<el-form label-position="top">
|
||||
|
||||
<el-form-item label="审批人员类型">
|
||||
<el-select v-model="form.setType">
|
||||
<el-option :value="1" label="指定成员"></el-option>
|
||||
<el-option :value="2" label="主管"></el-option>
|
||||
<el-option :value="3" label="角色"></el-option>
|
||||
<el-option :value="4" label="发起人自选"></el-option>
|
||||
<el-option :value="5" label="发起人自己"></el-option>
|
||||
<el-option :value="7" label="连续多级主管"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.setType==1" label="选择成员">
|
||||
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(1, form.nodeUserList)">选择人员</el-button>
|
||||
<div class="tags-list">
|
||||
<el-tag v-for="(user, index) in form.nodeUserList" :key="user.id" closable @close="delUser(index)">{{user.name}}</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.setType==2" label="指定主管">
|
||||
发起人的第 <el-input-number v-model="form.examineLevel" :min="1"/> 级主管
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.setType==3" label="选择角色">
|
||||
<el-button type="primary" icon="el-icon-plus" round @click="selectHandle(2, form.nodeRoleList)">选择角色</el-button>
|
||||
<div class="tags-list">
|
||||
<el-tag v-for="(role, index) in form.nodeRoleList" :key="role.id" type="info" closable @close="delRole(index)">{{role.name}}</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.setType==4" label="发起人自选">
|
||||
<el-radio-group v-model="form.selectMode">
|
||||
<el-radio :label="1">自选一个人</el-radio>
|
||||
<el-radio :label="2">自选多个人</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.setType==7" label="连续主管审批终点">
|
||||
<el-radio-group v-model="form.directorMode">
|
||||
<el-radio :label="0">直到最上层主管</el-radio>
|
||||
<el-radio :label="1">自定义审批终点</el-radio>
|
||||
</el-radio-group>
|
||||
<p v-if="form.directorMode==1">直到发起人的第 <el-input-number v-model="form.directorLevel" :min="1"/> 级主管</p>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider></el-divider>
|
||||
<el-form-item label="">
|
||||
<el-checkbox v-model="form.termAuto" label="超时自动审批"></el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.termAuto">
|
||||
<el-form-item label="审批期限(为 0 则不生效)">
|
||||
<el-input-number v-model="form.term" :min="0"/> 小时
|
||||
</el-form-item>
|
||||
<el-form-item label="审批期限超时后执行">
|
||||
<el-radio-group v-model="form.termMode">
|
||||
<el-radio :label="0">自动通过</el-radio>
|
||||
<el-radio :label="1">自动拒绝</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-divider></el-divider>
|
||||
<el-form-item label="多人审批时审批方式">
|
||||
<el-radio-group v-model="form.examineMode">
|
||||
<p style="width: 100%;"><el-radio :label="1">按顺序依次审批</el-radio></p>
|
||||
<p style="width: 100%;"><el-radio :label="2">会签 (可同时审批,每个人必须审批通过)</el-radio></p>
|
||||
<p style="width: 100%;"><el-radio :label="3">或签 (有一人审批通过即可)</el-radio></p>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-main>
|
||||
<el-footer>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
<el-button @click="drawer=false">取消</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNode from './addNode'
|
||||
|
||||
export default {
|
||||
inject: ['select'],
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} }
|
||||
},
|
||||
components: {
|
||||
addNode
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeConfig: {},
|
||||
drawer: false,
|
||||
isEditTitle: false,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
modelValue(){
|
||||
this.nodeConfig = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.nodeConfig = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
show(){
|
||||
this.form = {}
|
||||
this.form = JSON.parse(JSON.stringify(this.nodeConfig))
|
||||
this.drawer = true
|
||||
},
|
||||
editTitle(){
|
||||
this.isEditTitle = true
|
||||
this.$nextTick(()=>{
|
||||
this.$refs.nodeTitle.focus()
|
||||
})
|
||||
},
|
||||
saveTitle(){
|
||||
this.isEditTitle = false
|
||||
},
|
||||
save(){
|
||||
this.$emit("update:modelValue", this.form)
|
||||
this.drawer = false
|
||||
},
|
||||
delNode(){
|
||||
this.$emit("update:modelValue", this.nodeConfig.childNode)
|
||||
},
|
||||
delUser(index){
|
||||
this.form.nodeUserList.splice(index, 1)
|
||||
},
|
||||
delRole(index){
|
||||
this.form.nodeRoleList.splice(index, 1)
|
||||
},
|
||||
selectHandle(type, data){
|
||||
this.select(type, data)
|
||||
},
|
||||
toText(nodeConfig){
|
||||
if(nodeConfig.setType == 1){
|
||||
if (nodeConfig.nodeUserList && nodeConfig.nodeUserList.length>0) {
|
||||
const users = nodeConfig.nodeUserList.map(item=>item.name).join("、")
|
||||
return users
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}else if (nodeConfig.setType == 2) {
|
||||
return nodeConfig.examineLevel == 1 ? '直接主管' : `发起人的第${nodeConfig.examineLevel}级主管`
|
||||
}else if (nodeConfig.setType == 3) {
|
||||
if (nodeConfig.nodeRoleList && nodeConfig.nodeRoleList.length>0) {
|
||||
const roles = nodeConfig.nodeRoleList.map(item=>item.name).join("、")
|
||||
return '角色-' + roles
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}else if (nodeConfig.setType == 4) {
|
||||
return "发起人自选"
|
||||
}else if (nodeConfig.setType == 5) {
|
||||
return "发起人自己"
|
||||
}else if (nodeConfig.setType == 7) {
|
||||
return "连续多级主管"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||