提交 346cbf07 authored 作者: HectorLi's avatar HectorLi

init

上级 9420e96e
docs
public
src
.dockerignore
.editorconfig
.eslintignore
.gitattributes
.gitignore
.prettierrc
babel.config.js
Dockerfile
idea.config.js
LICENSE
package.json
package-lock.json
README.md
vue.config.js
yarn
yarn.lock
yarn-error.log
.idea
.svn
node_modules
\ No newline at end of file
[*]
charset=utf-8
end_of_line=crlf
insert_final_newline=false
indent_style=space
indent_size=2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style=space
indent_size=2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style=space
indent_size=2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2
[*.svg]
indent_style=space
indent_size=2
[*.js.map]
indent_style=space
indent_size=2
[*.less]
indent_style=space
indent_size=2
[*.vue]
indent_style=space
indent_size=2
[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2
/src
\ No newline at end of file
public/* linguist-vendored
\ No newline at end of file
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
{
"printWidth": 120,
"semi": false,
"singleQuote": true
}
FROM nginx
VOLUME /tmp
ENV LANG en_US.UTF-8
RUN echo "\
# gzip config\
gzip on;\
gzip_min_length 1k;\
gzip_comp_level 9;\
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;\
gzip_vary on;\
gzip_disable "MSIE [1-6]\.";\
server { \
listen 80; \
location ^~ /wmssystem { \
proxy_pass http://127.0.0.1:18686/wmssystem/; \
proxy_set_header Host 127.0.0.1; \
proxy_set_header X-Real-IP \$remote_addr; \
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; \
} \
#解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题 \
location / { \
root /var/www/html/; \
index index.html index.htm; \
if (!-e \$request_filename) { \
rewrite ^(.*)\$ /index.html?s=\$1 last; \
break; \
} \
} \
access_log /var/log/nginx/access.log ; \
} " > /etc/nginx/conf.d/default.conf \
&& mkdir -p /var/www \
&& mkdir -p /var/www/html
ADD dist/ /var/www/html/
EXPOSE 80
EXPOSE 443
\ No newline at end of file
MIT License
Copyright (c) 2019 DaiHao Zhang
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.
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<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">
<title>仓库管理系统</title>
<link rel="icon" href="<%= BASE_URL %>logo.png">
<script src="/cdn/babel-polyfill/polyfill_7_2_5.js"></script>
<style>
html,
body,
#app {
height: 100%;
margin: 0px;
padding: 0px;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 120px;
height: 120px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 1 */
border-top-color: #FFF;
-webkit-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 2s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 2 */
border-top-color: #FFF;
-webkit-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 3s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
/* COLOR 3 */
-moz-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-webkit-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 1.5s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #49a9ee;
/* Old browsers */
z-index: 1000;
-webkit-transform: translateX(0);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(0);
/* IE 9 */
transform: translateX(0);
/* Firefox 16+, IE 10+, Opera */
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
/* Loaded */
.loaded #loader-wrapper .loader-section.section-left {
-webkit-transform: translateX(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(-100%);
/* IE 9 */
transform: translateX(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: translateX(100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(100%);
/* IE 9 */
transform: translateX(100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateY(-100%);
/* IE 9 */
transform: translateY(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
/* JavaScript Turned Off */
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#loader-wrapper .load_title {
font-family: 'Open Sans';
color: #FFF;
font-size: 14px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
#loader-wrapper .load_title span {
font-weight: normal;
font-style: italic;
font-size: 14px;
color: #FFF;
opacity: 0.5;
}
/* 滚动条优化 start */
::-webkit-scrollbar{
width:8px;
height:8px;
}
::-webkit-scrollbar-track{
background: #f6f6f6;
border-radius:2px;
}
::-webkit-scrollbar-thumb{
background: #cdcdcd;
border-radius:2px;
}
::-webkit-scrollbar-thumb:hover{
background: #747474;
}
::-webkit-scrollbar-corner {
background: #f6f6f6;
}
/* 滚动条优化 end */
</style>
<!-- 全局配置 -->
<script>
window._CONFIG = {};
window._CONFIG['domianURL'] = 'http://lf.lingqingkeji.com:8080/wmssystem';
window._CONFIG['casPrefixUrl'] = 'http://cas.example.org:8443/cas';
window._CONFIG['onlinePreviewDomainURL'] = 'http://fileview.jeecg.com/onlinePreview'
window._CONFIG['staticDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/static';
//window._CONFIG['downloadUrl'] = window._CONFIG['domianURL'] + '/sys/common/download';
window._CONFIG['pdfDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/pdf/pdfPreviewIframe';
</script>
</head>
<body>
<!-- built files will be auto injected -->
<div id="app">
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载,请耐心等待
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
差异被折叠。
差异被折叠。
差异被折叠。
!function(){"use strict";function e(e){return e===undefined||null===e}function t(e){return e!==undefined&&null!==e}function n(e){return null!==e&&"object"===(void 0===e?"undefined":d(e))}function r(e){return"object"===(void 0===e?"undefined":d(e))&&e instanceof HTMLElement}function o(e){var t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:""}function i(){return new RegExp("MSIE (\\d+\\.\\d+);").test(navigator.userAgent),parseFloat(RegExp.$1)||Infinity}function a(e,t){for(var n in t)e[n]=t[n];return e}function c(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function u(e){return p.call(e).slice(8,-1)}function s(e){throw new Error("Vaptcha error: "+e)}function l(e){var t=e.mode,n=e.outage;return!!t&&"downtime"===(t=t.toLowerCase())&&!!n}function Promise(e){var t=this;this.state="pending",this.value=undefined,this.reason=undefined,this.onResolveAsyncCallbacks=[],this.onRejectAsyncCallbacks=[];var n=function(e){"pending"===t.state&&(t.state="fulfilled",t.value=e,t.onResolveAsyncCallbacks.map(function(e){return e()}))},r=function(e){"pending"===t.state&&(t.state="rejected",t.reason=e,t.onRejectAsyncCallbacks.map(function(t){return t(e)}))};try{e(n,r)}catch(o){r(o)}}window.HTMLElement=window.HTMLElement||Element,Array.prototype.map||(Array.prototype.map=function(e,t){var n,r,o;if(null==this)throw new TypeError(" this is null or not defined");var i=Object(this),a=i.length>>>0;if("[object Function]"!=Object.prototype.toString.call(e))throw new TypeError(e+" is not a function");for(t&&(n=t),r=new Array(a),o=0;o<a;){var c,u;o in i&&(c=i[o],u=e.call(n,c,o,i),r[o]=u),o++}return r}),Array.prototype.includes||(Array.prototype.includes=function(e,t){if(null==this)throw new TypeError('"this" is null or not defined');var n=Object(this),r=n.length>>>0;if(0===r)return!1;for(var o=0|t,i=Math.max(o>=0?o:r-Math.abs(o),0);i<r;){if(n[i]===e)return!0;i++}return!1}),Array.prototype.findIndex||(Array.prototype.findIndex=function(e){if(null==this)throw new TypeError('"this" is null or not defined');var t=Object(this),n=t.length>>>0;if("function"!=typeof e)throw new TypeError("predicate must be a function");for(var r=arguments[1],o=0;o<n;){if(e.call(r,t[o],o,t))return o;o++}return-1}),Object.create||(Object.create=function(e){var t=function(){};return t.prototype=e,new t});var f={vid:null,scene:"",container:null,type:"float",style:"dark",lang:"zh-CN",ai:!0,https:!0,guide:!0,aiAnimation:!0,protocol:"https://",css_version:"downtime",cdn_servers:["cdn.vaptcha.com"],api_server:"api.vaptcha.com/v2",canvas_path:"/canvas.min.js"},d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p=Object.prototype.toString,h=(c(function(e){for(var t={},n=e&&-1!==e.indexOf("?")&&e.split("?")[1]||window.location.search.substring(1),r=n.split("&"),o=0;o<r.length;o++){var i=r[o].split("=");t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return t}),c(function(e){return e.charAt(0).toUpperCase()+e.slice(1)})),v=function(t){this.data=t,this.valiudateFuns=[],this.ruleFuns={required:function(t,n){return e(t)||0===t.length?n:null}}};v.prototype={constructor:v,addValidateRules:function(e){a(this.ruleFuns,e)},add:function(e,t,n){var r=this,o=t.split(":"),i=o.shift(),a=this.ruleFuns[i];o.unshift(this.data[e]),o.push(n),a&&this.valiudateFuns.push(function(){return a.apply(r,o)})},validate:function(){for(var e,t=0;e=this.valiudateFuns[t++];){var n=e();if(n)return s(n),!1}return!0}};var m={AccessDenied:"0101",RefreshAgain:"0102",Success:"0103",Fail:"0104",RefreshTooFast:"0105",RefreshTanto:"0106",DrawTanto:"0107",Attack:"0108",jsonpTimeOut:"0703",challengeExpire:"1002"};Promise.prototype.then=function(e){var t=this;if("fulfilled"===this.state){var r=e(this.value);if(n(r)&&"Promise"===o(r.constructor))return r}return"pending"===this.state?new Promise(function(r){t.onResolveAsyncCallbacks.push(function(){var i=e(t.value);if(n(i)&&"Promise"===o(i.constructor))return i.then(r);r(i)})}):this},Promise.prototype["catch"]=function(e){return"rejected"===this.state&&e(this.reason),"pending"===this.state&&this.onRejectAsyncCallbacks.push(e),this},Promise.resolve=function(e){return new Promise(function(t){t(e)})},Promise.reject=function(e){return new Promise(function(t,n){n(e)})};var y=function(){var e=f.protocol,t=f.api_server,n=function(e){var t="";for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+="&"+n+"="+encodeURIComponent(e[n]));return t},r=function(r,o){var i=n(o),a=r.indexOf("http://")>-1||r.indexOf("https://")>-1;return r.indexOf("?")<0&&(i="?"+i.slice(1)),a?""+r+i:""+e+t+r+i},o=function(e){var t=document.getElementsByTagName("head")[0],n=document.createElement("script");return n.charset="UTF-8",n.src=e,t.appendChild(n),{remove:function(){t.removeChild(n)}}},i=function(e,t){return t=t||{},new Promise(function(n){var i="VaptchaJsonp"+(new Date).valueOf();window[i]&&(i+="1"),a(t,{callback:i}),e=r(e,t);var c=o(e),u=setTimeout(function(){clearTimeout(u),window[i]=null,c.remove(),n({code:"0703",msg:"Time out,Refresh Again!"})},1e4);window[i]=function(){clearTimeout(u),n.apply(this,arguments),c.remove(),window[i]=null}})};return i.setConfig=function(n){e=n.protocol||e,t=n.api_server||t},i}(),g={getConfig:function(e){return y("/config",{id:e.vid,type:e.type,scene:e.scene||""})},refresh:function(e){return y("/refresh",e)},click:function(e){return y("/click",e)},get:function(e){return y("/get",e)},verify:function(e){return y("/verify",e)},userbehavior:function(e){return y("/userbehavior",e)}},w={"0201":"id empty","0202":"id error","0208":"scene error","0209":"request used up","0906":"params error","0702":"domain not match"},b=function(){var o=!1,c=function(e){var t=new v(e);return t.add("outage","required","please configure outage"),t.validate(),a(e,{js_path:"vaptcha-sdk-downtime.2.0.2.js",api_server:window.location.host,protocol:window.location.protocol+"//",mode:"DownTime"}),y.setConfig(e),y(e.outage,{action:"get"}).then(function(t){return t.code!==m.Success?(s(w[t.msg]||t.msg),Promise.reject(t.code)):(a(e,t),Promise.resolve())})},f=function(e){return l(e)?c(e):g.getConfig(e).then(function(t){return t.code!==m.Success?(s(w[t.msg]||t.msg),Promise.reject(t.code)):(t.type!==e.type&&(t.mode=t.type,t.type=e.type),a(e,t),l(e)?c(e):Promise.resolve())})},d=function(e,t){return""+e.protocol+e.cdn_servers[0]+"/"+t},p=function(t){var n=document.getElementsByTagName("head")[0],r=document.getElementById("vaptcha_style");return new Promise(function(o){e(r)?(r=document.createElement("link"),a(r,{rel:"stylesheet",type:"text/css",href:t,id:"vaptcha_style",onload:o}),n&&n.appendChild(r)):o()})},b=function C(e){var n=document.getElementsByTagName("head")[0],r=document.querySelector("script[src='"+e+"']");return new Promise(function(o){if(t(r))return void(r.loaded?o():setTimeout(function(){return C(e).then(o)}));r=document.createElement("script");var i=function(){r.readyState&&"loaded"!==r.readyState&&"complete"!==r.readyState||(o(),r.loaded=!0,r.onload=null,r.onreadystatechange=null)};a(r,{async:!0,charset:"utf-8",src:e,onerror:function(){return s("load sdk timeout")},onload:i,onreadystatechange:i}),n.appendChild(r)})},j=function T(e){var t=e.sdkName,n=e.config,r=d(n,n.js_path);return b(r).then(function(){var e=window["_"+h(t)+"Vaptcha"],r=new e(n);return r.vaptcha.resetCaptcha=function(e,t){a(n,t),T({sdkName:e,config:n}).then(function(e){r.destroy(),r.options=e.options,r.vaptcha=e.vaptcha,e.render(),"character"===n.mode&&["click","float","popup"].includes(n.type)&&e.vaptcha.dtClickCb({target:e.vaptcha.btnDiv})})},Promise.resolve(r)})},A=function(e){o=!0,e.https=!0,e.protocol="https://",y.setConfig(e),!["embed","popup","invisible"].includes(e.type)&&(e.type="popup"),i()<9&&b(d(e,e.canvas_path));var t=new v(e);if(t.addValidateRules({elementOrSelector:function(t,o){if("String"===u(e.container)&&(e.container=document.querySelector(e.container)),n(e.container)&&r(e.container[0])&&(e.container=e.container[0]),!r(e.container))return o}}),t.add("vid","required","please configure vid"),"invisible"!==e.type&&t.add("container","elementOrSelector","please configure container with element or selector"),t.validate())return f(e).then(function(){var t="theme_https."+e.css_version+".css",n=d(e,t);return p(n)}).then(function(){var t=e.mode||e.type;return o=!1,j({sdkName:t,config:e})})};return function S(e){return new Promise(function(t){o?setTimeout(function(){S(e).then(t)},1e3):A(e).then(t)})["catch"](function(e){return o=!1,s(e),Promise.reject(e)})}}(),j=function(){var e=function(e){var n=e.getAttribute("data-config"),r={};if(t(n))try{r=JSON.parse(n)}catch(o){s("dom config format error")}return r},n=function(e){var n=e.getAttribute("data-vid");return t(n)?{vid:n}:{}},r=function(e,n){var r=Object.create(f);r.container=e,a(r,n),t(r.vid)&&b(r).then(function(e){e.renderTokenInput(),e.render()})};return function(){for(var t=document.querySelectorAll("[data-vid]"),o=document.querySelectorAll("[data-config]"),i=0;i<o.length;i++){var a=e(o[i]);r(o[i],a)}for(var c=0;c<t.length;c++)if(!Array.prototype.includes.call(o,t[c])){var u=n(t[c]);r(t[c],u)}}}();window.onload=j,window.vaptcha=function(e){var t=Object.create(f);return a(t,e),b(t)}}();
\ No newline at end of file
<template>
<a-config-provider :locale="locale">
<div id="app">
<router-view/>
</div>
</a-config-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import enquireScreen from '@/utils/device'
export default {
data () {
return {
locale: zhCN,
}
},
created () {
let that = this
enquireScreen(deviceType => {
// tablet
if (deviceType === 0) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
}
// mobile
else if (deviceType === 1) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
}
else {
that.$store.commit('TOGGLE_DEVICE', 'desktop')
that.$store.dispatch('setSidebar', true)
}
})
}
}
</script>
<style>
#app {
height: 100%;
}
</style>
\ No newline at end of file
import Vue from 'vue'
/**
* 将一个请求分组
*
* @param getPromise 传入一个可以获取到Promise对象的方法
* @param groupId 分组ID,如果不传或者为空则不分组
* @param expire 过期时间,默认 半分钟
*/
export function httpGroupRequest(getPromise, groupId, expire = 1000 * 30) {
if (groupId == null || groupId === '') {
console.log("--------popup----------getFrom DB-------with---no--groupId ")
return getPromise()
}
if (Vue.ls.get(groupId)) {
console.log("---------popup--------getFrom Cache--------groupId = " + groupId)
return Promise.resolve(Vue.ls.get(groupId));
} else {
console.log("--------popup----------getFrom DB---------groupId = " + groupId)
}
// 还没有发出请求,就发出第一次的请求
return getPromise().then(res => {
Vue.ls.set(groupId, res, expire);
return Promise.resolve(res);
})
}
import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
import Vue from 'vue'
import {UI_CACHE_DB_DICT_DATA } from "@/store/mutation-types"
////根路径
// const doMian = "/wmssystem/";
////图片预览请求地址
// const imgView = "http://localhost:8080/wmssystem/sys/common/view/";
//角色管理
const addRole = (params)=>postAction("/sys/role/add",params);
const editRole = (params)=>putAction("/sys/role/edit",params);
// const getRoleList = (params)=>getAction("/sys/role/list",params);
// const deleteRole = (params)=>deleteAction("/sys/role/delete",params);
// const deleteRoleList = (params)=>deleteAction("/sys/role/deleteBatch",params);
const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
const queryall = (params)=>getAction("/sys/role/queryall",params);
//用户管理
const addUser = (params)=>postAction("/sys/user/add",params);
const editUser = (params)=>putAction("/sys/user/edit",params);
const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
const getUserList = (params)=>getAction("/sys/user/list",params);
// const deleteUser = (params)=>deleteAction("/sys/user/delete",params);
// const deleteUserList = (params)=>deleteAction("/sys/user/deleteBatch",params);
const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
//验证用户是否存在
const checkOnlyUser = (params)=>getAction("/sys/user/checkOnlyUser",params);
//改变密码
const changePassword = (params)=>putAction("/sys/user/changePassword",params);
//权限管理
const addPermission= (params)=>postAction("/sys/permission/add",params);
const editPermission= (params)=>putAction("/sys/permission/edit",params);
const getPermissionList = (params)=>getAction("/sys/permission/list",params);
/*update_begin author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
/*update_end author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
// const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
// const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",params);
const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
//const queryPermissionsByUser = (params)=>getAction("/sys/permission/queryByUser",params);
const queryPermissionsByUser = (params)=>getAction("/sys/permission/getUserPermissionByToken",params);
const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
// 部门管理
const queryDepartTreeList = (params)=>getAction("/sys/sysDepart/queryTreeList",params);
const queryIdTree = (params)=>getAction("/sys/sysDepart/queryIdTree",params);
const queryParentName = (params)=>getAction("/sys/sysDepart/queryParentName",params);
const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
const deleteByDepartId = (params)=>deleteAction("/sys/sysDepart/delete",params);
//二级部门管理
const queryDepartPermission = (params)=>getAction("/sys/permission/queryDepartPermission",params);
const saveDepartPermission = (params)=>postAction("/sys/permission/saveDepartPermission",params);
const queryTreeListForDeptRole = (params)=>getAction("/sys/sysDepartPermission/queryTreeListForDeptRole",params);
const queryDeptRolePermission = (params)=>getAction("/sys/sysDepartPermission/queryDeptRolePermission",params);
const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/saveDeptRolePermission",params);
const queryMyDepartTreeList = (params)=>getAction("/sys/sysDepart/queryMyDeptTreeList",params);
//日志管理
//const getLogList = (params)=>getAction("/sys/log/list",params);
const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
//数据字典
const addDict = (params)=>postAction("/sys/dict/add",params);
const editDict = (params)=>putAction("/sys/dict/edit",params);
//const getDictList = (params)=>getAction("/sys/dict/list",params);
const treeList = (params)=>getAction("/sys/dict/treeList",params);
// const delDict = (params)=>deleteAction("/sys/dict/delete",params);
//const getDictItemList = (params)=>getAction("/sys/dictItem/list",params);
const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
//const delDictItem = (params)=>deleteAction("/sys/dictItem/delete",params);
//const delDictItemList = (params)=>deleteAction("/sys/dictItem/deleteBatch",params);
//字典标签专用(通过code获取字典数组)
export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
//从缓存中获取字典配置
function getDictItemsFromCache(dictCode) {
if (Vue.ls.get(UI_CACHE_DB_DICT_DATA) && Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode]) {
let dictItems = Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode];
console.log("-----------getDictItemsFromCache----------dictCode="+dictCode+"---- dictItems=",dictItems)
return dictItems;
}
}
//系统通告
const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",params);
const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",params);
//获取系统访问量
const getLoginfo = (params)=>getAction("/sys/loginfo",params);
const getVisitInfo = (params)=>getAction("/sys/visitInfo",params);
//数据日志访问
// const getDataLogList = (params)=>getAction("/sys/dataLog/list",params);
// 根据部门主键查询用户信息
const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
// 查询用户角色表里的所有信息
// const queryUserRoleMap = (params)=>getAction("/sys/user/queryUserRoleMap",params);
// 重复校验
const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
// 加载分类字典
const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
const checkRuleByCode = (params) => getAction('/sys/checkRule/checkByCode', params)
//加载我的通告信息
const getUserNoticeInfo= (params)=>getAction("/sys/sysAnnouncementSend/getMyAnnouncementSend",params);
const getTransitURL = url => `/sys/common/transitRESTful?url=${encodeURIComponent(url)}`
// 中转HTTP请求
export const transitRESTful = {
get: (url, parameter) => getAction(getTransitURL(url), parameter),
post: (url, parameter) => postAction(getTransitURL(url), parameter),
put: (url, parameter) => putAction(getTransitURL(url), parameter),
http: (url, parameter) => httpAction(getTransitURL(url), parameter),
}
export {
// imgView,
// doMian,
addRole,
editRole,
checkRoleCode,
addUser,
editUser,
queryUserRole,
getUserList,
queryall,
frozenBatch,
checkOnlyUser,
changePassword,
getPermissionList,
addPermission,
editPermission,
queryTreeList,
queryListAsync,
queryRolePermission,
saveRolePermission,
queryPermissionsByUser,
loadAllRoleIds,
getPermissionRuleList,
queryPermissionRule,
queryDepartTreeList,
queryIdTree,
queryParentName,
searchByKeywords,
deleteByDepartId,
deleteLog,
deleteLogList,
addDict,
editDict,
treeList,
addDictItem,
editDictItem,
doReleaseData,
doReovkeData,
getLoginfo,
getVisitInfo,
queryUserByDepId,
duplicateCheck,
queryTreeListForRole,
getSystemMenuList,
getSystemSubmenu,
getSystemSubmenuBatch,
loadCategoryData,
checkRuleByCode,
queryDepartPermission,
saveDepartPermission,
queryTreeListForDeptRole,
queryDeptRolePermission,
saveDeptRolePermission,
queryMyDepartTreeList,
getUserNoticeInfo,
getDictItemsFromCache
}
const api = {
Login: '/sys/login',
Logout: '/sys/logout',
ForgePassword: '/auth/forge-password',
Register: '/auth/register',
SendSms: '/account/sms',
// get my info
UserInfo: '/user/info'
}
export default api
\ No newline at end of file
import api from './index'
import { axios } from '@/utils/request'
/**
* login func
* parameter: {
* username: '',
* password: '',
* remember_me: true,
* captcha: '12345'
* }
* @param parameter
* @returns {*}
*/
export function login(parameter) {
return axios({
url: '/sys/login',
method: 'post',
data: parameter
})
}
export function phoneLogin(parameter) {
return axios({
url: '/sys/phoneLogin',
method: 'post',
data: parameter
})
}
export function getSmsCaptcha(parameter) {
return axios({
url: api.SendSms,
method: 'post',
data: parameter
})
}
export function getInfo() {
return axios({
url: '/api/user/info',
method: 'get',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
export function logout(logoutToken) {
return axios({
url: '/sys/logout',
method: 'post',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'X-Access-Token': logoutToken
}
})
}
/**
* 第三方登录
* @param token
* @returns {*}
*/
export function thirdLogin(token) {
return axios({
url: `/thirdLogin/getLoginUser/${token}`,
method: 'get',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
\ No newline at end of file
import Vue from 'vue'
import { axios } from '@/utils/request'
const api = {
user: '/api/user',
role: '/api/role',
service: '/api/service',
permission: '/api/permission',
permissionNoPager: '/api/permission/no-pager'
}
export default api
//post
export function postAction(url,parameter) {
return axios({
url: url,
method:'post' ,
data: parameter
})
}
//post method= {post | put}
export function httpAction(url,parameter,method) {
return axios({
url: url,
method:method ,
data: parameter
})
}
//put
export function putAction(url,parameter) {
return axios({
url: url,
method:'put',
data: parameter
})
}
//get
export function getAction(url,parameter) {
return axios({
url: url,
method: 'get',
params: parameter
})
}
//deleteAction
export function deleteAction(url,parameter) {
return axios({
url: url,
method: 'delete',
params: parameter
})
}
export function getUserList(parameter) {
return axios({
url: api.user,
method: 'get',
params: parameter
})
}
export function getRoleList(parameter) {
return axios({
url: api.role,
method: 'get',
params: parameter
})
}
export function getServiceList(parameter) {
return axios({
url: api.service,
method: 'get',
params: parameter
})
}
export function getPermissions(parameter) {
return axios({
url: api.permissionNoPager,
method: 'get',
params: parameter
})
}
// id == 0 add post
// id != 0 update put
export function saveService(parameter) {
return axios({
url: api.service,
method: parameter.id == 0 ? 'post' : 'put',
data: parameter
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFile(url,parameter){
return axios({
url: url,
params: parameter,
method:'get' ,
responseType: 'blob'
})
}
/**
* 下载文件
* @param url 文件路径
* @param fileName 文件名
* @param parameter
* @returns {*}
*/
export function downloadFile(url, fileName, parameter) {
return downFile(url, parameter).then((data) => {
if (!data || data.size === 0) {
Vue.prototype['$message'].warning('文件下载失败')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName)
} else {
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) //下载完成移除元素
window.URL.revokeObjectURL(url) //释放掉blob对象
}
})
}
/**
* 文件上传 用于富文本上传图片
* @param url
* @param parameter
* @returns {*}
*/
export function uploadAction(url,parameter){
return axios({
url: url,
data: parameter,
method:'post' ,
headers: {
'Content-Type': 'multipart/form-data', // 文件上传
},
})
}
/**
* 获取文件服务访问路径
* @param avatar
* @param subStr
* @returns {*}
*/
export function getFileAccessHttpUrl(avatar,subStr) {
if(!subStr) subStr = 'http'
try {
if(avatar && avatar.startsWith(subStr)){
return avatar;
}else{
if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
return window._CONFIG['staticDomainURL'] + "/" + avatar;
}
}
}catch(err){
return;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
.area-zoom-in-top-enter-active,
.area-zoom-in-top-leave-active {
opacity: 1;
transform: scaleY(1);
}
.area-zoom-in-top-enter,
.area-zoom-in-top-leave-active {
opacity: 0;
transform: scaleY(0);
}
.area-select {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: 'tnum';
position: relative;
outline: 0;
display: block;
background-color: #fff;
border: 1px solid #d9d9d9;
border-top-width: 1.02px;
border-radius: 4px;
outline: none;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.area-select-wrap .area-select {
display: inline-block;
}
.area-select * {
box-sizing: border-box;
}
.area-select:hover {
border-color: #40a9ff;
border-right-width: 1px !important;
outline: 0;
}
.area-select:active {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.area-select.small {
width: 126px;
}
.area-select.medium {
width: 160px;
}
.area-select.large {
width: 194px;
}
.area-select.is-disabled {
background: #eceff5;
cursor: not-allowed;
}
.area-select.is-disabled:hover {
border-color: #e1e2e6;
}
.area-select.is-disabled .area-selected-trigger {
cursor: not-allowed;
}
.area-select .area-selected-trigger {
position: relative;
display: block;
font-size: 14px;
cursor: pointer;
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
height: 100%;
padding: 8px 20px 7px 12px;
}
.area-select .area-select-icon {
position: absolute;
top: 50%;
margin-top: -2px;
right: 6px;
content: "";
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: rgba(0, 0, 0, 0.25);
transition: all .3s linear;
transform-origin: center;
}
.area-select .area-select-icon.active {
margin-top: -8px;
transform: rotate(180deg);
}
.area-selectable-list-wrap {
position: absolute;
width: 100%;
max-height: 275px;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow-x: auto;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.area-selectable-list {
position: relative;
margin: 0;
padding: 6px 0;
width: 100%;
font-size: 14px;
color: #565656;
list-style: none;
}
.area-selectable-list .area-select-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.area-selectable-list .area-select-option.hover {
background-color: #e6f7ff;
}
.area-selectable-list .area-select-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list-wrap {
position: absolute;
white-space: nowrap;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
font-size: 0;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.cascader-menu-list {
position: relative;
margin: 0;
font-size: 14px;
color: #565656;
padding: 6px 0;
list-style: none;
display: inline-block;
height: 204px;
overflow-x: hidden;
overflow-y: auto;
min-width: 160px;
vertical-align: top;
background-color: #fff;
border-right: 1px solid #e4e7ed;
}
.cascader-menu-list:last-child {
border-right: none;
}
.cascader-menu-list .cascader-menu-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.cascader-menu-list .cascader-menu-option.hover,
.cascader-menu-list .cascader-menu-option:hover {
background-color: #e6f7ff;
}
.cascader-menu-list .cascader-menu-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list .cascader-menu-option.cascader-menu-extensible:after {
position: absolute;
top: 50%;
margin-top: -4px;
right: 5px;
content: "";
width: 0;
height: 0;
border: 4px solid transparent;
border-left-color: #a1a4ad;
}
.cascader-menu-list::-webkit-scrollbar,
.area-selectable-list-wrap::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:decremen,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:end:decrement,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:increment,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:start:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:decremen,
.cascader-menu-list::-webkit-scrollbar-button:vertical:end:decrement,
.cascader-menu-list::-webkit-scrollbar-button:vertical:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:start:increment {
display: none;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical {
background-color: #b8b8b8;
border-radius: 4px;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
background-color: #777;
}
\ No newline at end of file
/** [表格主题样式一] 表格强制列不换行 */
.j-table-force-nowrap {
td, th {
white-space: nowrap;
}
.ant-table-selection-column {
padding: 12px 22px !important;
}
/** 列自适应,弊端会导致列宽失效 */
&.ant-table-wrapper .ant-table-content {
overflow-x: auto;
}
}
.cm-s-idea span.cm-meta{color:olive}.cm-s-idea span.cm-number{color:#00f}.cm-s-idea span.cm-keyword{line-height:1em;font-weight:700;color:navy}.cm-s-idea span.cm-atom{font-weight:700;color:navy}.cm-s-idea span.cm-def{color:#000}.cm-s-idea span.cm-variable{color:#000}.cm-s-idea span.cm-variable-2{color:#000}.cm-s-idea span.cm-type,.cm-s-idea span.cm-variable-3{color:#000}.cm-s-idea span.cm-property{color:#000}.cm-s-idea span.cm-operator{color:#000}.cm-s-idea span.cm-comment{color:grey}.cm-s-idea span.cm-string{color:green}.cm-s-idea span.cm-string-2{color:green}.cm-s-idea span.cm-qualifier{color:#555}.cm-s-idea span.cm-error{color:red}.cm-s-idea span.cm-attribute{color:#00f}.cm-s-idea span.cm-tag{color:navy}.cm-s-idea span.cm-link{color:#00f}.cm-s-idea .CodeMirror-activeline-background{background:#fffae3}.cm-s-idea span.cm-builtin{color:#30a}.cm-s-idea span.cm-bracket{color:#cc7}.cm-s-idea{font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif}.cm-s-idea .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important}.CodeMirror-hints.idea{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;color:#616569;background-color:#ebf3fd!important}.CodeMirror-hints.idea .CodeMirror-hint-active{background-color:#a2b8c9!important;color:#5c6065!important}
\ No newline at end of file
/*列表上方操作按钮区域*/
.ant-card-body .table-operator {
margin-bottom: 8px;
}
/** Button按钮间距 */
.table-operator .ant-btn {
margin: 0 8px 8px 0;
}
.table-operator .ant-btn-group .ant-btn {
margin: 0;
}
.table-operator .ant-btn-group .ant-btn:last-child {
margin: 0 8px 8px 0;
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
/*列表页面弹出modal*/
.ant-modal-cust-warp {
height: 100%
}
/*弹出modal Y轴滚动条*/
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto
}
/*弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden
}
/*列表中有图片的加这个样式 参考用户管理*/
.anty-img-wrap {
height: 25px;
position: relative;
}
.anty-img-wrap > img {
max-height: 100%;
}
/*列表中范围查询样式*/
.query-group-cust{width: calc(50% - 10px)}
.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}
/*erp风格子表外框padding设置*/
.ant-card-wider-padding.cust-erp-sub-tab>.ant-card-body{padding:5px 12px}
/* 内嵌子表背景颜色 */
.j-inner-table-wrapper /deep/ .ant-table-expanded-row .ant-table-wrapper .ant-table-tbody .ant-table-row {
background-color: #FFFFFF;
}
/**隐藏样式-modal确定按钮 */
.jee-hidden{display: none}
\ No newline at end of file
/**
* 列表查询通用样式,移动端自适应
*/
.search{
margin-bottom: 54px;
}
.fold{
width: calc(100% - 216px);
display: inline-block
}
.operator{
margin-bottom: 18px;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
.operator button {
margin-right: 5px;
}
i {
cursor: pointer;
}
.trcolor{
background-color: rgba(255, 192, 203, 0.31);
color:red;
}
差异被折叠。
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import store from '@/store'
/**
* 单点登录
*/
const init = (callback) => {
console.log("-------单点登录开始-------");
let token = Vue.ls.get(ACCESS_TOKEN);
let st = getUrlParam("ticket");
let sevice = "http://"+window.location.host+"/";
if(token){
loginSuccess(callback);
}else{
if(st){
validateSt(st,sevice,callback);
}else{
let serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}
console.log("-------单点登录结束-------");
};
const SSO = {
init: init
};
function getUrlParam(paraName) {
let url = document.location.toString();
let arrObj = url.split("?");
if (arrObj.length > 1) {
let arrPara = arrObj[1].split("&");
let arr;
for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("=");
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return "";
}
else {
return "";
}
}
function validateSt(ticket,service,callback){
let params = {
ticket: ticket,
service:service
};
store.dispatch('ValidateLogin',params).then(res => {
//this.departConfirm(res)
if(res.success){
loginSuccess(callback);
}else{
let sevice = "http://"+window.location.host+"/";
let serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}).catch((err) => {
console.log(err);
//that.requestFailed(err);
});
}
function loginSuccess (callback) {
callback();
}
export default SSO;
\ No newline at end of file
<template>
<tooltip v-if="tips !== ''">
<template slot="title">{{ tips }}</template>
<avatar :size="avatarSize" :src="src" />
</tooltip>
<avatar v-else :size="avatarSize" :src="src" />
</template>
<script>
import Avatar from 'ant-design-vue/es/avatar'
import Tooltip from 'ant-design-vue/es/tooltip'
export default {
name: "AvatarItem",
components: {
Avatar,
Tooltip
},
props: {
tips: {
type: String,
default: '',
required: false
},
src: {
type: String,
default: ''
}
},
data () {
return {
size: this.$parent.size
}
},
computed: {
avatarSize () {
return this.size !== 'mini' && this.size || 20
}
},
watch: {
'$parent.size' (val) {
this.size = val
}
}
}
</script>
\ No newline at end of file
<!--
<template>
<div :class="[prefixCls]">
<ul>
<slot></slot>
<template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template>
<template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength">
<avatar-item :size="size">
<avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar>
</avatar-item>
</template>
</ul>
</div>
</template>
-->
<script>
import Avatar from 'ant-design-vue/es/avatar'
import AvatarItem from './Item'
import { filterEmpty } from '@/components/_util/util'
export default {
AvatarItem,
name: "AvatarList",
components: {
Avatar,
AvatarItem
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-avatar-list'
},
/**
* 头像大小 类型: large、small 、mini, default
* 默认值: default
*/
size: {
type: [String, Number],
default: 'default'
},
/**
* 要显示的最大项目
*/
maxLength: {
type: Number,
default: 0
},
/**
* 多余的项目风格
*/
excessItemsStyle: {
type: Object,
default: () => {
return {
color: '#f56a00',
backgroundColor: '#fde3cf'
}
}
}
},
data () {
return {}
},
methods: {
getItems(items) {
const classString = {
[`${this.prefixCls}-item`]: true,
[`${this.size}`]: true
}
if (this.maxLength > 0) {
items = items.slice(0, this.maxLength)
items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>))
}
const itemList = items.map((item) => (
<li class={ classString }>{ item }</li>
))
return itemList
}
},
render () {
const { prefixCls, size } = this.$props
const classString = {
[`${prefixCls}`]: true,
[`${size}`]: true,
}
const items = filterEmpty(this.$slots.default)
const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null
return (
<div class={ classString }>
{ itemsDom }
</div>
)
}
}
</script>
\ No newline at end of file
import AvatarList from './List'
import "./index.less"
export default AvatarList
\ No newline at end of file
@import "../index";
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
.@{avatar-list-prefix-cls} {
display: inline-block;
ul {
list-style: none;
display: inline-block;
padding: 0;
margin: 0 0 0 8px;
font-size: 0;
}
}
.@{avatar-list-item-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
margin-left: -8px;
width: @avatar-size-base;
height: @avatar-size-base;
:global {
.ant-avatar {
border: 1px solid #fff;
cursor: pointer;
}
}
&.large {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
&.small {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
&.mini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
}
<template>
<a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
<div class="chart-card-header">
<div class="meta">
<span class="chart-card-title">{{ title }}</span>
<span class="chart-card-action">
<slot name="action"></slot>
</span>
</div>
<div class="total"><span>{{ total }}</span></div>
</div>
<div class="chart-card-content">
<div class="content-fix">
<slot></slot>
</div>
</div>
<div class="chart-card-footer">
<div class="field">
<slot name="footer"></slot>
</div>
</div>
</a-card>
</template>
<script>
export default {
name: "ChartCard",
props: {
title: {
type: String,
default: ''
},
total: {
type: String,
default: ''
},
loading: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="less" scoped>
.chart-card-header {
position: relative;
overflow: hidden;
width: 100%;
.meta {
position: relative;
overflow: hidden;
width: 100%;
color: rgba(0, 0, 0, .45);
font-size: 14px;
line-height: 22px;
}
}
.chart-card-action {
cursor: pointer;
position: absolute;
top: 0;
right: 0;
}
.chart-card-footer {
border-top: 1px solid #e8e8e8;
padding-top: 9px;
margin-top: 8px;
> * {
position: relative;
}
.field {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
}
.chart-card-content {
margin-bottom: 12px;
position: relative;
height: 46px;
width: 100%;
.content-fix {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
}
}
.total {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
color: #000;
margin-top: 4px;
margin-bottom: 0;
font-size: 30px;
line-height: 38px;
height: 38px;
}
</style>
\ No newline at end of file
<template>
<span>
{{ lastTime | format }}
</span>
</template>
<script>
function fixedZero(val) {
return val * 1 < 10 ? `0${val}` : val;
}
export default {
name: "CountDown",
props: {
format: {
type: Function,
default: undefined
},
target: {
type: [Date, Number],
required: true,
},
onEnd: {
type: Function,
default: () => {
}
}
},
data() {
return {
dateTime: '0',
originTargetTime: 0,
lastTime: 0,
timer: 0,
interval: 1000
}
},
filters: {
format(time) {
const hours = 60 * 60 * 1000;
const minutes = 60 * 1000;
const h = Math.floor(time / hours);
const m = Math.floor((time - h * hours) / minutes);
const s = Math.floor((time - h * hours - m * minutes) / 1000);
return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
}
},
created() {
this.initTime()
this.tick()
},
methods: {
initTime() {
let lastTime = 0;
let targetTime = 0;
this.originTargetTime = this.target
try {
if (Object.prototype.toString.call(this.target) === '[object Date]') {
targetTime = this.target
} else {
targetTime = new Date(this.target).getTime()
}
} catch (e) {
throw new Error('invalid target prop')
}
lastTime = targetTime - new Date().getTime();
this.lastTime = lastTime < 0 ? 0 : lastTime
},
tick() {
const {onEnd} = this
this.timer = setTimeout(() => {
if (this.lastTime < this.interval) {
clearTimeout(this.timer)
this.lastTime = 0
if (typeof onEnd === 'function') {
onEnd();
}
} else {
this.lastTime -= this.interval
this.tick()
}
}, this.interval)
}
},
beforeUpdate () {
if (this.originTargetTime !== this.target) {
this.initTime()
}
},
beforeDestroy() {
clearTimeout(this.timer)
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
import CountDown from './CountDown'
export default CountDown
\ No newline at end of file
<script>
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'
export default {
name: 'Ellipsis',
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean,
default: true,
},
length: {
type: Number,
default: 25,
},
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
},
methods: {},
render() {
const { tooltip, length } = this.$props
let text = ''
// 处理没有default插槽时的特殊情况
if (this.$slots.default) {
text = this.$slots.default.map(vNode => vNode.text).join('')
}
// 判断是否显示 tooltip
if (tooltip && getStrFullLength(text) > length) {
return (
<a-tooltip>
<template slot="title">{text}</template>
<span>{cutStrByFullLength(text, this.length) + '…'}</span>
</a-tooltip>
)
} else {
return (<span>{text}</span>)
}
}
}
</script>
\ No newline at end of file
import Ellipsis from './Ellipsis'
export default Ellipsis
\ No newline at end of file
<template>
<div>
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
<div :key="fileKey" style="position: relative;">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
<span style="margin-left:5px">上传中…</span>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" :title="file.name">
<a-icon type="paper-clip"/>
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<a-tooltip v-else :title="file.name">
<a-icon type="paper-clip" style="color:red;"/>
<span style="color:red;margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
<a-tooltip title="操作">
<a-icon
v-if="file.status!=='uploading'"
type="setting"
style="cursor: pointer;"/>
</a-tooltip>
<a-menu slot="overlay">
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
<span><a-icon type="download"/>&nbsp;下载</span>
</a-menu-item>
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
<span><a-icon type="delete"/>&nbsp;删除</span>
</a-menu-item>
<a-menu-item @click="handleMoreOperation">
<span><a-icon type="bars"/> 更多</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</div>
</template>
<a-upload
v-show="!hasFile"
name="file"
:data="{'isup': 1}"
:multiple="false"
:action="uploadAction"
:headers="uploadHeaders"
:showUploadList="false"
v-bind="cellProps"
@change="handleChangeUpload"
>
<a-button icon="upload">{{originColumn.btnText || '上传文件'}}</a-button>
</a-upload>
<j-file-pop ref="filePop" @ok="handleFileSuccess"/>
</div>
</template>
<script>
import { getFileAccessHttpUrl } from '@api/manage'
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import JFilePop from '@/components/jeecg/minipop/JFilePop'
import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
export default {
name: 'JVxeFileCell',
mixins: [JVxeCellMixins],
components: {JFilePop},
props: {},
data() {
return {
innerFile: null,
}
},
computed: {
/** upload headers */
uploadHeaders() {
let {originColumn: col} = this
let headers = {}
if (col.token === true) {
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
}
return headers
},
/** 上传请求地址 */
uploadAction() {
if (!this.originColumn.action) {
return window._CONFIG['domianURL'] + '/sys/common/upload'
} else {
return this.originColumn.action
}
},
hasFile() {
return this.innerFile != null
},
ellipsisFileName() {
let length = 5
let file = this.innerFile
if (!file || !file.name) {
return ''
}
if (file.name.length > length) {
return file.name.substr(0, length) + '…'
}
return file.name
},
responseName() {
if (this.originColumn.responseName) {
return this.originColumn.responseName
} else {
return 'message'
}
},
},
watch: {
innerValue: {
immediate: true,
handler() {
if (this.innerValue) {
this.innerFile = this.innerValue
} else {
this.innerFile = null
}
},
},
},
methods: {
// 点击更多按钮
handleMoreOperation() {
let path = ''
if (this.innerFile) {
path = this.innerFile.path
}
this.$refs.filePop.show('', path)
},
// 更多上传回调
handleFileSuccess(file) {
if (file) {
this.innerFile.path = file.path
this.handleChangeCommon(this.innerFile)
}
},
handleChangeUpload(info) {
let {originColumn: col} = this
let {file} = info
let value = {
name: file.name,
type: file.type,
size: file.size,
status: file.status,
percent: file.percent
}
if (file.response) {
value['responseName'] = file.response[this.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}
this.innerFile = value
},
handleClickDownloadFile() {
let {url, path} = this.innerFile || {}
if (!url || url.length === 0) {
if (path && path.length > 0) {
url = getFileAccessHttpUrl(path.split(',')[0])
}
}
if (url) {
window.open(url)
}
},
handleClickDeleteFile() {
this.handleChangeCommon(null)
},
},
// 【组件增强】注释详见:JVxeCellMixins.js
enhanced: {
switches: {visible: true},
getValue: value => JVxeUploadCell.enhanced.getValue(value),
setValue: value => JVxeUploadCell.enhanced.setValue(value),
}
}
</script>
<style scoped lang="less">
</style>
\ No newline at end of file
<template>
<div>
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
<div :key="fileKey" style="position: relative;">
<template v-if="!file || !(file['url'] || file['path'] || file['message'])">
<a-tooltip :title="'请稍后: ' + JSON.stringify (file) + ((file['url'] || file['path'] || file['message']))">
<a-icon type="loading"/>
</a-tooltip>
</template>
<template v-else-if="file['path']">
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
</template>
<template v-else>
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError"/>
</template>
<template slot="addonBefore" style="width: 30px">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
</template>
<template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
<a-tooltip title="操作">
<a-icon
v-if="file.status!=='uploading'"
type="setting"
style="cursor: pointer;"/>
</a-tooltip>
<a-menu slot="overlay">
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
<span><a-icon type="download"/>&nbsp;下载</span>
</a-menu-item>
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
<span><a-icon type="delete"/>&nbsp;删除</span>
</a-menu-item>
<a-menu-item @click="handleMoreOperation">
<span><a-icon type="bars"/> 更多</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</div>
</template>
<a-upload
v-show="!hasFile"
name="file"
:data="{'isup': 1}"
:multiple="false"
:action="uploadAction"
:headers="uploadHeaders"
:showUploadList="false"
v-bind="cellProps"
@change="handleChangeUpload"
>
<a-button icon="upload">{{originColumn.btnText || '上传图片'}}</a-button>
</a-upload>
<j-file-pop ref="filePop" @ok="handleFileSuccess"/>
</div>
</template>
<script>
import { getFileAccessHttpUrl } from '@api/manage'
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import JFilePop from '@/components/jeecg/minipop/JFilePop'
import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
export default {
name: 'JVxeImageCell',
mixins: [JVxeCellMixins],
components: {JFilePop},
props: {},
data() {
return {
innerFile: null,
}
},
computed: {
/** upload headers */
uploadHeaders() {
let {originColumn: col} = this
let headers = {}
if (col.token === true) {
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
}
return headers
},
/** 上传请求地址 */
uploadAction() {
if (!this.originColumn.action) {
return window._CONFIG['domianURL'] + '/sys/common/upload'
} else {
return this.originColumn.action
}
},
hasFile() {
return this.innerFile != null
},
/** 预览图片地址 */
imgSrc() {
if (this.innerFile) {
if (this.innerFile['url']) {
return this.innerFile['url']
} else if (this.innerFile['path']) {
let path = this.innerFile['path'].split(',')[0]
return getFileAccessHttpUrl(path)
}
}
return ''
},
responseName() {
if (this.originColumn.responseName) {
return this.originColumn.responseName
} else {
return 'message'
}
},
},
watch: {
innerValue: {
immediate: true,
handler() {
if (this.innerValue) {
this.innerFile = this.innerValue
} else {
this.innerFile = null
}
},
},
},
methods: {
// 点击更多按钮
handleMoreOperation() {
let path = ''
if (this.innerFile) {
path = this.innerFile.path
}
this.$refs.filePop.show('', path, 'img')
},
// 更多上传回调
handleFileSuccess(file) {
if (file) {
this.innerFile.path = file.path
this.handleChangeCommon(this.innerFile)
}
},
// 弹出上传出错详细信息
handleClickShowImageError() {
let file = this.innerFile || null
if (file && file['message']) {
this.$error({title: '上传出错', content: '错误信息:' + file['message'], maskClosable: true})
}
},
handleChangeUpload(info) {
let {originColumn: col} = this
let {file} = info
let value = {
name: file.name,
type: file.type,
size: file.size,
status: file.status,
percent: file.percent
}
if (file.response) {
value['responseName'] = file.response[this.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}
this.innerFile = value
},
handleClickDownloadFile() {
if (this.imgSrc) {
window.open(this.imgSrc)
}
},
handleClickDeleteFile() {
this.handleChangeCommon(null)
},
},
// 【组件增强】注释详见:JVxeCellMixins.js
enhanced: {
switches: {visible: true},
getValue: value => JVxeUploadCell.enhanced.getValue(value),
setValue: value => JVxeUploadCell.enhanced.setValue(value),
}
}
</script>
<style scoped lang="less">
.j-editable-image {
height: 32px;
max-width: 100px !important;
cursor: pointer;
&:hover {
opacity: 0.8;
}
&:active {
opacity: 0.6;
}
}
</style>
\ No newline at end of file
<template>
<j-popup
v-bind="popupProps"
@input="handlePopupInput"
/>
</template>
<script>
import JVxeCellMixins, { vModel, dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
export default {
name: 'JVxePopupCell',
mixins: [JVxeCellMixins],
computed: {
popupProps() {
const {innerValue, originColumn: col, caseId, cellProps} = this
return {
...cellProps,
value: innerValue,
field: col.field || col.key,
code: col.popupCode,
orgFields: col.orgFields,
destFields: col.destFields,
groupId: caseId,
}
},
},
methods: {
/** popup回调 */
handlePopupInput(value, others) {
const {row, originColumn: col} = this
// 存储输入的值
let popupValue = value
if (others && Object.keys(others).length > 0) {
Object.keys(others).forEach(key => {
let currentValue = others[key]
// 当前列直接赋值,其他列通过vModel赋值
if (key === col.key) {
popupValue = currentValue
} else {
vModel.call(this, currentValue, row, key)
}
})
}
this.handleChangeCommon(popupValue)
},
},
// 【组件增强】注释详见:JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-input'),
},
},
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<a-radio-group
:class="clazz"
:value="innerValue"
v-bind="cellProps"
@change="(e)=>handleChangeCommon(e.target.value)"
>
<a-radio
v-for="item of originColumn.options"
:key="item.value"
:value="item.value"
@click="$event=>handleRadioClick(item,$event)"
>{{ item.text }}
</a-radio>
</a-radio-group>
</template>
<script>
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
export default {
name: 'JVxeRadioCell',
mixins: [JVxeCellMixins],
computed: {
scrolling() {
return !!this.renderOptions.scrolling
},
clazz() {
return {
'j-vxe-radio': true,
'no-animation': this.scrolling
}
},
},
methods: {
handleRadioClick(item) {
if (this.originColumn.allowClear === true) {
// 取消选择
if (item.value === this.innerValue) {
this.handleChangeCommon(null)
}
}
},
},
// 【组件增强】注释详见:JVxeCellMixins.js
enhanced: {
switches: {visible: true},
}
}
</script>
<style lang="less">
// 关闭动画,防止滚动时动态赋值出现问题
.j-vxe-radio.no-animation {
.ant-radio-inner,
.ant-radio-inner::after {
transition: none !important;
}
}
</style>
\ No newline at end of file
import debounce from 'lodash/debounce'
import { getAction } from '@/api/manage'
import { cloneObject } from '@/utils/util'
import { filterDictText } from '@/components/dict/JDictSelectUtil'
import { ajaxGetDictItems, getDictItemsFromCache } from '@/api/api'
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
/** 公共资源 */
const common = {
/** value - label map,防止重复查询(刷新清空缓存) */
labelMap: new Map(),
/** 公共data */
data() {
return {
loading: false,
innerSelectValue: null,
innerOptions: [],
}
},
/** 公共计算属性 */
computed: {
dict() {
return this.originColumn.dict
},
options() {
if (this.isAsync) {
return this.innerOptions
} else {
return this.originColumn.options || []
}
},
// 是否是异步模式
isAsync() {
let isAsync = this.originColumn.async
return (isAsync != null && isAsync !== '') ? !!isAsync : true
},
},
/** 公共属性监听 */
watch: {
innerValue: {
immediate: true,
handler(value) {
if (value == null || value === '') {
this.innerSelectValue = null
} else {
this.loadDataByValue(value)
}
}
},
dict() {
this.loadDataByDict()
}
},
/** 公共方法 */
methods: {
// 根据 value 查询数据,用于回显
async loadDataByValue(value) {
if (this.isAsync) {
if (this.innerSelectValue !== value) {
if (common.labelMap.has(value)) {
this.innerOptions = cloneObject(common.labelMap.get(value))
} else {
let {success, result} = await getAction(`/sys/dict/loadDictItem/${this.dict}`, {key: value})
if (success && result && result.length > 0) {
this.innerOptions = [{value: value, text: result[0]}]
common.labelMap.set(value, cloneObject(this.innerOptions))
}
}
}
}
this.innerSelectValue = (value || '').toString()
},
// 初始化字典
async loadDataByDict() {
if (!this.isAsync) {
// 如果字典项集合有数据
if (!this.originColumn.options || this.originColumn.options.length === 0) {
// 根据字典Code, 初始化字典数组
let dictStr = ''
if (this.dict) {
let arr = this.dict.split(',')
if (arr[0].indexOf('where') > 0) {
let tbInfo = arr[0].split('where')
dictStr = tbInfo[0].trim() + ',' + arr[1] + ',' + arr[2] + ',' + encodeURIComponent(tbInfo[1])
} else {
dictStr = this.dict
}
if (this.dict.indexOf(',') === -1) {
//优先从缓存中读取字典配置
let cache = getDictItemsFromCache(this.dict)
if (cache) {
this.innerOptions = cache
return
}
}
let {success, result} = await ajaxGetDictItems(dictStr, null)
if (success) {
this.innerOptions = result
}
}
}
}
},
},
}
// 显示组件,自带翻译
export const DictSearchSpanCell = {
name: 'JVxeSelectSearchSpanCell',
mixins: [JVxeCellMixins],
data() {
return {
...common.data.apply(this),
}
},
computed: {
...common.computed,
},
watch: {
...common.watch,
},
methods: {
...common.methods,
},
render(h) {
return h('span', {}, [
filterDictText(this.innerOptions, this.innerSelectValue || this.innerValue)
])
},
}
// 请求id
let requestId = 0
// 输入选择组件
export const DictSearchInputCell = {
name: 'JVxeSelectSearchInputCell',
mixins: [JVxeCellMixins],
data() {
return {
...common.data.apply(this),
hasRequest: false,
scopedSlots: {
notFoundContent: () => {
if (this.loading) {
return <a-spin size="small"/>
} else if (this.hasRequest) {
return <div>没有查询到任何数据</div>
} else {
return <div>{this.tipsContent}</div>
}
}
}
}
},
computed: {
...common.computed,
tipsContent() {
return this.originColumn.tipsContent || '请输入搜索内容'
},
filterOption() {
if (this.isAsync) {
return null
}
return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
},
watch: {
...common.watch,
},
created() {
this.loadData = debounce(this.loadData, 300)//消抖
},
methods: {
...common.methods,
loadData(value) {
const currentRequestId = ++requestId
this.loading = true
this.innerOptions = []
if (value == null || value.trim() === '') {
this.loading = false
this.hasRequest = false
return
}
// 字典code格式:table,text,code
this.hasRequest = true
getAction(`/sys/dict/loadDict/${this.dict}`, {keyword: value}).then(res => {
if (currentRequestId !== requestId) {
return
}
let {success, result, message} = res
if (success) {
this.innerOptions = result
result.forEach((item) => {
common.labelMap.set(item.value, [item])
})
} else {
this.$message.warning(message)
}
}).finally(() => {
this.loading = false
})
},
handleChange(selectedValue) {
this.innerSelectValue = selectedValue
this.handleChangeCommon(this.innerSelectValue)
},
handleSearch(value) {
if (this.isAsync) {
// 在输入时也应该开启加载,因为loadData加了消抖,所以会有800ms的用户主观上认为的卡顿时间
this.loading = true
if (this.innerOptions.length > 0) {
this.innerOptions = []
}
this.loadData(value)
}
},
renderOptionItem() {
let options = []
this.options.forEach(({value, text, label, title, disabled}) => {
options.push(
<a-select-option key={value} value={value} disabled={disabled}>{text || label || title}</a-select-option>
)
})
return options
},
},
render() {
return (
<a-select
showSearch
allowClear
value={this.innerSelectValue}
filterOption={this.filterOption}
style="width: 100%"
{...this.cellProps}
onSearch={this.handleSearch}
onChange={this.handleChange}
scopedSlots={this.scopedSlots}
>
{this.renderOptionItem()}
</a-select>
)
},
// 【组件增强】注释详见:JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-select'),
},
}
}
\ No newline at end of file
import { installCell, JVXETypes } from '@/components/jeecg/JVxeTable'
import JVxePopupCell from './JVxePopupCell'
import { DictSearchInputCell, DictSearchSpanCell } from './JVxeSelectDictSearchCell'
import JVxeFileCell from './JVxeFileCell'
import JVxeImageCell from './JVxeImageCell'
import JVxeRadioCell from './JVxeRadioCell'
import JVxeSelectCell from '@comp/jeecg/JVxeTable/components/cells/JVxeSelectCell'
import JVxeTextareaCell from '@comp/jeecg/JVxeTable/components/cells/JVxeTextareaCell'
// 注册online组件
JVXETypes.input_pop = 'input_pop'
JVXETypes.list_multi = 'list_multi'
JVXETypes.sel_search = 'sel_search'
installCell(JVXETypes.input_pop, JVxeTextareaCell)
installCell(JVXETypes.list_multi, JVxeSelectCell)
installCell(JVXETypes.sel_search, JVxeSelectCell)
// 注册【popup】组件(普通封装方式)
JVXETypes.popup = 'popup'
installCell(JVXETypes.popup, JVxePopupCell)
// 注册【字典搜索下拉】组件(高级封装方式)
JVXETypes.selectDictSearch = 'select-dict-search'
installCell(JVXETypes.selectDictSearch, DictSearchInputCell, DictSearchSpanCell)
// 注册【文件上传】组件
JVXETypes.file = 'file'
installCell(JVXETypes.file, JVxeFileCell)
// 注册【图片上传】组件
JVXETypes.image = 'image'
installCell(JVXETypes.image, JVxeImageCell)
// 注册【单选框】组件
JVXETypes.radio = 'radio'
installCell(JVXETypes.radio, JVxeRadioCell)
<!--
* @Description: 动态表格 用于批量填入数据
* @Author: kcz
* @Date: 2020-03-27 18:36:56
* @LastEditors: kcz
* @LastEditTime: 2020-07-09 20:53:57
-->
<template>
<a-form-model
ref="dynamicValidateForm"
layout="inline"
:model="dynamicValidateForm"
>
<a-table
class="batch-table"
:pagination="false"
:rowKey="record => record.key"
:columns="columns"
:dataSource="dynamicValidateForm.domains"
bordered
:scroll="{
x: listLength * 190 + 80 + (!record.options.hideSequence ? 60 : 0),
y: record.options.scrollY
}"
>
<template
v-for="item in record.list"
:slot="item.key"
slot-scope="text, record, index"
>
<KFormModelItem
:key="item.key + '1'"
:record="item"
:config="config"
:parentDisabled="disabled"
:index="index"
:domains="dynamicValidateForm.domains"
:dynamicData="dynamicData"
v-model="record[item.model]"
@input="handleInput"
/>
</template>
<template slot="dynamic-delete-button" slot-scope="text, record">
<a-icon
v-if="!disabled"
class="dynamic-delete-button"
type="minus-circle-o"
@click="removeDomain(record)"
/>
</template>
</a-table>
<a-button type="dashed" :disabled="disabled" @click="addDomain">
<a-icon type="plus" />增加
</a-button>
</a-form-model>
</template>
<script>
import KFormModelItem from "./module/KFormModelItem";
export default {
name: "KBatch",
props: ["record", "value", "dynamicData", "config", "parentDisabled"],
components: {
KFormModelItem
},
watch: {
value: {
// value 需要深度监听及默认先执行handler函数
handler(val) {
this.dynamicValidateForm.domains = val || [];
},
immediate: true,
deep: true
}
},
data() {
return {
dynamicValidateForm: {
domains: []
}
};
},
computed: {
listLength() {
return this.record.list.filter(item => !item.options.hidden).length;
},
columns() {
let columns = [];
if (!this.record.options.hideSequence) {
columns.push({
title: "序号",
dataIndex: "sequence_index_number",
width: "60px",
align: "center",
customRender: (text, record, index) => {
return index + 1;
}
});
}
columns.push(
...this.record.list
.filter(item => !item.options.hidden)
.map((item, index) => {
return {
title: item.label,
dataIndex: item.key,
width: index === this.record.list.length - 1 ? "" : "190px",
scopedSlots: { customRender: item.key }
};
})
);
columns.push({
title: "操作",
dataIndex: "dynamic-delete-button",
fixed: "right",
width: "80px",
align: "center",
scopedSlots: { customRender: "dynamic-delete-button" }
});
return columns;
},
disabled() {
return this.record.options.disabled || this.parentDisabled;
}
},
methods: {
validationSubform() {
let verification;
this.$refs.dynamicValidateForm.validate(valid => {
verification = valid;
});
return verification;
},
resetForm() {
this.$refs.dynamicValidateForm.resetFields();
},
removeDomain(item) {
const index = this.dynamicValidateForm.domains.indexOf(item);
if (index !== -1) {
this.dynamicValidateForm.domains.splice(index, 1);
}
},
addDomain() {
let data = {};
this.record.list.forEach(item => {
data[item.model] = item.options.defaultValue;
});
this.dynamicValidateForm.domains.push({
...data,
key: Date.now()
});
this.handleInput();
},
handleInput() {
this.$emit("change", this.dynamicValidateForm.domains);
}
}
};
</script>
<style scoped>
.dynamic-delete-button {
cursor: pointer;
position: relative;
top: 4px;
font-size: 24px;
color: #999;
transition: all 0.3s;
}
.dynamic-delete-button:hover {
color: #e89;
}
.dynamic-delete-button[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
</style>
import KBatch from "./batch.vue";
export default KBatch;
<!--
* @Description: 传入record数据,通过判断record.type,来渲染对应的组件
* @Author: kcz
* @Date: 2020-01-02 22:41:48
* @LastEditors: kcz
* @LastEditTime: 2020-06-08 20:46:36
-->
<template>
<a-form-model-item
v-if="
[
'input',
'textarea',
'date',
'time',
'number',
'radio',
'checkbox',
'select',
'rate',
'switch',
'slider',
'uploadImg',
'uploadFile',
'cascader',
'treeSelect'
].includes(record.type)
"
:prop="`domains.${index}.${record.model}`"
:rules="record.rules"
>
<!-- 单行文本 -->
<a-input
:style="`width:${record.options.width}`"
v-if="record.type === 'input'"
:disabled="record.options.disabled || parentDisabled"
:placeholder="record.options.placeholder"
:type="record.options.type"
:allowClear="record.options.clearable"
:maxLength="record.options.maxLength"
:value="value"
@change="handleChange($event.target.value)"
/>
<!-- 多行文本 -->
<a-textarea
:style="`width:${record.options.width}`"
v-else-if="record.type === 'textarea'"
:autoSize="{
minRows: record.options.minRows,
maxRows: record.options.maxRows
}"
:disabled="record.options.disabled || parentDisabled"
:placeholder="record.options.placeholder"
:allowClear="record.options.clearable"
:maxLength="record.options.maxLength"
:rows="4"
:value="value"
@change="handleChange($event.target.value)"
/>
<!-- 日期选择 -->
<KDatePicker
v-else-if="record.type === 'date'"
:parentDisabled="parentDisabled"
:record="record"
:value="value"
@change="handleChange"
/>
<!-- 时间选择 -->
<KTimePicker
v-else-if="record.type === 'time'"
:parentDisabled="parentDisabled"
:record="record"
:value="value"
@change="handleChange"
/>
<!-- 数字输入框 -->
<a-input-number
v-else-if="record.type === 'number'"
:style="`width:${record.options.width}`"
:min="
record.options.min || record.options.min === 0
? record.options.min
: -Infinity
"
:max="
record.options.max || record.options.max === 0
? record.options.max
: Infinity
"
:precision="
record.options.precision > 50 ||
(!record.options.precision && record.options.precision !== 0)
? null
: record.options.precision
"
:disabled="record.options.disabled || parentDisabled"
:step="record.options.step"
:placeholder="record.options.placeholder"
:value="value"
@change="handleChange"
/>
<!-- 单选框 -->
<a-radio-group
v-else-if="record.type === 'radio'"
:options="
!record.options.dynamic
? record.options.options
: dynamicData[record.options.dynamicKey]
? dynamicData[record.options.dynamicKey]
: []
"
:disabled="record.options.disabled || parentDisabled"
:placeholder="record.options.placeholder"
:value="value"
@change="handleChange($event.target.value)"
/>
<!-- 多选框 -->
<a-checkbox-group
v-else-if="record.type === 'checkbox'"
:options="
!record.options.dynamic
? record.options.options
: dynamicData[record.options.dynamicKey]
? dynamicData[record.options.dynamicKey]
: []
"
:disabled="record.options.disabled || parentDisabled"
:placeholder="record.options.placeholder"
:value="value"
@change="handleChange"
/>
<!-- 评分 -->
<a-rate
v-else-if="record.type === 'rate'"
:count="record.options.max"
:disabled="record.options.disabled || parentDisabled"
:placeholder="record.options.placeholder"
:allowHalf="record.options.allowHalf"
:value="value"
@change="handleChange"
/>
<!-- 下拉选框 -->
<a-select
:style="`width:${record.options.width}`"
v-else-if="record.type === 'select'"
:placeholder="record.options.placeholder"
:showSearch="record.options.filterable"
:options="
!record.options.dynamic
? record.options.options
: dynamicData[record.options.dynamicKey]
? dynamicData[record.options.dynamicKey]
: []
"
:disabled="record.options.disabled || parentDisabled"
:allowClear="record.options.clearable"
:mode="record.options.multiple ? 'multiple' : ''"
:value="value"
@change="handleChange"
/>
<!-- 开关 -->
<a-switch
v-else-if="record.type === 'switch'"
:disabled="record.options.disabled || parentDisabled"
:checked="value"
@change="handleChange"
/>
<!-- 滑块 -->
<div
v-else-if="record.type === 'slider'"
:style="`width:${record.options.width}`"
class="slider-box"
>
<div class="slider">
<a-slider
:disabled="record.options.disabled || parentDisabled"
:min="record.options.min"
:max="record.options.max"
:step="record.options.step"
:value="value"
@change="handleChange"
/>
</div>
<div class="number" v-if="record.options.showInput">
<a-input-number
style="width:100%"
:disabled="record.options.disabled || parentDisabled"
:min="record.options.min"
:max="record.options.max"
:step="record.options.step"
:value="value"
@change="handleChange"
/>
</div>
</div>
<!-- 上传图片 -->
<UploadImg
v-else-if="record.type === 'uploadImg'"
:style="`width:${record.options.width}`"
:parentDisabled="parentDisabled"
:record="record"
:config="config"
:value="value"
@change="handleChange"
/>
<!-- 上传文件 -->
<UploadFile
v-else-if="record.type === 'uploadFile'"
:style="`width:${record.options.width}`"
:parentDisabled="parentDisabled"
:dynamicData="dynamicData"
:config="config"
:record="record"
:value="value"
@change="handleChange"
/>
<!-- 树选择器 -->
<a-tree-select
v-else-if="record.type === 'treeSelect'"
:style="`width:${record.options.width}`"
:placeholder="record.options.placeholder"
:multiple="record.options.multiple"
:showSearch="record.options.showSearch"
:treeCheckable="record.options.treeCheckable"
:treeData="
!record.options.dynamic
? record.options.options
: dynamicData[record.options.dynamicKey]
? dynamicData[record.options.dynamicKey]
: []
"
:disabled="record.options.disabled || parentDisabled"
:allowClear="record.options.clearable"
:value="value"
@change="handleChange"
/>
<!-- 级联选择器 -->
<a-cascader
v-else-if="record.type === 'cascader'"
:style="`width:${record.options.width}`"
:placeholder="record.options.placeholder"
:showSearch="record.options.showSearch"
:options="
!record.options.dynamic
? record.options.options
: dynamicData[record.options.dynamicKey]
? dynamicData[record.options.dynamicKey]
: []
"
:disabled="record.options.disabled || parentDisabled"
:allowClear="record.options.clearable"
:value="value"
@change="handleChange"
/>
</a-form-model-item>
<!-- 文本 -->
<a-form-model-item v-else-if="record.type === 'text'">
<div :style="{ textAlign: record.options.textAlign }">
<label
:class="{ 'ant-form-item-required': record.options.showRequiredMark }"
v-text="record.label"
></label>
</div>
</a-form-model-item>
<!-- html -->
<div
v-else-if="record.type === 'html'"
v-html="record.options.defaultValue"
></div>
<div v-else>
<!-- 空 -->
</div>
</template>
<script>
/*
* author kcz
* date 2019-11-20
*/
// import moment from "moment";
import UploadFile from "../../UploadFile";
import UploadImg from "../../UploadImg";
import KDatePicker from "../../KDatePicker";
import KTimePicker from "../../KTimePicker";
export default {
name: "KFormItem",
props: [
"record",
"domains",
"index",
"value",
"parentDisabled",
"dynamicData",
"config"
],
components: {
UploadImg,
UploadFile,
KDatePicker,
KTimePicker
},
computed: {
customList() {
if (window.$customComponentList) {
return window.$customComponentList.map(item => item.type);
} else {
return [];
}
}
},
methods: {
handleChange(e) {
this.$emit("input", e);
}
}
};
</script>
<style lang="less" scoped>
.slider-box {
display: flex;
> .slider {
flex: 1;
margin-right: 16px;
}
> .number {
width: 70px;
}
}
</style>
<template>
<div class="option-change-container">
<a-row v-if="type === 'option' || type === 'tab'" :gutter="8">
<div class="option-change-box" v-for="(val, index) in value" :key="index">
<a-col :span="9"
><a-input v-model="val.label" placeholder="名称"
/></a-col>
<a-col :span="9"><a-input v-model="val.value" placeholder="值"/></a-col>
<a-col :span="6"
><div @click="handleDelete(index)" class="option-delete-box">
<a-icon type="delete" /></div
></a-col>
</div>
<a-col :span="24"><a @click="handleAdd">添加</a></a-col>
</a-row>
<a-row v-if="type === 'rules'" :gutter="8">
<span v-for="(val, index) in value" :key="index">
<div class="option-change-box" v-if="index !== 0">
<a-col :span="18"
><a-input v-model="val.message" placeholder="提示信息"
/></a-col>
<a-col :span="18"
><a-input v-model="val.pattern" placeholder="正则表达式pattern"
/></a-col>
<a-col :span="6"
><div @click="handleDelete(index)" class="option-delete-box">
<a-icon type="delete" /></div
></a-col>
</div>
</span>
<a-col :span="24"><a @click="handleAddRules">增加校验</a></a-col>
</a-row>
<a-row v-else-if="type === 'colspan'" :gutter="8">
<div class="option-change-box" v-for="(val, index) in value" :key="index">
<a-col :span="18"
><a-input-number
style="width:100%"
:max="24"
v-model="val.span"
placeholder="名称"
/></a-col>
<a-col :span="6"
><div @click="handleDelete(index)" class="option-delete-box">
<a-icon type="delete" /></div
></a-col>
</div>
<a-col :span="24"><a @click="handleAddCol">添加</a></a-col>
</a-row>
</div>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 修改多选、下拉、单选等控件options的组件,添加移除校验规制的组件
*/
export default {
name: "KChangeOption",
props: {
value: {
type: Array,
required: true
},
type: {
type: String,
default: "option"
}
},
methods: {
handleAdd() {
// 添加
let addData = [
...this.value,
{
value: `${this.value.length + 1}`,
label: "选项" + (this.value.length + 1),
list: this.type === "tab" ? [] : undefined
}
];
this.$emit("input", addData);
},
handleAddCol() {
// 添加栅格Col
let addData = [
...this.value,
{
span: 12,
list: []
}
];
this.$emit("input", addData);
},
handleAddRules() {
let addData = [
...this.value,
{
pattern: "",
message: ""
}
];
this.$emit("input", addData);
},
handleDelete(deleteIndex) {
// 删除
this.$emit(
"input",
this.value.filter((val, index) => index !== deleteIndex)
);
}
}
};
</script>
<style lang="less" scoped>
.option-change-container {
width: calc(100% - 8px);
}
.option-change-box {
height: 38px;
padding-bottom: 6px;
.option-delete-box {
margin-top: 3px;
background: #ffe9e9;
color: #f22;
width: 32px;
height: 32px;
line-height: 32px;
text-align: center;
border-radius: 50%;
overflow: hidden;
transition: all 0.3s;
&:hover {
background: #f22;
color: #fff;
}
}
}
</style>
<template>
<a-checkbox :val="_val" @change="handleChange" :checked="chackboxVal">
{{ label }}
</a-checkbox>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 多选框组件,改成v-model Boolean值
*/
export default {
name: "kCheckbox",
data() {
return {
chackboxVal: false
};
},
props: {
value: {
type: Boolean,
default: false
},
label: {
type: String,
default: ""
}
},
computed: {
_val() {
this.handleSetChackboxVal(this.value);
return this.value;
}
},
methods: {
handleChange(e) {
this.$emit("input", e.target.checked);
},
handleSetChackboxVal(val) {
this.chackboxVal = val;
}
}
};
</script>
<!--
* @Description: 日期选择器
* @Author: kcz
* @Date: 2020-01-11 15:38:28
* @LastEditors: kcz
* @LastEditTime: 2020-03-28 17:37:49
-->
<template>
<!-- 月份选择 -->
<a-month-picker
:style="`width:${record.options.width}`"
v-if="
record.type === 'date' &&
record.options.format === 'YYYY-MM' &&
record.options.range === false
"
:disabled="record.options.disabled || parentDisabled"
:allowClear="record.options.clearable"
:placeholder="record.options.placeholder"
:format="record.options.format"
@change="handleSelectChange"
:value="date"
/>
<!-- 日期选择 -->
<a-date-picker
:style="`width:${record.options.width}`"
v-else-if="record.type === 'date' && record.options.range === false"
:disabled="record.options.disabled || parentDisabled"
:show-time="record.options.showTime"
:allowClear="record.options.clearable"
:placeholder="record.options.placeholder"
:format="record.options.format"
@change="handleSelectChange"
:value="date"
/>
<!-- 范围日期选择 -->
<a-range-picker
:style="`width:${record.options.width}`"
v-else-if="record.type === 'date' && record.options.range === true"
:show-time="record.options.showTime"
:disabled="record.options.disabled || parentDisabled"
:allowClear="record.options.clearable"
:placeholder="record.options.rangePlaceholder"
:format="record.options.format"
@change="handleSelectChange"
:value="date"
/>
</template>
<script>
import moment from "moment";
export default {
// eslint-disable-next-line vue/require-prop-types
props: ["record", "value", "parentDisabled"],
data() {
return {
// date: undefined
};
},
computed: {
date() {
if (
!this.value ||
(this.record.options.range && this.value.length === 0)
) {
return undefined;
} else if (this.record.options.range) {
return this.value.map(item => moment(item, this.record.options.format));
} else {
return moment(this.value, this.record.options.format);
}
}
},
methods: {
handleSelectChange(val) {
let date;
if (!val || (this.record.options.range && val.length === 0)) {
date = "";
} else if (this.record.options.range) {
date = val.map(item => item.format(this.record.options.format));
} else {
date = val.format(this.record.options.format);
}
this.$emit("change", date);
this.$emit("input", date);
}
}
};
</script>
import DatePicker from "./datePicker.vue";
export default DatePicker;
/*
* @Description:导出富文本编辑器
* @Author: kcz
* @Date: 2020-03-30 12:45:04
* @LastEditors: kcz
* @LastEditTime: 2020-03-30 12:45:40
*/
import KEditor from "./kEditor.vue";
export default KEditor;
<!--
* @Description: 对vue-quill-editor封装
* @Author: kcz
* @Date: 2020-03-30 12:44:03
* @LastEditors: kcz
* @LastEditTime: 2020-04-26 19:21:27
-->
<template>
<quillEditor
:style="{ height: `${record.options.height}px` }"
:value="value"
ref="vueQuillEditor"
class="ql-editor-class"
:class="{ chinesization: record.options.chinesization }"
:options="editorOption"
:disabled="record.options.disabled || parentDisabled"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
>
</quillEditor>
</template>
<script>
import { quillEditor } from "vue-quill-editor"; //调用编辑器
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
export default {
name: "editor",
props: ["value", "record", "parentDisabled"],
components: { quillEditor },
data() {
return {
editorOption: {
placeholder: this.record.options.placeholder
}
};
},
methods: {
onEditorBlur() {}, // 失去焦点事件
onEditorFocus() {}, // 获得焦点事件
onEditorChange(e) {
this.$emit("change", e.html);
}
}
};
</script>
<style lang="less" scoped>
.ql-editor-class {
-webkit-box-sizing: border-box;
box-sizing: border-box;
line-height: 1.42;
height: 100%;
outline: none;
padding: 0 0 66px;
tab-size: 4;
-moz-tab-size: 4;
text-align: left;
word-wrap: break-word;
}
</style>
KFormBuild
====
构建表单
将传入的json数组,解析成ant UI组件
### 使用方式
```vue
<template>
<k-form-build :value="jsonData" />
</template>
<script>
const jsonData = {
"list": [
{
"type": "input",
"name": "单行文本",
"options": {
"width": "100%",
"defaultValue": "",
"placeholder": "请输入",
"disabled": false
},
"model": "input_1569051592984",
"key": "input_1569051592984",
"rules": [
{
"required": false,
"message": "必填项"
}
]
}
],
"config": {
"layout": "horizontal",
"labelCol": {
"span": 4
},
"wrapperCol": {
"span": 18
},
"hideRequiredMark": false,
"customStyle": ""
}
}
export default {
data() {
return {
form: this.$form.createForm(this),
jsonData
}
}
}
</script>
```
父组件可调用函数 getData - 返回Porimse对象
<template>
<!-- 标签页布局 -->
<a-tabs
v-if="record.type === 'tabs'"
class="grid-row"
:default-active-key="0"
:tabBarGutter="record.options.tabBarGutter"
:type="record.options.type"
:size="record.options.size"
:tabPosition="record.options.tabPosition"
:animated="record.options.animated"
>
<a-tab-pane
v-for="(tabItem, index) in record.columns"
:key="index"
:tab="tabItem.label"
>
<buildBlocks
ref="nestedComponents"
@handleReset="$emit('handleReset')"
@change="handleChange"
v-for="item in tabItem.list"
:disabled="disabled"
:dynamicData="dynamicData"
:key="item.key"
:record="item"
:formConfig="formConfig"
:config="config"
/>
</a-tab-pane>
</a-tabs>
<!-- 栅格布局 -->
<a-row
v-else-if="record.type === 'grid'"
class="grid-row"
:gutter="record.options.gutter"
>
<a-col
class="grid-col"
v-for="(colItem, idnex) in record.columns"
:key="idnex"
:span="colItem.span || 0"
>
<buildBlocks
ref="nestedComponents"
@handleReset="$emit('handleReset')"
@change="handleChange"
v-for="item in colItem.list"
:disabled="disabled"
:dynamicData="dynamicData"
:key="item.key"
:record="item"
:formConfig="formConfig"
:config="config"
/>
</a-col>
</a-row>
<!-- 卡片布局 -->
<a-card
v-else-if="record.type === 'card'"
class="grid-row"
:title="record.label"
>
<buildBlocks
ref="nestedComponents"
@handleReset="$emit('handleReset')"
@change="handleChange"
v-for="item in record.list"
:disabled="disabled"
:dynamicData="dynamicData"
:key="item.key"
:record="item"
:formConfig="formConfig"
:config="config"
/>
</a-card>
<!-- 表格布局 -->
<table
v-else-if="record.type === 'table'"
class="kk-table-9136076486841527"
:class="{
bright: record.options.bright,
small: record.options.small,
bordered: record.options.bordered
}"
:style="record.options.customStyle"
>
<tr v-for="(trItem, trIndex) in record.trs" :key="trIndex">
<td
class="table-td"
v-for="(tdItem, tdIndex) in trItem.tds"
:key="tdIndex"
:colspan="tdItem.colspan"
:rowspan="tdItem.rowspan"
>
<buildBlocks
ref="nestedComponents"
@handleReset="$emit('handleReset')"
@change="handleChange"
v-for="item in tdItem.list"
:disabled="disabled"
:dynamicData="dynamicData"
:key="item.key"
:record="item"
:formConfig="formConfig"
:config="config"
/>
</td>
</tr>
</table>
<KFormItem
v-else
ref="nestedComponents"
@handleReset="$emit('handleReset')"
@change="handleChange"
:disabled="disabled"
:dynamicData="dynamicData"
:key="record.key"
:record="record"
:formConfig="formConfig"
:config="config"
/>
</template>
<script>
/*
* author kcz
* date 2019-11-20
*/
import KFormItem from "../KFormItem/index";
export default {
name: "buildBlocks",
props: {
record: {
type: Object,
required: true
},
formConfig: {
type: Object,
required: true
},
config: {
type: Object,
default: () => ({})
},
dynamicData: {
type: Object,
required: true
},
disabled: {
type: Boolean,
default: false
}
},
components: {
KFormItem
},
methods: {
validationSubform() {
// 验证动态表格
let nestedComponents = this.$refs.nestedComponents;
if (
typeof nestedComponents === "object" &&
nestedComponents instanceof Array
) {
for (let i = 0; nestedComponents.length > i; i++) {
if (!nestedComponents[i].validationSubform()) {
return false;
}
}
return true;
} else if (typeof nestedComponents !== "undefined") {
return nestedComponents.validationSubform();
} else {
return true;
}
},
handleChange(value, key) {
this.$emit("change", value, key);
}
}
};
</script>
/*
* author kcz
* date 2019-11-20
*/
import KFormBuild from "./index.vue";
KFormBuild.install = function(Vue) {
Vue.component(KFormBuild.name, KFormBuild);
};
export default KFormBuild;
<template>
<a-config-provider :locale="locale">
<a-form
v-if="
typeof value.list !== 'undefined' && typeof value.config !== 'undefined'
"
class="k-form-build-9136076486841527"
:layout="value.config.layout"
:hideRequiredMark="value.config.hideRequiredMark"
:form="form"
@submit="handleSubmit"
:style="value.config.customStyle"
>
<buildBlocks
ref="buildBlocks"
@handleReset="reset"
v-for="(record, index) in value.list"
:record="record"
:dynamicData="dynamicData"
:config="config"
:disabled="disabled"
:formConfig="value.config"
:key="index"
@change="handleChange"
/>
</a-form>
</a-config-provider>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 将json数据构建成表单
*/
import buildBlocks from "./buildBlocks";
import zhCN from "ant-design-vue/lib/locale-provider/zh_CN";
// import moment from "moment";
export default {
name: "KFormBuild",
data() {
return {
locale: zhCN,
form: this.$form.createForm(this)
};
},
// props: ["value", "dynamicData"],
props: {
value: {
type: Object,
required: true
},
dynamicData: {
type: Object,
default: () => ({})
},
config: {
type: Object,
default: () => ({})
},
disabled: {
type: Boolean,
default: false
},
outputString: {
type: Boolean,
default: false
},
defaultValue: {
type: Object,
default: () => ({})
}
},
components: {
buildBlocks
},
methods: {
// moment,
handleSubmit(e) {
// 提交按钮触发,并触发submit函数,返回getData函数
e.preventDefault();
this.$emit("submit", this.getData);
},
reset() {
// 重置表单
this.form.resetFields();
},
getData() {
// 提交函数,提供父级组件调用
return new Promise((resolve, reject) => {
try {
this.form.validateFields((err, values) => {
if (err) {
reject(err);
}
this.$refs.buildBlocks.forEach(item => {
if (!item.validationSubform()) {
reject(err);
}
});
if (this.outputString) {
// 需要所有value转成字符串
for (let key in values) {
let type = typeof values[key];
if (type === "string" || type === "undefined") {
continue;
} else if (type === "object") {
values[key] = `k-form-design#${type}#${JSON.stringify(
values[key]
)}`;
} else {
values[key] = `k-form-design#${type}#${String(values[key])}`;
}
}
resolve(values);
} else {
resolve(values);
}
});
} catch (err) {
reject(err);
}
});
},
setData(json) {
return new Promise((resolve, reject) => {
try {
if (this.outputString) {
// 将非string数据还原
for (let key in json) {
if (!json[key].startsWith("k-form-design#")) {
continue;
}
let array = json[key].split("#");
if (array[1] === "object") {
json[key] = JSON.parse(array[2]);
} else if (array[1] === "number") {
json[key] = Number(array[2]);
} else if (array[1] === "boolean") {
json[key] = Boolean(array[2]);
}
}
this.form.setFieldsValue(json);
} else {
this.form.setFieldsValue(json);
}
} catch (err) {
reject(err);
}
});
},
// 批量设置某个option的值
setOptions(fields, optionName, value) {
fields = new Set(fields);
// 递归遍历控件树
const traverse = array => {
array.forEach(element => {
if (fields.has(element.model)) {
element.options[optionName] = value;
}
if (element.type === "grid" || element.type === "tabs") {
// 栅格布局 and 标签页
element.columns.forEach(item => {
traverse(item.list);
});
} else if (element.type === "card" || element.type === "batch") {
// 卡片布局 and 动态表格
traverse(element.list);
} else if (element.type === "table") {
// 表格布局
element.trs.forEach(item => {
item.tds.forEach(val => {
traverse(val.list);
});
});
}
});
};
traverse(this.value.list);
},
// 隐藏表单字段
hide(fields) {
this.setOptions(fields, "hidden", true);
},
// 显示表单字段
show(fields) {
this.setOptions(fields, "hidden", false);
},
// 禁用表单字段
disable(fields) {
this.setOptions(fields, "disabled", true);
},
// 启用表单字段
enable(fields) {
this.setOptions(fields, "disabled", false);
},
handleChange(value, key) {
// 触发change事件
this.$emit("change", value, key);
}
},
mounted() {
this.$nextTick(() => {
this.setData(this.defaultValue);
});
}
};
</script>
/*
* author kcz
* date 2019-11-20
* description 默认导入json数据格式
*/
export default `{
"list": [
{
"type": "input",
"label": "输入框",
"options": {
"type": "text",
"width": "100%",
"defaultValue": "",
"placeholder": "请输入",
"clearable": false,
"maxLength": null,
"hidden": false,
"disabled": false
},
"model": "input_1603939737389",
"key": "input_1603939737389",
"rules": [
{
"required": false,
"message": "必填项"
}
]
}
],
"config": {
"layout": "horizontal",
"labelCol": {
"xs": 4,
"sm": 4,
"md": 4,
"lg": 4,
"xl": 4,
"xxl": 4
},
"wrapperCol": {
"xs": 18,
"sm": 18,
"md": 18,
"lg": 18,
"xl": 18,
"xxl": 18
},
"hideRequiredMark": false,
"customStyle": ""
}
}`;
/*
* author kcz
* date 2019-11-20
*/
import KFormDesign from "./index.vue";
KFormDesign.install = function(Vue) {
Vue.component(KFormDesign.name, KFormDesign);
};
export default KFormDesign;
差异被折叠。
<!--
* @Description:
* @Author: kcz
* @Date: 2019-12-30 00:37:05
* @LastEditors: kcz
* @LastEditTime: 2020-03-22 20:56:48
-->
<template>
<a-modal
title="代码"
:footer="null"
:visible="visible"
@cancel="handleCancel"
wrapClassName="code-modal-9136076486841527"
style="top:20px;"
width="850px"
:destroyOnClose="true"
>
<a-tabs tabPosition="left" style="height:100%">
<a-tab-pane tab="VUE" key="1">
<!-- vue code start -->
<previewCode :editorJson="editorVueJson" fileFormat="vue" />
<!-- vue code end -->
</a-tab-pane>
<a-tab-pane tab="HTML" key="2">
<!-- html code start -->
<previewCode :editorJson="editorHtmlJson" fileFormat="html" />
<!-- html code end -->
</a-tab-pane>
</a-tabs>
</a-modal>
</template>
<script>
let codeVueFront = `<template>
<div>
<k-form-build
:value="jsonData"
ref="KFB"
@submit="handleSubmit"
/>
<button @click="getData">提交</button>
</div>
</template>
<script>
export default {
name: 'Demo',
data () {
return {
jsonData: `;
/* eslint-disable */
let codeVueLast = `
}
},
methods: {
handleSubmit(p) {
// 通过表单提交按钮触发,获取promise对象
p().then(res => {
// 获取数据成功
alert(JSON.stringify(res))
})
.catch(err => {
console.log(err, '校验失败')
})
},
getData() {
// 通过函数获取数据
this.$refs.KFB.getData().then(res => {
// 获取数据成功
alert(JSON.stringify(res))
})
.catch(err => {
console.log(err, '校验失败')
})
}
}
}
<\/script>`;
let codeHtmlFront = `<!DOCTYPE html>
<html>
<head>
<title>表单设计器kcz</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="">
</head>
<body>
<div class="app">
<k-form-build ref="KFB" @submit="handleSubmit" :value="jsonData"></k-form-build>
<button @click="getData">提交</button>
</div>
<script src="http://cdn.kcz66.com/vue.min.js"><\/script>
<script src=""><\/script>
<script>
let jsonData = `;
let codeHtmlLast = `
let vm = new Vue({
el: '.app',
data: {
jsonData
},
methods: {
handleSubmit(p) {
// 通过表单提交按钮触发,获取promise对象
p().then(res => {
// 获取数据成功
alert(JSON.stringify(res))
})
.catch(err => {
console.log(err, '校验失败')
})
},
getData() {
// 通过函数获取数据
this.$refs.KFB.getData().then(res => {
// 获取数据成功
alert(JSON.stringify(res))
})
.catch(err => {
console.log(err, '校验失败')
})
}
}
})
<\/script>
</body>
</html>`
/* eslint-enable */
import previewCode from "../../PreviewCode/index";
export default {
name: "CodeModal",
data() {
return {
visible: false,
editorVueJson: "",
editorHtmlJson: "",
jsonData: {}
};
},
watch: {
visible(val) {
if (val) {
this.editorVueJson =
codeVueFront + JSON.stringify(this.jsonData) + codeVueLast;
this.editorHtmlJson =
codeHtmlFront + JSON.stringify(this.jsonData) + codeHtmlLast;
}
}
},
components: {
previewCode
},
methods: {
handleCancel() {
this.visible = false;
}
}
};
</script>
<!--
* @Description: 折叠组件
* @Author: kcz
* @Date: 2020-01-13 00:37:54
* @LastEditors: kcz
* @LastEditTime: 2020-03-28 11:32:39
-->
<template>
<draggable
tag="ul"
:value="list"
v-bind="{
group: { name: 'form-draggable', pull: 'clone', put: false },
sort: false,
animation: 180,
ghostClass: 'moving'
}"
@start="handleStart($event, list)"
>
<li
v-for="(val, index) in list"
:key="index"
@dragstart="$emit('generateKey', list, index)"
@click="$emit('handleListPush', val)"
>
<svg v-if="val.icon" class="icon" aria-hidden="true">
<use :xlink:href="`#${val.icon}`"></use>
</svg>
{{ val.label }}
</li>
</draggable>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "collapseItem",
props: ["list"],
components: {
draggable
},
methods: {
handleStart(e, list) {
this.$emit("start", list[e.oldIndex].type);
}
}
};
</script>
<!--
* @Description:
* @Author: kcz
* @Date: 2019-12-26 21:15:52
* @LastEditors : kcz
* @LastEditTime : 2020-01-13 20:13:15
-->
<template>
<footer class="footer-9136076486841527">
<a target="_new_page" href="https://github.com/Kchengz/k-form-design"
>k-form-design</a
>
</footer>
</template>
<!--
* @Author: kcz
* @Date: 2019-12-30 00:37:05
* @LastEditTime: 2020-06-08 20:27:55
* @LastEditors: kcz
* @Description: 将数据通过k-form-item组件解析,生成控件
* @FilePath: \k-form-design\packages\KFormDesign\module\formNode.vue
-->
<template>
<div
class="drag-move-box"
@click.stop="$emit('handleSelectItem', record)"
:class="{ active: record.key === selectItem.key }"
>
<div class="form-item-box">
<kFormItem :formConfig="config" :record="record" />
</div>
<div v-if="!hideModel" class="show-key-box" v-text="record.model" />
<div
class="copy"
:class="record.key === selectItem.key ? 'active' : 'unactivated'"
@click.stop="$emit('handleCopy')"
>
<a-icon type="copy" />
</div>
<div
class="delete"
:class="record.key === selectItem.key ? 'active' : 'unactivated'"
@click.stop="$emit('handleDelete')"
>
<a-icon type="delete" />
</div>
</div>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 通过json生成的单个表单节点
*/
import kFormItem from "../../KFormItem/index";
export default {
props: {
record: {
type: Object,
required: true
},
selectItem: {
type: Object,
default: () => {}
},
config: {
type: Object,
required: true
},
hideModel: {
type: Boolean,
default: false
}
},
components: {
kFormItem
}
};
</script>
<template>
<div class="properties-centent kk-checkbox">
<div class="head-title">
表单属性设置
</div>
<div class="properties-body">
<a-form>
<a-form-item
v-if="typeof config.layout !== 'undefined'"
label="表单布局"
>
<a-radio-group buttonStyle="solid" v-model="config.layout">
<a-radio-button value="horizontal">水平</a-radio-button>
<a-radio-button value="vertical">垂直</a-radio-button>
<a-radio-button value="inline">行内</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="labelCol(水平布局生效)">
<div class="change-col-box">
<a-slider
id="test"
:max="24"
:min="0"
v-model="config.labelCol.xs"
@change="handleChangeCol"
/>
<div>
<label>xs:</label>
<a-input-number v-model="config.labelCol.xs" />
</div>
<div>
<label>sm:</label>
<a-input-number v-model="config.labelCol.sm" />
</div>
<div>
<label>md:</label>
<a-input-number v-model="config.labelCol.md" />
</div>
<div>
<label>lg:</label>
<a-input-number v-model="config.labelCol.lg" />
</div>
<div>
<label>xl:</label>
<a-input-number v-model="config.labelCol.xl" />
</div>
<div>
<label>xxl:</label>
<a-input-number v-model="config.labelCol.xxl" />
</div>
</div>
</a-form-item>
<a-form-item label="wrapperCol(水平布局生效)">
<div class="change-col-box">
<div>
<label>xs:</label>
<a-input-number v-model="config.wrapperCol.xs" />
</div>
<div>
<label>sm:</label>
<a-input-number v-model="config.wrapperCol.sm" />
</div>
<div>
<label>md:</label>
<a-input-number v-model="config.wrapperCol.md" />
</div>
<div>
<label>lg:</label>
<a-input-number v-model="config.wrapperCol.lg" />
</div>
<div>
<label>xl:</label>
<a-input-number v-model="config.wrapperCol.xl" />
</div>
<div>
<label>xxl:</label>
<a-input-number v-model="config.wrapperCol.xxl" />
</div>
</div>
</a-form-item>
<a-form-item label="预览模态框宽度">
<a-input-number style="width:100%;" v-model="previewOptions.width" />
</a-form-item>
<a-form-item label="表单CSS">
<a-input v-model="config.customStyle" />
</a-form-item>
<a-form-item label="表单属性">
<kCheckbox
v-if="typeof config.hideRequiredMark !== 'undefined'"
v-model="config.hideRequiredMark"
label="隐藏必选标记"
/>
</a-form-item>
<a-form-item label="提示">
实际预览效果请点击预览查看
</a-form-item>
</a-form>
</div>
</div>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 表单属性设置面板组件
*/
import kCheckbox from "../../KCheckbox/index.vue";
export default {
name: "formProperties",
components: {
kCheckbox
},
props: {
config: {
type: Object,
required: true
},
previewOptions: {
type: Object,
required: true
}
},
methods: {
handleChangeCol(e) {
this.config.labelCol.xs = this.config.labelCol.sm = this.config.labelCol.md = this.config.labelCol.lg = this.config.labelCol.xl = this.config.labelCol.xxl = e;
this.config.wrapperCol.xs = this.config.wrapperCol.sm = this.config.wrapperCol.md = this.config.wrapperCol.lg = this.config.wrapperCol.xl = this.config.wrapperCol.xxl =
24 - e;
}
}
};
</script>
<style lang="less" scoped>
.change-col-box {
> div {
padding: 5px;
display: flex;
> label {
text-align: right;
padding-right: 8px;
display: block;
font-size: 16px;
width: 45px;
}
}
}
</style>
<!--
* @Description: 头部
* @Author: kcz
* @Date: 2019-12-30 00:37:05
* @LastEditors: kcz
* @LastEditTime: 2020-03-26 20:18:56
-->
<template>
<header class="header" v-text="title"></header>
</template>
<script>
export default {
props: {
title: {
type: String,
default: "表单设计器"
}
}
};
</script>
<template>
<a-modal
title="JSON数据"
:visible="visible"
@ok="handleImportJson"
@cancel="handleCancel"
cancelText="关闭"
:destroyOnClose="true"
wrapClassName="code-modal-9136076486841527"
style="top:20px;"
width="850px"
>
<p class="hint-box">导入格式如下:</p>
<div class="json-box-9136076486841527">
<codemirror
style="height:100%;"
ref="myEditor"
v-model="jsonFormat"
></codemirror>
</div>
<a-upload
action="/abc"
:beforeUpload="beforeUpload"
:showUploadList="false"
accept="application/json"
>
<a-button type="primary"> 导入json文件 </a-button>
</a-upload>
</a-modal>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 导入json Modal
*/
import { codemirror } from "vue-codemirror-lite";
import jsonFormat from "../config/jsonFormat";
export default {
name: "importJsonModal",
data() {
return {
visible: false,
jsonFormat,
jsonData: {},
handleSetSelectItem: null
};
},
watch: {
visible(val) {
if (val) {
this.jsonFormat = jsonFormat;
}
}
},
components: {
codemirror
},
computed: {
editor() {
// get current editor object
return this.$refs.myEditor.editor;
}
},
methods: {
handleCancel() {
this.visible = false;
},
beforeUpload(e) {
// 通过json文件导入
let _this = this;
let reader = new FileReader();
reader.readAsText(e);
reader.onload = function() {
_this.jsonFormat = this.result;
_this.handleImportJson();
};
return false;
},
handleImportJson() {
// 导入JSON
try {
const editorJsonData = JSON.parse(this.jsonFormat);
this.jsonData.list = editorJsonData.list;
this.jsonData.config = editorJsonData.config;
this.jsonData.config.layout = editorJsonData.config.layout;
this.handleCancel();
// 导入之后,需要清除已选择key
this.handleSetSelectItem({ key: "" });
this.$message.success("导入成功");
} catch (error) {
this.$message.error("导入失败,数据格式不对");
}
}
}
};
</script>
<style lang="less" scoped>
.hint-box {
background: #e9e9e9;
margin: 0;
border-bottom: 2px solid #fff;
}
</style>
<template>
<a-modal
title="JSON数据"
:footer="null"
:visible="visible"
@cancel="handleCancel"
:destroyOnClose="true"
wrapClassName="code-modal-9136076486841527"
style="top:20px;"
width="850px"
>
<previewCode :editorJson="editorJson" />
</a-modal>
</template>
<script>
/*
* author kcz
* date 2019-11-20
* description 生成json Modal
*/
import previewCode from "../../PreviewCode/index";
export default {
name: "JsonModal",
data() {
return {
visible: false,
editorJson: "",
jsonData: {}
};
},
watch: {
visible(val) {
if (val) {
this.editorJson = JSON.stringify(this.jsonData, null, "\t");
}
}
},
components: {
previewCode
},
methods: {
handleCancel() {
this.visible = false;
}
}
};
</script>
<!--
* @Description: 头部
* @Author: kcz
* @Date: 2019-12-30 00:37:05
* @LastEditors: kcz
* @LastEditTime: 2020-03-26 20:05:57
-->
<template>
<div class="operating-area">
<!-- 头部操作按钮区域 start -->
<!-- 操作左侧区域 start -->
<div class="left-btn-box">
<a-tooltip title="保存">
<a v-if="toolbars.includes('save')" @click="$emit('handleSave')">
<a-icon type="save" />
<span v-if="showToolbarsText">保存</span>
</a>
</a-tooltip>
<a-tooltip title="预览">
<a v-if="toolbars.includes('preview')" @click="$emit('handlePreview')">
<a-icon type="chrome" />
<span v-if="showToolbarsText">预览</span>
</a>
</a-tooltip>
<a-tooltip title="导入">
<a
v-if="toolbars.includes('importJson')"
@click="$emit('handleOpenImportJsonModal')"
>
<a-icon type="upload" />
<span v-if="showToolbarsText">导入</span>
</a>
</a-tooltip>
<a-tooltip title="生成JSON">
<a
v-if="toolbars.includes('exportJson')"
@click="$emit('handleOpenJsonModal')"
>
<a-icon type="credit-card" />
<span v-if="showToolbarsText">生成JSON</span>
</a>
</a-tooltip>
<a-tooltip title="生成代码">
<a
v-if="toolbars.includes('exportCode')"
@click="$emit('handleOpenCodeModal')"
>
<a-icon type="code" />
<span v-if="showToolbarsText">生成代码</span>
</a>
</a-tooltip>
<a-tooltip title="清空">
<a v-if="toolbars.includes('reset')" @click="$emit('handleReset')">
<a-icon type="delete" />
<span v-if="showToolbarsText">清空</span>
</a>
</a-tooltip>
<!-- 按钮左侧插槽 start -->
<slot name="left-action"></slot>
<!-- 按钮左侧插槽 end -->
</div>
<!-- 操作左侧区域 end -->
<!-- 操作右侧区域 start -->
<div class="right-btn-box">
<!-- 按钮右侧插槽 start -->
<slot name="right-action"></slot>
<!-- 按钮右侧插槽 end -->
<a-tooltip title="关闭">
<a v-if="toolbars.includes('close')" @click="$emit('handleClose')">
<a-icon type="close" />
</a>
</a-tooltip>
</div>
<!-- 操作右侧区域 end -->
<!-- 头部操作按钮区域 end -->
</div>
<!-- 操作区域 start -->
</template>
<script>
export default {
props: {
toolbars: {
type: Array,
default: () => [
"save",
"preview",
"importJson",
"exportJson",
"exportCode",
"reset",
"close"
]
},
showToolbarsText: {
type: Boolean,
default: false
}
}
};
</script>
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
This source diff could not be displayed because it is too large. You can view the blob instead.
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
This source diff could not be displayed because it is too large. You can view the blob instead.
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论