This commit is contained in:
2026-02-06 18:43:18 +08:00
commit f1f8301bc8
382 changed files with 108163 additions and 0 deletions

3
.browserslistrc Normal file
View File

@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

28
.eslintrc.js Normal file
View File

@@ -0,0 +1,28 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
mocha: true
}
}
]
}

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
/node_modules

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# iMES_Vue
MES前端项目VUE3

View File

@@ -0,0 +1,7 @@
前端vue3环境配置如果没有安装过环境
1、先卸载原来vue2脚手架
npm uninstall vue-cli -g
2、安装vue3脚手架
npm install -g @vue/cli 如果安装很慢切换成taobao镜像 npm config set registry https://registry.npm.taobao.org
3、回滚原来vue2脚手架vue init webpack test
npm i -g @vue/cli-init

5
babel.config.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

1
build.bat Normal file
View File

@@ -0,0 +1 @@
npm run build

84
config/buttons.js Normal file
View File

@@ -0,0 +1,84 @@
let buttons = [{
name: "查 询",
value: 'Search',
icon: 'el-icon-search',
class: '',
type: 'primary',
onClick: function () {
this.search();
}
},
{
name: "新 建",
icon: 'el-icon-plus',
value: 'Add',
class: '',
// plain:true,
type: 'success',
// plain:true,
onClick: function () {
this.add();
}
},{
name: "编 辑",
icon: 'el-icon-edit',
value: 'Update',
// plain:true,
class: '',
type: 'primary',
onClick: function () {
this.edit();
}
}, {
name: "删 除",
icon: 'el-icon-delete',
class: '',
value: 'Delete',
type: 'danger',
onClick: function () {
this.del();
}
}, {
name: "审 核",
icon: 'el-icon-check',
class: '',
value: 'Audit',
plain:true,
type: 'primary',
onClick: function () {
this.audit();
}
},
{
name: "导 入",
icon: 'el-icon-top',
class: '',
type:'success',
plain:true,
value: 'Import',
onClick: function () {
this.import();
}
}, {
name: "导 出",
icon: 'el-icon-bottom',
type:'success',
plain:true,
value: 'Export',
onClick: function () {
this.export();
}
}
// , {
// name: "数据结构",
// icon: 'ios-cog',
// class: '',
// value: '',
// onClick: function () {
// this.openViewColumns();
// }
// }
]
export default buttons

1
install.bat Normal file
View File

@@ -0,0 +1 @@
npm install

33772
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

75
package.json Normal file
View File

@@ -0,0 +1,75 @@
{
"name": "imes.vue3",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@chenfengyuan/vue-qrcode": "^2.0.0",
"@element-plus/icons-vue": "^2.1.0",
"@microsoft/signalr": "^6.0.4",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"echarts": "^5.0.2",
"element-plus": "^2.2.15",
"file-save": "^0.2.0",
"file-saver": "^2.0.5",
"less": "^4.1.1",
"magix": "^3.8.16",
"qrcode": "^1.5.1",
"qrcode.vue": "^3.3.3",
"qrcodejs2": "0.0.2",
"sortable.js": "^0.3.0",
"sortablejs": "^1.15.0",
"uuidv1": "^1.6.14",
"vue": "^3.2.37",
"vue-draggable-next": "^2.0.1",
"vue-i18n": "^9.13.1",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0",
"wangeditor": "^4.7.6"
},
"devDependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-unit-mocha": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/test-utils": "^2.0.0-0",
"babel-eslint": "^10.1.0",
"chai": "^4.1.2",
"less": "^4.1.1",
"less-loader": "^7.3.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"@vue/standard"
],
"rules": {
"indent": [
1,
4
]
},
"parserOptions": {
"parser": "babel-eslint"
}
},
"eslintIgnore": [
"*"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

134
public/index.html Normal file
View File

@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="">
<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">
<meta name="keywords" content=".netccore,dotnet core,vue,element,element plus,vue3" />
<meta name="description" content="iMES-您的新一代工厂管家【商业版】" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<% if (process.env.NODE_ENV === 'production' ) { %>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?eb00554ca26601489c051a64e4915d60";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<% } %>
</body>
</html>
<style>
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
*{
box-sizing:border-box;
}
.el-loading {
z-index: 999999;
}
.el-table th {
display: table-cell !important;
}
.el-loading .el-loading-spinner {
padding: 7px;
background: #ececec;
width: 200px;
color: red;
left: 0;
right: 0;
margin: 0 auto;
border-radius: 5px;
border: 1px solid #a0a0a0;
}
h1,
h2,
h3,
h4 {
margin: 0;
}
.v-dialog {
border-radius: 5px;
top: 50%;
/* margin-top: -220px !important; */
}
.v-dialog .el-dialog__header {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 0px 13px;
line-height: 53px;
border-bottom: 1px solid #e2e2e2;
height: 50px;
color: white;
font-weight: bold;
font-size: 14px;
background-image: linear-gradient(135deg, #0cd7bd 10%, #50c3f7);
}
.v-dialog .el-dialog__header .el-dialog__headerbtn {
top: 3px;
right: 0px;
}
.v-dialog .el-dialog__header .el-dialog__headerbtn .el-dialog__close {
font-size: 19px;
color: white;
}
.v-dialog .el-dialog__body{
padding: 0;
}
.el-message{
z-index: 3500 !important;
}
.v-date-range .el-input__inner{
padding:0 15px 0 8px
}
.v-date-range .el-input__suffix .el-input__icon{
display: table-caption;
background: white;
margin: 1px;
height: auto;
margin-right: -4px;
height: 33px;
width: 19px;
font-size: 13px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
.v-date-range .el-icon-circle-check {
display: none !important;
}
.v-dialog .el-dialog__header {
margin-right: 0;
}
.el-button {
font-size: 12px !important;
}
.el-button--small {
padding: 15px 15px !important;
}
</style>

1
run.bat Normal file
View File

@@ -0,0 +1 @@
npm run serve

63
src/App.vue Normal file
View File

@@ -0,0 +1,63 @@
<template>
<div id="nav"></div>
<el-config-provider :locale="locale">
<router-view />
</el-config-provider>
</template>
<script>
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
export default {
name: "imes_app",
components: {
[ElConfigProvider.name]: ElConfigProvider, //添加组件
},
data() {
return {
locale: zhCn,
};
},
};
</script>
<style lang="stylus">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
height: 100%;
width: 100%;
}
.el-alert--error.is-light {
border: 1px solid #ffe0e0;
}
.el-alert--error.is-light {
color: #f74444 !important;
}
.el-alert--warning.is-light {
border: 1px solid #ffe6c1;
}
.el-alert--info.is-light {
border: 1px solid #e6e5e5;
}
.el-alert--info .el-alert__description {
color: #6b6b6b !important;
}
.el-alert--warning.is-light {
background-color: #fdf6ec;
color: #d68409 !important;
}
.el-alert--success.is-light {
border: 1px solid #cdf7b8;
}
.el-alert--success.is-light .el-alert__description {
color: #3baf02 !important;
}
</style>

315
src/api/http.js Normal file
View File

@@ -0,0 +1,315 @@
import axios from 'axios'
import store from '../store/index'
// import {getCurrentInstance} from 'vue'
import { useRouter, useRoute } from 'vue-router'
//const router = useRouter();
axios.defaults.timeout = 50000;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
import { ElLoading as Loading, ElMessage as Message } from 'element-plus';
let loadingInstance;
let loadingStatus = false;
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = 'http://localhost:9991/';
}
else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = 'http://localhost:9991/';
}
else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'http://120.48.150.78:8881/';
}
if (!axios.defaults.baseURL.endsWith('/')) {
axios.defaults.baseURL+="/";
}
let ipAddress = axios.defaults.baseURL;
let webAddress = "https://imes.625sc.com";
axios.interceptors.request.use((config) => {
return config;
}, (error) => {
return Promise.reject(error);
});
axios.interceptors.response.use((res) => {
closeLoading();
checkResponse(res);
return Promise.resolve(res);
}, (error) => {
closeLoading();
let httpMessage = '';
if (error.response) {
if (error.response.status == '401') {
if (error.response.data && error.response.data.code == 401) {
toLogin();
return;
}
}
if (error.response.status == '404') {
httpMessage = "未找到请求地址";
}
else if (error.response.data && error.response.data.message) {
httpMessage = error.response.data.message;
}
}
else {
httpMessage = '服务器处理异常'
}
redirect(httpMessage);
return Promise.reject(error.response || {}, httpMessage);
});
function closeLoading () {
if (loadingInstance) {
loadingInstance.close();
}
if (loadingStatus) {
loadingStatus = false;
if (loadingInstance) {
loadingInstance.close();
}
}
}
function checkResponse (res) {
//刷新token
if (!res.headers) {
if (res.getResponseHeader("vol_exp") == "1") {
replaceToken();
}
}
else if (res.headers.vol_exp == "1") {
replaceToken();
}
}
const _Authorization = 'Authorization';
function showLoading (loading) {
if (!loading || loadingStatus) {
return;
}
loadingInstance = Loading.service({
lock: true,
text: 'Loading',
customClass:"http-loading",
background: typeof loading == "string" ? loading : '正在处理.....',
background: 'rgba(58, 61, 63, 0.32)'
});
}
function getToken () {
return store.getters.getToken();
}
/*
url
params请求后台的参数,如:{name:123,values:['a','b','c']}
loading是否显示遮罩层,可以传入true.false.及提示文本
config配置信息,如{timeout:3000,headers:{token:123}}
*/
function post (url, params, loading, config) {
showLoading(loading);
axios.defaults.headers[_Authorization] = getToken();
return new Promise((resolve, reject) => {
axios.post(url, params, config)
.then(response => {
resolve(response.data);
}, err => {
reject(err && err.data && err.data.message ? err.data.message : '服务器处理异常');
})
.catch((error) => {
reject(error)
})
})
}
//=true异步请求时会显示遮罩层,=字符串,异步请求时遮罩层显示当前字符串
function get (url, param, loading, config) {
showLoading(loading);
axios.defaults.headers[_Authorization] = getToken();
return new Promise((resolve, reject) => {
axios.get(url, config)
.then(response => {
resolve(response.data)
}, err => {
reject(err)
})
.catch((error) => {
reject(error)
})
})
}
//url:url地址
//params:请求参数
//fileName:下载的文件名
//loading:是否显示加载状态
function download (url, params, fileName, loading,callback) {
fileName = fileName.replace(">", "").replace("<", "");
post(url, params, loading, { responseType: 'blob' }).then(content => {
const blob = new Blob([content])
if ('download' in document.createElement('a')) { // 非IE下载
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
} else { // IE10+下载
navigator.msSaveBlob(blob, fileName)
}
callback&&callback();
})
}
function createXHR () {
if (XMLHttpRequest) {
return new XMLHttpRequest();
}
if (ActiveXObject) {
if (typeof arguments.callee.activeXString != "string") {
var versions = [
"MSXML2.XMLHttp.6.0",
"MSXML2.XMLHttp",
"MSXML2.XMLHttp.3.0"
];
for (var i = 0; i < versions.length; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (e) {
console.log(e);
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
}
function redirect (responseText, message) {
try {
let responseData = typeof responseText == 'string' ? JSON.parse(responseText) : responseText;
if ((responseData.hasOwnProperty('code') && responseData.code == 401)
|| (responseData.data && responseData.data.code == 401)) {
closeLoading();
toLogin();
} else {
if (message) {
Message.error({
showClose: true,
message: message,
type: 'error'
});
}
}
} catch (error) {
console.log(error);
Message.error({
showClose: true,
message: responseText,
type: 'error'
});
}
}
function toLogin () {
// const vueinstance= getCurrentInstance();
if (window.location.hash) {
window.location.href = window.location.origin + '/#/login'
return
}
window.location.href = window.location.origin + '/login'
// router.push({ path: '/login', params: { r: Math.random() } });
}
//动态刷新token
function replaceToken () {
ajax({
url: "/api/User/replaceToken",
param: {},
json: true,
success: function (x) {
if (x.status) {
let userInfo = store.getters.getUserInfo();
userInfo.token = x.data;
store.commit('setUserInfo', userInfo);
} else {
console.log(x.message);
toLogin();
}
},
errror: function (ex) {
console.log(ex);
toLogin();
},
type: "post",
async: false
});
}
function ajax (param) {
let httpParam =
Object.assign({
url: '', headers: {},
param: {}, json: true,
success: function () { },
errror: function () { },
type: 'post', async: true
}, param);
httpParam.url = axios.defaults.baseURL + httpParam.url.replace(/\/?/, '');
httpParam.headers[_Authorization] = getToken();
var xhr = createXHR();
xhr.onreadystatechange = function () {
if (xhr.status == 403 || xhr.status == 401) {
redirect(xhr.responseText);
return;
}
checkResponse(xhr);
if (xhr.readyState == 4 && xhr.status == 200) {
httpParam.success(httpParam.json ? JSON.parse(xhr.responseText) : xhr.responseText);
return;
}
if (xhr.status != 0 && xhr.readyState != 1) {
httpParam.errror(xhr);
}
};
//初始化请求
xhr.open(
httpParam.type,
httpParam.url,
httpParam.async
);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
for (const key in httpParam.headers) {
xhr.setRequestHeader(key, httpParam.headers[key]);
}
let dataStr = '';
for (const key in httpParam.param) {
dataStr += key + "=" + httpParam.param[key];
}
try {
xhr.send(dataStr);
} catch (error) {
toLogin();
}
}
ajax.post = function (url, param, success, errror) {
ajax({ url: url, param: param, success: success, error: errror, type: 'post' })
}
ajax.get = function (url, param, success, errror) {
ajax({ url: url, param: param, success: success, error: errror, type: 'get' })
}
export default { post, get,download, ajax, ipAddress, webAddress }

43
src/api/permission.js Normal file
View File

@@ -0,0 +1,43 @@
import http from '@/../src/api/http.js'
import buttons from '@/../config/buttons.js'
import store from '../store/index'
import { useRouter } from 'vue-router'
let permission = {
getMenu() {
return http.get("/api/getTreeMenu");
}, getButtons(path, extra, table, tableName) {//extra自定额外按钮
//table获取指定表的权限
if (table) { table = '/' + table; }
let permission = store.getters.getPermission(table || path);
if (!permission) {
permission = store.getters.getPermission(path.substring(1));
if (!permission) {
permission = store.getters.getPermission("/" + tableName);
if (!permission) {
to401();
return;
}
}
}
let permissions = permission.permission;//.split(',');
let gridButtons = buttons.filter(item => {
return !item.value || permissions.indexOf(item.value) != -1
});
if (extra && extra instanceof Array) {
gridButtons.push(...extra)
}
return gridButtons;
}, to401() {
to401();
}
}
function to401() {
const router = useRouter();
router.push({
path: '/401'
});
}
export default permission;

9
src/api/useTest.js Normal file
View File

@@ -0,0 +1,9 @@
const tipxx = {
install: function (vue) {
alert(1);
vue.prototype.$tip = function () {
alert('测试use')
};
}
}
export default { tipxx }

View File

@@ -0,0 +1,75 @@
*{
box-sizing:border-box;
-moz-box-sizing:border-box; /* Firefox */
-webkit-box-sizing:border-box; /* Safari */
}
.el-pager li{
font-weight: 100;
margin-right: 9px;
border: 1px solid #eee;
border-radius: 3px;
min-width: 28px;
}
.el-pager li.active,.el-pager li:hover{
background: #ed4014;
color: white;
}
.el-pagination__editor.el-input .el-input__inner{
height: 23px;
}
.animated {
-webkit-animation-duration: 0.5s;
animation-duration: 0.5s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
@media (print), (prefers-reduced-motion) {
.animated {
-webkit-animation: unset !important;
animation: unset !important;
-webkit-transition: none !important;
transition: none !important;
}
}
@-webkit-keyframes fadeInDown {
from {
opacity: 1;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
@keyframes fadeInDown {
from {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
}
.ivu-message{
z-index: 999999999 !important;
}
.ivu-form-item-content{
text-align: left;
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/assets/imgs/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/assets/imgs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/imgs/log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/assets/imgs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
src/assets/imgs/miniapp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
src/assets/imgs/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
src/assets/imgs/wechat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,2 @@
var test1 = function () { alert(11) }
export { test1 }

View File

@@ -0,0 +1,5 @@
//对vue参数进行扩展
var extend = function (param) {
console.log(param)
}
export { extend }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
//对vue参数进行扩展
var extend = function ($vueParam) {
$vueParam.methods.mesBoxFrom = function () {
this.$Message.info("扩展js,增加弹出消息");
this.$refs.mesBoxFrom.show();
}
//修改data属性:
let data = $vueParam.data();
data.formFileds['extend'] = "动态扩展字段";
data.formOptions.splice(0,0,{ filed: "extend", title: "动态增加字段", type: "text", required: true });
$vueParam.data = function () {
return data;
}
}
export { extend }

View File

@@ -0,0 +1,10 @@
<template>
<div style="text-align: center;font-size: 16px;padding: 20px;">正在加载资源...</div>
</template>
<script>
export default {
created() {
// console.log('loading')
}
};
</script>

View File

@@ -0,0 +1,59 @@
<template>
<el-alert
:title="'当前选中' + auditParam.rows + '条记录待审核..'"
type="success"
:closable="false"
>
</el-alert>
<div class="item">
<label>审核结果</label>
<el-radio-group v-model="auditParam.status">
<el-radio
v-for="item in auditParam.data"
:key="item.status"
:label="item.status"
>
<span>{{ item.text }}</span>
</el-radio>
</el-radio-group>
</div>
<div class="item">
<label style="margin-right: 13px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </label>
<el-input
v-model="auditParam.reason"
type="textarea"
style="margin-right: 13px;"
:autosize="{ minRows: 4, maxRows: 10 }"
placeholder="审核备注..."
></el-input>
</div>
</template>
<script>
export default {
props: {
auditParam: {
type: Object,
default: () => {
return {
auditParam: {
rows: 0,
model: false,
status: -1,
reason: "",
data: [], //[{ text: "通过", status: 1 }, { text: "拒绝", status: 2 }]
},
};
},
},
},
};
</script>
<style lang="less" scoped>
.item{
margin-top: 20px;
display: flex;
> label{
width: 86px;
}
}
</style>

View File

@@ -0,0 +1,3 @@
<template>
<div></div>
</template>

View File

@@ -0,0 +1,3 @@
<template>
<div id="test"></div>
</template>

View File

@@ -0,0 +1,345 @@
<template>
<div data-v-394040b0 class="icons">
<div
@click="select(index)"
v-for="(item, index) in icons"
:key="index"
class="icons-item"
>
<i
:class="[item, selectIndex == index ? 'active' : '']"
style="font-size: 32px"
></i>
<p>{{ item }}</p>
</div>
</div>
</template>
<script>
export default {
props: {
onSelect: {
type: Function,
default: () => {
return "";
},
},
},
methods: {
select(index) {
this.selectIndex = index;
this.onSelect(index < 0 ? "" : this.icons[index]);
},
},
data() {
return {
selectIndex: -1,
icons: [
//ivu-icon ivu-icon-ios-add
"el-icon-platform-eleme",
"el-icon-eleme",
"el-icon-delete-solid",
"el-icon-delete",
"el-icon-s-tools",
"el-icon-setting",
"el-icon-user-solid",
"el-icon-user",
"el-icon-phone",
"el-icon-phone-outline",
"el-icon-more",
"el-icon-more-outline",
"el-icon-star-on",
"el-icon-star-off",
"el-icon-s-goods",
"el-icon-goods",
"el-icon-warning",
"el-icon-warning-outline",
"el-icon-question",
"el-icon-info",
"el-icon-remove",
"el-icon-circle-plus",
"el-icon-success",
"el-icon-error",
"el-icon-zoom-in",
"el-icon-zoom-out",
"el-icon-remove-outline",
"el-icon-circle-plus-outline",
"el-icon-circle-check",
"el-icon-circle-close",
"el-icon-s-help",
"el-icon-help",
"el-icon-minus",
"el-icon-plus",
"el-icon-check",
"el-icon-close",
"el-icon-picture",
"el-icon-picture-outline",
"el-icon-picture-outline-round",
"el-icon-upload",
"el-icon-upload2",
"el-icon-download",
"el-icon-camera-solid",
"el-icon-camera",
"el-icon-video-camera-solid",
"el-icon-video-camera",
"el-icon-message-solid",
"el-icon-bell",
"el-icon-s-cooperation",
"el-icon-s-order",
"el-icon-s-platform",
"el-icon-s-fold",
"el-icon-s-unfold",
"el-icon-s-operation",
"el-icon-s-promotion",
"el-icon-s-home",
"el-icon-s-release",
"el-icon-s-ticket",
"el-icon-s-management",
"el-icon-s-open",
"el-icon-s-shop",
"el-icon-s-marketing",
"el-icon-s-flag",
"el-icon-s-comment",
"el-icon-s-finance",
"el-icon-s-claim",
"el-icon-s-custom",
"el-icon-s-opportunity",
"el-icon-s-data",
"el-icon-s-check",
"el-icon-s-grid",
"el-icon-menu",
"el-icon-share",
"el-icon-d-caret",
"el-icon-caret-left",
"el-icon-caret-right",
"el-icon-caret-bottom",
"el-icon-caret-top",
"el-icon-bottom-left",
"el-icon-bottom-right",
"el-icon-back",
"el-icon-right",
"el-icon-bottom",
"el-icon-top",
"el-icon-top-left",
"el-icon-top-right",
"el-icon-arrow-left",
"el-icon-arrow-right",
"el-icon-arrow-down",
"el-icon-arrow-up",
"el-icon-d-arrow-left",
"el-icon-d-arrow-right",
"el-icon-video-pause",
"el-icon-video-play",
"el-icon-refresh",
"el-icon-refresh-right",
"el-icon-refresh-left",
"el-icon-finished",
"el-icon-sort",
"el-icon-sort-up",
"el-icon-sort-down",
"el-icon-rank",
"el-icon-loading",
"el-icon-view",
"el-icon-c-scale-to-original",
"el-icon-date",
"el-icon-edit",
"el-icon-edit-outline",
"el-icon-folder",
"el-icon-folder-opened",
"el-icon-folder-add",
"el-icon-folder-remove",
"el-icon-folder-delete",
"el-icon-folder-checked",
"el-icon-tickets",
"el-icon-document-remove",
"el-icon-document-delete",
"el-icon-document-copy",
"el-icon-document-checked",
"el-icon-document",
"el-icon-document-add",
"el-icon-printer",
"el-icon-paperclip",
"el-icon-takeaway-box",
"el-icon-search",
"el-icon-monitor",
"el-icon-attract",
"el-icon-mobile",
"el-icon-scissors",
"el-icon-umbrella",
"el-icon-headset",
"el-icon-brush",
"el-icon-mouse",
"el-icon-coordinate",
"el-icon-magic-stick",
"el-icon-reading",
"el-icon-data-line",
"el-icon-data-board",
"el-icon-pie-chart",
"el-icon-data-analysis",
"el-icon-collection-tag",
"el-icon-film",
"el-icon-suitcase",
"el-icon-suitcase-1",
"el-icon-receiving",
"el-icon-collection",
"el-icon-files",
"el-icon-notebook-1",
"el-icon-notebook-2",
"el-icon-toilet-paper",
"el-icon-office-building",
"el-icon-school",
"el-icon-table-lamp",
"el-icon-house",
"el-icon-no-smoking",
"el-icon-smoking",
"el-icon-shopping-cart-full",
"el-icon-shopping-cart-1",
"el-icon-shopping-cart-2",
"el-icon-shopping-bag-1",
"el-icon-shopping-bag-2",
"el-icon-sold-out",
"el-icon-sell",
"el-icon-present",
"el-icon-box",
"el-icon-bank-card",
"el-icon-money",
"el-icon-coin",
"el-icon-wallet",
"el-icon-discount",
"el-icon-price-tag",
"el-icon-news",
"el-icon-guide",
"el-icon-male",
"el-icon-female",
"el-icon-thumb",
"el-icon-cpu",
"el-icon-link",
"el-icon-connection",
"el-icon-open",
"el-icon-turn-off",
"el-icon-set-up",
"el-icon-chat-round",
"el-icon-chat-line-round",
"el-icon-chat-square",
"el-icon-chat-dot-round",
"el-icon-chat-dot-square",
"el-icon-chat-line-square",
"el-icon-message",
"el-icon-postcard",
"el-icon-position",
"el-icon-turn-off-microphone",
"el-icon-microphone",
"el-icon-close-notification",
"el-icon-bangzhu",
"el-icon-time",
"el-icon-odometer",
"el-icon-crop",
"el-icon-aim",
"el-icon-switch-button",
"el-icon-full-screen",
"el-icon-copy-document",
"el-icon-mic",
"el-icon-stopwatch",
"el-icon-medal-1",
"el-icon-medal",
"el-icon-trophy",
"el-icon-trophy-1",
"el-icon-first-aid-kit",
"el-icon-discover",
"el-icon-place",
"el-icon-location",
"el-icon-location-outline",
"el-icon-location-information",
"el-icon-add-location",
"el-icon-delete-location",
"el-icon-map-location",
"el-icon-alarm-clock",
"el-icon-timer",
"el-icon-watch-1",
"el-icon-watch",
"el-icon-lock",
"el-icon-unlock",
"el-icon-key",
"el-icon-service",
"el-icon-mobile-phone",
"el-icon-bicycle",
"el-icon-truck",
"el-icon-ship",
"el-icon-basketball",
"el-icon-football",
"el-icon-soccer",
"el-icon-baseball",
"el-icon-wind-power",
"el-icon-light-rain",
"el-icon-lightning",
"el-icon-heavy-rain",
"el-icon-sunrise",
"el-icon-sunrise-1",
"el-icon-sunset",
"el-icon-sunny",
"el-icon-cloudy",
"el-icon-partly-cloudy",
"el-icon-cloudy-and-sunny",
"el-icon-moon",
"el-icon-moon-night",
"el-icon-dish",
"el-icon-dish-1",
"el-icon-food",
"el-icon-chicken",
"el-icon-fork-spoon",
"el-icon-knife-fork",
"el-icon-burger",
"el-icon-tableware",
"el-icon-sugar",
"el-icon-dessert",
"el-icon-ice-cream",
"el-icon-hot-water",
"el-icon-water-cup",
"el-icon-coffee-cup",
"el-icon-cold-drink",
"el-icon-goblet",
"el-icon-goblet-full",
"el-icon-goblet-square",
"el-icon-goblet-square-full",
"el-icon-refrigerator",
"el-icon-grape",
"el-icon-watermelon",
"el-icon-cherry",
"el-icon-apple",
"el-icon-pear",
"el-icon-orange",
"el-icon-coffee",
"el-icon-ice-tea",
"el-icon-ice-drink",
"el-icon-milk-tea",
"el-icon-potato-strips",
"el-icon-lollipop",
"el-icon-ice-cream-square",
"el-icon-ice-cream-round",
],
};
},
};
</script>
<style lang="less" scoped>
.icons-item {
float: left;
margin: 6px 6px 6px 0;
width: 115px;
text-align: center;
list-style: none;
cursor: pointer;
height: 100px;
color: #5c6b77;
transition: all 0.2s ease;
position: relative;
padding-top: 10px;
}
.active {
border: 1px solid;
background: #f44336;
color: white;
font-size: 32px;
}
</style>

View File

@@ -0,0 +1,185 @@
<template>
<div class="vol-dialog">
<el-dialog v-model="vmodel" :close-on-click-modal="false" :close-on-press-escape="false"
:width="width" :draggable="draggable" :modal="modal" :before-close="handleClose">
<template #header> <i :class="icon"></i> {{ title }} </template>
<el-scrollbar :max-height="contentHeight">
<div v-if="inited" style="min-height: 50px;" class="srcoll-content" :style="{ padding: padding + 'px' }">
<slot name="content"></slot>
<slot></slot>
</div>
</el-scrollbar>
<template #footer>
<div class="dia-footer" v-if="footer">
<slot name="footer"></slot>
<el-button type="primary" v-if="!footer" size="mini" @click="handleClose()"><i
class="el-icon-close"></i>关闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import { defineComponent, ref, watch, watchEffect } from 'vue';
export default defineComponent({
props: {
modelValue: false,
lazy: {
//是否开启懒加载2020.12.06
type: Boolean,
default: false
},
icon: {
type: String,
default: 'el-icon-warning-outline'
},
title: {
type: String,
default: '基本信息'
},
height: {
type: Number,
default: 200
},
width: {
type: Number,
default: 650
},
padding: {
type: Number,
default: 16
},
modal: { //是否需要遮罩层
type: Boolean,
default: true
},
draggable: { //启用可拖拽功能
type: Boolean,
default: false
},
mask: {
type: Boolean,
default: true
},
onModelClose: {
//2021.07.11增加弹出框关闭事件
type: Function,
default: (iconClick) => {
return true;
}
},
footer:{ //是否显示底部按钮
type: Boolean,
default: true
}
},
setup(props, context) {
const clientHeight = document.body.clientHeight * 0.95 - 60;
const inited = ref(true);
const vmodel = ref(false);
const footer = ref(false);
const top = ref(100);
vmodel.value = props.modelValue;
footer.value = !!context.slots.footer;
const contentHeight = ref(200);
contentHeight.value = props.height;
const handleClose = (done, iconClose) => {
let result = props.onModelClose(!!iconClose);
if (result === false) return;
vmodel.value = false;
context.emit('update:modelValue', false);
done && done();
};
const calcHeight = (val) => {
// if (props.height > clientHeight) {
// if(true){
contentHeight.value = clientHeight - 30;
return clientHeight / -2 + 'px';
// }
// contentHeight.value = val || props.height;
// return (props.height + 56) / -2 + 'px';
};
top.value = calcHeight();
watch(
() => props.modelValue,
(newVal, oldVal) => {
vmodel.value = newVal;
}
);
watch(
() => props.height,
(newVal, oldVal) => {
top.value = calcHeight();
}
);
return {
handleClose,
inited,
vmodel,
footer,
top,
calcHeight,
contentHeight
};
}
});
</script>
<style lang="less" scoped>
.dia-footer {
text-align: right;
width: 100%;
border-top: 1px solid #e2e2e2;
text-align: right;
padding: 6px 8px;
}
</style>
<style scoped lang="less">
.vol-dialog ::v-deep(.el-overlay-dialog) {
display: flex !important;
}
.vol-dialog ::v-deep(.el-dialog) {
margin: auto;
}
.vol-dialog ::v-deep(.el-dialog) {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.vol-dialog ::v-deep(.el-dialog__header) {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 0px 13px;
line-height: 53px;
border-bottom: 1px solid #e6e6e6;
height: 50px;
color: rgb(79, 79, 79);
font-weight: bold;
font-size: 14px;
margin: 0;
// background-image: linear-gradient(135deg, #0cd7bd 10%, #50c3f7);
}
.vol-dialog ::v-deep(.el-dialog__footer),
.vol-dialog ::v-deep(.el-dialog__body) {
padding: 0;
}
.vol-dialog ::v-deep(.el-dialog__headerbtn) {
top: 0;
padding-top: 8px;
height: 50px;
}
// .vol-dialog ::v-deep(.el-dialog__headerbtn .el-dialog__close) {
// color: #fff;
// }
</style>

View File

@@ -0,0 +1,197 @@
<template>
<div class="mes-el-menu">
<el-menu
close="mes-el-menu--vertical"
:default-openeds="openedIds"
:default-active="defaultActive"
:unique-opened="true"
@select="select"
:collapse="isCollapse"
@open="handleOpen"
@close="handleClose"
@contextmenu.prevent="bindRightClickMenu"
>
<template v-for="item in convertTree(list)">
<el-sub-menu
:key="item.id"
:index="'' + item.id"
v-if="item.children.length && (!enable || item.enable == 1)"
>
<template #title>
<i class="menu-icon" :class="item.icon"></i>
<span>{{$t(item.name)}}</span>
</template>
<mes-element-menu-child
:enable="enable"
:list="item.children"
></mes-element-menu-child>
</el-sub-menu>
<template v-else>
<el-menu-item
class="menu-item-lv1"
v-if="!enable || item.enable == 1"
:key="item.id"
:index="'' + item.id"
>
<i :class="item.icon"></i>
<span>{{$t(item.name)}}</span>
</el-menu-item>
</template>
</template>
</el-menu>
</div>
</template>
<script>
import MesElementMenuChild from './MesElementMenuChild';
import { useRouter } from 'vue-router';
import {
defineComponent,
reactive,
watch,
ref,
toRef,
toRefs,
getCurrentInstance
// onMounted,
} from 'vue';
export default defineComponent({
components: {
'mes-element-menu-child': MesElementMenuChild
},
props: {
enable: {
type: Boolean,
default: false //是否判断enable=1
},
isCollapse: {
type: Boolean,
default: false
},
onSelect: {
type: Function,
default: (x) => {}
},
openSelect: {
//打开的时候是否触发选中事件
type: Boolean,
default: true
},
list: {
type: Array,
default: []
},
rootId: {
type: String,
default: '0'
},
currentMenuId: {
type: Number,
default: 0
}
},
setup(props) {
// const { list } = toRefs(props);
// const treeList = ref([]);
const getTree = (id, node, data) => {
if (!node.children) {
node.children = [];
}
data.forEach((x) => {
if (x.parentId == id && !node.children.some((c) => c.id === x.id)) {
node.children.push(x);
getTree(x.id, x, data);
}
});
};
let rootTreeId = !isNaN(props.rootId) ? ~~props.rootId : props.rootId;
props.list.forEach((x) => {
if (!x.icon || x.icon.substring(0, 3) != 'el-') {
x.icon = 'el-icon-menu';
}
x.children = [];
x.isRoot = x.parentId === rootTreeId;
});
const convertTree = (data) => {
var root_data = [];
data.forEach((x) => {
if (x.parentId === rootTreeId) {
if (!x.hasOwnProperty('enable')) x.enable = 1;
root_data.push(x);
getTree(x.id, x, data);
}
});
return root_data;
};
const openedIds = reactive([props.currentMenuId]);
const defaultActive = ref(props.currentMenuId + '');
let _base = getCurrentInstance().appContext.config.globalProperties.base;
watch(
() => props.currentMenuId,
(newVal, oldVal) => {
defaultActive.value = newVal + '';
openedIds.splice(0);
openedIds.push(
..._base.getTreeAllParent(newVal, props.list).map((c) => {
return c.id;
})
);
}
);
const router = useRouter();
let eventSelect = false;
const select = (index, path) => {
if (eventSelect) {
return;
}
eventSelect = true;
setTimeout(() => {
eventSelect = false;
}, 20);
let _item = props.list.find((x) => {
return x.id == index;
});
props.onSelect(index, _item);
router.push({ path: _item.path || '' });
};
const handleOpen = (index, path) => {
if (props.openSelect) {
select(index, path);
}
};
const handleClose = () => {};
/**
* 菜单导航右键事件
* @param {*} enable 是否启用右键事件[true:启用;false:禁用;]
*/
const bindRightClickMenu = (enable) => {
if (!enable) return;
};
return {
// treeList,
// list,
select,
convertTree,
handleOpen,
handleClose,
bindRightClickMenu,
openedIds,
defaultActive
};
}
});
</script>
<style lang="less" scoped>
.mes-el-menu {
box-sizing: content-box;
width: 100%;
.menu-icon {
font-size: 18px;
margin-right: 6px;
}
}
</style>

View File

@@ -0,0 +1,56 @@
<template>
<div class="mes-el-menu-item">
<template v-for="(item) in list">
<template v-if="item.children&&item.children.length">
<el-menu-item :key="item.id"
:index="'' + item.id"
v-if="!item.children.length && (!enable || item.enable == 1)">
<template #title></template>
<span>{{$t(item.name)}}</span>
</el-menu-item>
<el-sub-menu :key="item.id"
:index="'' + item.id"
v-if="item.children.length && (!enable || item.enable == 1)">
<template #title>
<span>{{$t(item.name)}}</span>
</template>
<mes-element-menu-child :list="item.children" />
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :key="item.id"
:index="'' + item.id"
v-if="(!enable || item.enable == 1)">
<template #title></template>
<span> {{$t(item.name)}}</span>
</el-menu-item>
</template>
</template>
</div>
</template>
<script>
export default {
name: "mes-element-menu-child",
props: {
list: {
type: Array,
default: [],
},
enable: {
type: Boolean,
default: false, //是否判断enable=1
},
},
};
</script>
<style scoped lang="less">
.mes-el-menu-item ::v-deep(.el-menu-item) {
height: 42px !important;
line-height: 42px !important;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import { h } from 'vue';
export default {
name: "FormExpand",
functional: true,
props: {
render: Function,
par: {}//测试参数
},
render: ({ render, par }) => {
return render(h, { par }); //h();
}
};

View File

@@ -0,0 +1,156 @@
import templateCode from './templateCode'
export default function () {
let code = templateCode;
let _formOptions = this.options.formOptions.map(m => {
let _op = m.map((m1, i) => {
let _obj;
//.data[0].hasOwnProperty("key")
if (m1.data && m1.data.length && !m1.dataKey) {
let m2 = JSON.parse(JSON.stringify(m1));
m2.data = m2.data.map(c => {
return { key: c.label || c.key, value: c.label }
})
_obj = JSON.stringify(m2)
} else {
_obj = JSON.stringify(m1)
}
return (i === 0 ? '' : '\n\t\t\t\t') + _obj
}).join(',');
//return JSON.stringify(m, null, '')
return _op;
})
code = code.replace('{#fields}', JSON.stringify(this.options.fields))
.replace('{#formOptions}', '[' + _formOptions.join('],\n\t\t\t\t[') + ']');
code = code.replace('}],', '\t\t\t\t\t}],')
.replace("[{#tableOptions}]", JSON.stringify(this.options.tables, null, '\t'))
.replace("[{#tabsOptions}]", JSON.stringify(this.options.tabs, null, '\t'))
// fields: {#fields},
// formOptions: [{#formOptions}],
// tables: [{#tables}],
// tabs: [{#tabs}]
var tabsText = this.options.tabs.length ? ` <div class="tables"
style="padding-bottom: 10px">
<el-tabs v-model="tabsModel"
@tab-click="() => {}">
<el-tab-pane style="padding: 0"
class="table-item"
v-for="(item, index) in tabs"
:label="item.name"
:name="index+''"
:key="index">
<div class="table-header">
<div class="header-text">
{{ item.name }}
</div>
<div class="header-btns">
<el-button type="primary"
size="mini"
:key="bindex"
:icon="btnItem.icon"
plain
@click="tabsTableBtnClick(item, bindex, index)"
v-for="(btnItem, bindex) in item.buttons">
{{ btnItem.name }}
</el-button>
</div>
</div>
<mes-table :url="item.url"
:load-key="false"
:index="true"
:ref="'tabsTable' + index"
:tableData="item.tableData"
:columns="item.columns"
:max-height="250"
:pagination-hide="item.pagination"
:column-index="true"
:ck="true"></mes-table>
</el-tab-pane>
</el-tabs>
</div>`: ''
code = code.replace('{#tabs}', tabsText);
if (this.options.tables.length || this.options.tabs.length) {
code = code.replace("{import_MesTable}", "import MesTable from '@/components/basic/MesTable'")
code = code.replace(",{component_table}", ",'mes-table': MesTable")
} else {
code = code.replace("{import_MesTable}", '')
code = code.replace("{component_table}", '')
}
if (this.options.tables.length) {
code = code.replace('{table_ms}', `
tableBtnClick (item, btnIndex, index) {
if (item.buttons[btnIndex].value == "add") {
this.$refs["table" + index][0].addRow({});
return;
}
if (item.buttons[btnIndex].value == "del") {
this.$refs["table" + index][0].delRow();
return;
}
},
tabsTableBtnClick (item, btnIndex, index) {
if (item.buttons[btnIndex].value == "add") {
this.$refs["tabsTable" + index][0].addRow({});
return;
}
if (item.buttons[btnIndex].value == "del") {
this.$refs["tabsTable" + index][0].delRow();
return;
}
},`)
code = code.replace('{#tables}',
`
<!--table配置 -->
<div class="tables">
<div class="table-item"
v-for="(item, index) in tables"
:key="index">
<div class="table-header">
<div class="header-text">
{{ item.name }}
</div>
<div class="header-btns">
<el-button type="primary"
size="mini"
:key="bindex"
plain
@click="tableBtnClick(item, bindex, index)"
:icon="btnItem.icon"
v-for="(btnItem, bindex) in item.buttons">
{{ btnItem.name }}
</el-button>
</div>
</div>
<mes-table :url="item.url"
:load-key="false"
:index="true"
:ref="'table' + index"
:tableData="item.tableData"
:columns="item.columns"
:max-height="250"
:pagination-hide="item.pagination"
:column-index="true"
:ck="true"></mes-table>
</div>
</div>`);
} else {
code = code.replace('{table_ms}', '')
code = code.replace('{#tables}', '');
}
const blob = new Blob([code], { type: "text/plain;charset=utf-8" })
if ('download' in document.createElement('a')) { // 非IE下载
const elink = document.createElement('a')
elink.download = `code${new Date().valueOf()}.vue`;
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
} else {
navigator.msSaveBlob(blob, fileName)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
<template>
<div style="padding: 15px 20px 15px 5px">
<div class="pre-text">{{ text }}</div>
<mes-form
ref="form"
:labelWidth="80"
:load-key="false"
:formFields="options.fields"
:formRules="options.formOptions"
>
</mes-form>
<div class="tables">
<div
class="table-item"
v-for="(item, index) in options.tables"
:key="index"
>
<div class="table-header">
<div class="header-text">
{{ item.name }}
</div>
<div class="header-btns">
<el-button
type="primary"
size="small"
:key="bindex"
plain
@click="tableBtnClick(item, bindex, index)"
:icon="btnItem.icon"
v-for="(btnItem, bindex) in item.buttons"
>
{{ btnItem.name }}
</el-button>
</div>
</div>
<mes-table
:url="item.url"
:load-key="false"
:index="true"
:ref="'table' + index"
:tableData="item.tableData"
:columns="item.columns"
:max-height="250"
:pagination-hide="item.pagination"
:column-index="true"
:ck="true"
></mes-table>
</div>
</div>
<div class="tables" style="padding-bottom: 10px">
<el-tabs
v-model="tabsModel"
v-show="options.tabs.length"
@tab-click="() => {}"
>
<el-tab-pane
style="padding: 0"
class="table-item"
v-for="(item, index) in options.tabs"
:label="item.name"
:name="index"
:key="index"
>
<div class="table-header">
<div class="header-text">
{{ item.name }}
</div>
<div class="header-btns">
<el-button
type="primary"
size="small"
:key="bindex"
:icon="btnItem.icon"
plain
@click="tabsTableBtnClick(item, bindex, index)"
v-for="(btnItem, bindex) in item.buttons"
>
{{ btnItem.name }}
</el-button>
</div>
</div>
<mes-table
:url="item.url"
:load-key="false"
:index="true"
:ref="'tabsTable' + index"
:tableData="item.tableData"
:columns="item.columns"
:max-height="250"
:pagination-hide="item.pagination"
:column-index="true"
:ck="true"
></mes-table>
</el-tab-pane>
</el-tabs>
</div>
<div class="form-btns">
<el-button type="primary" @click="submit" icon="el-icon-check" size="small"
>提交</el-button
>
<el-button
type="primary"
@click="reset"
plain
icon="el-icon-refresh-right"
size="small"
>重置</el-button
>
<el-button
type="primary"
@click="download"
plain
icon="el-icon-refresh-right"
size="small"
>下载代码</el-button
>
</div>
</div>
</template>
<script>
import MesTable from "./../MesTable";
import MesBox from "./../MesBox";
import MesForm from "./../MesForm";
import downloadForm from "./DownloadForm";
export default {
props: {
options: {
type: Object,
default: () => {
return { fields: {}, formOptions: [], tables: [], tabs: [] };
},
},
},
data() {
return {
text: "",
tabsModel: 0,
};
},
created() {},
methods: {
tableBtnClick(item, btnIndex, index) {
if (item.buttons[btnIndex].value == "add") {
this.$refs["table" + index].addRow({});
return;
}
if (item.buttons[btnIndex].value == "del") {
this.$refs["table" + index].delRow();
return;
}
},
tabsTableBtnClick(item, btnIndex, index) {
if (item.buttons[btnIndex].value == "add") {
this.$refs["tabsTable" + index].addRow({});
return;
}
if (item.buttons[btnIndex].value == "del") {
this.$refs["tabsTable" + index].delRow();
return;
}
},
submit() {},
reset() {
this.$refs.form.reset();
this.$Message.success("表单已重置");
},
download() {
downloadForm.call(this);
},
},
components: {
"mes-table": MesTable,
"mes-box": MesBox,
"mes-form": MesForm,
},
};
MesForm;
</script>
<style lang="less" scoped>
.form-btns {
text-align: center;
}
.tables {
padding-left: 15px;
.table-item {
padding: 10px;
}
.table-header {
display: flex;
margin-bottom: 8px;
}
.header-text {
position: relative;
bottom: -9px;
flex: 1;
font-weight: bold;
}
.header-btns {
text-align: right;
}
}
</style>

View File

@@ -0,0 +1,664 @@
let options1 = [
{
"id": 1,
"name": "输入框",
"type": "text",
"value": "",
"icon": "el-icon-document",
"field": "field1630258884671",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 3,
"name": "日期",
"type": "date",
"icon": "el-icon-date",
"value": null,
"field": "field1630258891760",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 7,
"name": "下拉框",
"value": null,
"key": "",
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"type": "select",
"icon": "el-icon-arrow-down",
"field": "field1630258904862",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 8,
"name": "下拉多选",
"type": "selectList",
"key": "",
"values": [],
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"icon": "el-icon-arrow-down",
"field": "field1630258924442",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 81,
"name": "级联",
"type": "cascader",
"icon": "el-icon-share",
"values": [],
"key": "",
"data": [
{
"value": "请配置数据源",
"label": "请配置数据源",
"children": [
{
"value": "具体",
"label": "菜单:下拉框绑定设置"
},
{
"value": "color",
"label": "可参照字典编号[tree_roles]"
}
]
}
],
"field": "field1630259518082",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 6,
"name": "多选",
"values": [
"发货"
],
"type": "checkbox",
"key": "ordertype",
"data": [
{
"key": "1",
"value": "发货"
},
{
"key": "2",
"value": "退货"
},
{
"key": "3",
"value": "返单"
}
],
"icon": "el-icon-circle-check",
"field": "field1630259033241",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 5,
"name": "单选",
"type": "radio",
"icon": "el-icon-aim",
"value": 0,
"data": [
{
"key": "0",
"value": "否"
},
{
"key": "2",
"value": "xx11"
},
{
"key": "1",
"value": "是"
}
],
"key": "enable",
"field": "field1630259538490",
"width": 30,
"readonly": false,
"required": false,
"values": [
"否"
]
},
{
"id": 4,
"name": "switch",
"type": "switch",
"icon": "el-icon-turn-off",
"value": 0,
"field": "field1630259172794",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 12,
"name": "分段信息",
"type": "line",
"icon": "el-icon-guide",
"field": "field1630259600186",
"width": 100,
"readonly": false,
"required": false
},
{
"id": 9,
"name": "图片",
"type": "img",
"url": "api/SellOrder/upload",
"maxSize": 3,
"fileInfo": [],
"multiple": false,
"autoUpload": false,
"maxFile": 5,
"icon": "el-icon-picture-outline",
"field": "field1630259295154",
"width": 100,
"readonly": false,
"required": false
},
{
"id": 10,
"name": "excel",
"url": "api/SellOrder/upload",
"maxSize": 3,
"multiple": false,
"autoUpload": true,
"maxFile": 5,
"fileInfo": [],
"type": "excel",
"icon": "el-icon-upload",
"field": "field1630259610476",
"width": 100,
"readonly": false,
"required": false
},
{
"id": 5,
"name": "单选",
"type": "radio",
"icon": "el-icon-aim",
"value": 0,
"data": [
{
"key": "0",
"value": "审核中"
},
{
"key": "1",
"value": "审核通过"
},
{
"key": "2",
"value": "审核未通过"
}
],
"key": "audit",
"field": "field1630258969346",
"width": 40,
"readonly": false,
"required": false,
"values": [
"审核中"
]
}
];
let options2=[
{
"id": 1,
"name": "输入框",
"type": "text",
"value": "",
"icon": "el-icon-document",
"field": "field1630258884671",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 3,
"name": "日期",
"type": "date",
"icon": "el-icon-date",
"value": null,
"field": "field1630258891760",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 7,
"name": "下拉框",
"value": null,
"key": "",
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"type": "select",
"icon": "el-icon-arrow-down",
"field": "field1630258904862",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 8,
"name": "下拉多选",
"type": "selectList",
"key": "",
"values": [],
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"icon": "el-icon-arrow-down",
"field": "field1630258924442",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 2,
"name": "textarea",
"type": "textarea",
"value": "",
"icon": "el-icon-document-copy",
"field": "field1630260207393",
"width": 100,
"readonly": false,
"required": false
},
{
"id": 13,
"name": "表格",
"type": "table",
"tabs": true,
"columns": [
{
"title": "运单号",
"field": "TranNo",
"show": true,
"required": false,
"edit": true,
"dataType": null,
"dataSource": null,
"width": "140",
"orderNo": null,
"elementIndex": 0
},
{
"title": "销售订单号",
"field": "SellNo",
"show": true,
"required": false,
"edit": true,
"dataType": null,
"dataSource": null,
"width": "140",
"orderNo": null,
"elementIndex": 1
},
{
"title": "订单类型",
"field": "OrderType",
"show": true,
"required": false,
"edit": true,
"dataType": null,
"dataSource": "ordertype",
"width": 120,
"orderNo": null,
"elementIndex": 2,
"editType": "select"
},
{
"title": "销售数量",
"field": "Qty",
"show": true,
"required": false,
"edit": true,
"dataType": null,
"dataSource": null,
"width": "80",
"orderNo": null,
"elementIndex": 3
},
{
"field": "CreateDate",
"elementIndex": 4,
"show": 1,
"required": 0,
"edit": 0,
"title": "订单时间",
"dataType": "date",
"width": "100"
}
],
"tableData": [
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
},
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
},
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
}
],
"height": 200,
"icon": "el-icon-c-scale-to-original",
"url": "api/SellOrder/getPageData",
"index": false,
"columnIndex": false,
"ck": true,
"buttons": [
{
"name": "添加行",
"ck": false,
"icon": "el-icon-plus",
"value": "add"
},
{
"name": "删除行",
"ck": false,
"icon": "el-icon-delete",
"value": "del"
},
{
"name": "刷新",
"ck": false,
"icon": "el-icon-refresh-right",
"value": "ref"
}
],
"field": "field1630260242867",
"width": 100,
"readonly": false,
"required": false,
"pagination": false
},
{
"id": 13,
"name": "表格",
"type": "table",
"tabs": true,
"columns": [
{
"title": "字段1",
"field": "field1",
"show": true,
"required": false,
"edit": false,
"dataType": null,
"dataSource": null,
"width": 120,
"orderNo": null
},
{
"title": "字段2",
"field": "field2",
"show": true,
"required": false,
"edit": false,
"dataType": null,
"dataSource": null,
"width": 120,
"orderNo": null
},
{
"title": "字段3",
"field": "field3",
"show": true,
"required": false,
"edit": false,
"dataType": null,
"dataSource": null,
"width": 120,
"orderNo": null
},
{
"title": "字段4",
"field": "field4",
"show": true,
"required": false,
"edit": false,
"dataType": null,
"dataSource": null,
"width": 120,
"orderNo": null
}
],
"tableData": [
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
},
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
},
{
"field1": "field1",
"field2": "field2",
"field3": "field3",
"field4": "field4"
}
],
"height": 200,
"icon": "el-icon-c-scale-to-original",
"url": null,
"index": false,
"columnIndex": false,
"ck": true,
"buttons": [
{
"name": "添加行",
"ck": false,
"icon": "el-icon-plus",
"value": "add"
},
{
"name": "删除行",
"ck": false,
"icon": "el-icon-delete",
"value": "del"
},
{
"name": "刷新",
"ck": false,
"icon": "el-icon-refresh-right",
"value": "ref"
}
],
"field": "field1630260481283",
"width": 100,
"readonly": false,
"required": false,
"pagination": true
}
]
let options3=[
{
"id": 1,
"name": "输入框",
"type": "text",
"value": "",
"icon": "el-icon-document",
"field": "field1630258884671",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 3,
"name": "日期",
"type": "date",
"icon": "el-icon-date",
"value": null,
"field": "field1630258891760",
"width": 20,
"readonly": false,
"required": false
},
{
"id": 7,
"name": "下拉框",
"value": null,
"key": "",
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"type": "select",
"icon": "el-icon-arrow-down",
"field": "field1630258904862",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 8,
"name": "下拉多选",
"type": "selectList",
"key": "",
"values": [],
"data": [
{
"label": "请设置数据源",
"value": "请设置数据源"
}
],
"icon": "el-icon-arrow-down",
"field": "field1630258924442",
"width": 30,
"readonly": false,
"required": false
},
{
"id": 5,
"name": "单选",
"type": "radio",
"icon": "el-icon-aim",
"value": 0,
"data": [
{
"key": "0",
"value": "否"
},
{
"key": "2",
"value": "xx11"
},
{
"key": "1",
"value": "是"
}
],
"key": "enable",
"field": "field1630260669595",
"width": 50,
"readonly": false,
"required": false,
"values": [
"否"
]
},
{
"id": 6,
"name": "多选",
"values": [
"否"
],
"type": "checkbox",
"key": "enable",
"data": [
{
"key": "0",
"value": "否"
},
{
"key": "2",
"value": "xx11"
},
{
"key": "1",
"value": "是"
}
],
"icon": "el-icon-circle-check",
"field": "field1630260695322",
"width": 50,
"readonly": false,
"required": false
},
{
"id": 2,
"name": "textarea",
"type": "textarea",
"value": "",
"icon": "el-icon-document-copy",
"field": "field1630260207393",
"width": 100,
"readonly": false,
"required": false
},
{
"id": 13,
"name": "编辑器",
"type": "editor",
"value": "",
"url": "",
"height": 200,
"icon": "el-icon-notebook-2",
"field": "field1630260646842",
"width": 100,
"readonly": false,
"required": false
}
]
export { options1, options2,options3 }

View File

@@ -0,0 +1,3 @@
import MesFormDraggable from './MesFormDraggable'
export default MesFormDraggable;

View File

@@ -0,0 +1,226 @@
const components = [
{
id: 1,
name: "输入框",
type: "text",
value: "",
icon: "el-icon-document",
},
{
id: 2,
name: "textarea",
type: "textarea",
value: "",
icon: "el-icon-document-copy",
},
{
id: 3,
name: "日期",
type: "date",
icon: "el-icon-date",
value: null,
},
{
id: 4,
name: "switch",
type: "switch",
icon: "el-icon-turn-off",
value: 0,
},
{ id: 5, name: "单选", type: "radio", icon: "el-icon-aim", value: 0, data: [{ label: "0", value: "请设置数据源1" }, { label: "1", value: "请设置数据源2" }], key: "" },
{
id: 6,
name: "多选",
values: [],
type: "checkbox",
key: "",
data: [{ label: "请设置数据源", value: "请设置数据源" }],
icon: "el-icon-circle-check",
},
{
id: 7,
name: "下拉框",
value: null,
key: "",
data: [{ label: "请设置数据源", value: "请设置数据源" }],
type: "select",
icon: "el-icon-arrow-down",
},
{
id: 8,
name: "下拉框多选",
type: "selectList",
key: "",
values: [],
data: [{ label: "请设置数据源", value: "请设置数据源" }],
icon: "el-icon-arrow-down",
},
{
id: 81,
name: "级联",
type: "cascader",
icon: "el-icon-share",
values: [],
key: "",
data: [
{
value: "请配置数据源",
label: "请配置数据源",
children: [
{
value: "具体",
label: "菜单:下拉框绑定设置",
},
{
value: "color",
label: "可参照字典编号[tree_roles]",
}
],
},
],
},
{
id: 9,
name: "图片上传",
type: "img",
url: "",
maxSize: 3,
fileInfo: [],
multiple: false,
autoUpload: false,
maxFile: 5,
icon: "el-icon-picture-outline",
},
{
id: 10,
name: "excel上传",
url: "",
maxSize: 3,
multiple: false,
autoUpload: false,
maxFile: 5, //最多可上传5个文件
fileInfo: [],
type: "excel",
icon: "el-icon-upload",
},
{
id: 11,
name: "文件上传",
type: "file",
url: "",
maxSize: 3,
multiple: false,
autoUpload: false,
maxFile: 5,
fileInfo: [],
icon: "el-icon-folder-opened",
},
{
id: 12,
name: "分段信息",
type: "line",
icon: "el-icon-guide",
},
{
id: 13,
name: "编辑器",
type: "editor",
value: "",
url: "",
height: 200,
icon: "el-icon-notebook-2",
},
{
id: 13,
name: "弹出框",
type: "box",
value: "",
url: "",
height: 250,
icon: "el-icon-notebook-2",
},
{
id: 13,
name: "表格",
type: "table",
tabs: false,
columns: [
{ title: "字段1", field: "field1", show: true, required: false, edit: false, dataType: null, dataSource: null, width: 120, orderNo: null },
{ title: "字段2", field: "field2", show: true, required: false, edit: false, dataType: null, dataSource: null, width: 120, orderNo: null },
{ title: "字段3", field: "field3", show: true, required: false, edit: false, dataType: null, dataSource: null, width: 120, orderNo: null },
{ title: "字段4", field: "field4", show: true, required: false, edit: false, dataType: null, dataSource: null, width: 120, orderNo: null },
// { title: "字段5", field: "Field5", width: 120 },
],
tableData: [
{
field1: "field1",
field2: "field2",
field3: "field3",
field4: "field4",
},
{
field1: "field1",
field2: "field2",
field3: "field3",
field4: "field4",
},
{
field1: "field1",
field2: "field2",
field3: "field3",
field4: "field4",
},
],
height: 150,
icon: "el-icon-c-scale-to-original",
url: null,
index: false, //item.index,
height: 200,
index: false,
columnIndex: false,
ck: true,
buttons: [
{ name: '添加行', ck: false, icon: 'el-icon-plus',value:'add' },
{ name: '删除行', ck: false, icon: 'el-icon-delete',value:'del' },
{ name: '刷新', ck: false ,icon:'el-icon-refresh-right',value:'ref'}],
},
]
const tableOption = [
{ field: 'field', title: '字段', edit: { type: "text", keep: true }, width: 160 },
{ field: 'title', title: '字段中文名', edit: { type: "text", keep: true }, width: 120 },
{ field: 'show', title: '是否显示', edit: { type: "switch", keep: true }, width: 90 },
{
field: 'dataType', title: '显示类型', edit: { type: "select", keep: true }, width: 120, bind: {
key: '', data: [
{ "key": "switch", "value": "单选" },
{ "key": "date", "value": "年月日" },
{ "key": "img", "value": "图片" },
{ "key": "excel", "value": "excel" },
{ "key": "file", "value": "文件" }
]
}
},
{ field: 'required', title: '是否必填', edit: { type: "switch", keep: true }, width: 90 },
{ field: 'edit', title: '是否可编辑', edit: { type: "switch", keep: true }, width: 90 },
{
field: 'editType', title: '编辑类型', edit: { type: "select", keep: true, }, width: 120, bind: {
key: '', data: [{ "key": "text", "value": "输入框" },
{ "key": "switch", "value": "单选" },
{ "key": "select", "value": "下拉框" },
{ "key": "selectList", "value": "下拉框多选" },
{ "key": "date", "value": "日期" },
{ "key": "datetime", "value": "日期时分秒" },
{ "key": "checkbox", "value": "复选框多选" },
{ "key": "mail", "value": "邮箱地址" },
{ "key": "number", "value": "数字" },
{ "key": "decimal", "value": "小数" },
{ "key": "phone", "value": "手机号" },
]
}
},
{ field: 'dataSource', title: '数据源', edit: { type: "select", keep: true, data: [] }, bind: { key: '', data: [] }, width: 120 },
{ field: 'width', title: '列宽度', edit: { type: "text", keep: true }, width: 80 },
{ field: 'orderNo', title: '列显示顺序', edit: { type: "text", keep: true }, width: 100 }
];
export { components, tableOption }

View File

@@ -0,0 +1,95 @@
var code = `<template>
<div style="padding: 15px 20px 15px 5px">
<div class="pre-text">{{ text }}</div>
<mes-form ref="form"
:labelWidth="80"
:load-key="false"
:formFields="fields"
:formRules="formOptions">
</mes-form>
{#tables}
{#tabs}
<div class="form-btns">
<el-button type="primary"
@click="submit"
icon="el-icon-check"
size="mini">提交</el-button>
<el-button type="primary"
@click="reset"
plain
icon="el-icon-refresh-right"
size="mini">重置</el-button>
</div>
</div>
</template>
<script>
// 使用方式:
// 1、新建一个vue页面把此页面内容复制进去
// 2、router->index.js配置路由页面上输入地址即可看到数据(也可以把菜单配置上)
// 3、或者参照表单设计页面做动态页面
//**表单设计器的table下载还在开发中
{import_MesTable}
import MesForm from '@/components/basic/MesForm'
export default {
components: {"mes-form": MesForm,{component_table}},
data () {
return {
text: "",
tabsModel: "0",
fields: {#fields},
formOptions: [{#formOptions}],
tables: [{#tableOptions}],
tabs: [{#tabsOptions}]
};
},
created () {
},
methods: {{table_ms}
submit () {
this.$Message.success("submit")
return;
this.http.post("url",this.fields,true).then(result=>{
})
},
reset () {
this.$refs.form.reset();
this.$Message.success("表单已重置")
},
download () {
this.$Message.info("111")
}
}
};
MesForm;
</script>
<style lang="less" scoped>
.form-btns {
text-align: center;
}
.tables {
padding-left: 15px;
.table-item {
padding: 10px;
}
.table-header {
display: flex;
margin-bottom: 8px;
}
.header-text {
position: relative;
bottom: -9px;
flex: 1;
font-weight: bold;
}
.header-btns {
text-align: right;
}
}
</style>`
export default code

View File

@@ -0,0 +1,67 @@
<template>
<div class="v-header">
<div class="v-left-text">
<!-- <i size="20" :class="icon" class="h-icon"/> -->
<span>{{ title || text }}</span>
</div>
<div class="content">
<slot name="content"></slot>
</div>
<div class="v-right-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
text: {
type: String,
default: '未定义名称'
}
}
};
</script>
<style lang="less" scoped>
.v-header {
display: flex;
border-bottom: 1px solid #dcdee2;
.v-left-text {
margin-top: 3px;
padding-bottom: 6px;
font-weight: bold;
font-size: 15px;
color: #484848;
white-space: nowrap;
border-bottom: 2px solid #676767;
margin-bottom: -1px;
letter-spacing: 1px;
> span {
position: relative;
top: 2px;
}
}
.content {
line-height: 25px;
padding-left: 10px;
padding: 6px 0 0 10px;
}
.v-right-content {
flex: 1;
text-align: right;
}
.h-icon {
position: relative;
top: 2px;
margin-right: 3px;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import { h } from 'vue';
export default {
name: "TableExpand",
functional: true,
props: {
render: Function,
row: {},//当前行的数据
column: {},//当前行的配置信息
index: { type: Number, default: 0 }//当前所在行
},
render: ({ render,row ,column,index }) => {
return render(h, {row ,column,index}); //h();
}
};

View File

@@ -0,0 +1,738 @@
<template>
<div class="upload-container">
<div>
<div class="input-btns" style="margin-bottom: 10px">
<input
ref="input"
type="file"
style="display: none"
@change="handleChange"
:multiple="multiple"
/>
<div v-if="img" class="upload-img">
<!-- v-for="(file,index) in fileInfo.length>0?fileInfo: files" -->
<div v-for="(file, index) in files" :key="index" class="img-item">
<div class="operation">
<div class="action">
<i class="el-icon-view view" @click="previewImg(index)"></i>
<i class="el-icon-delete remove" @click="removeFile(index)"></i>
</div>
<div class="mask"></div>
</div>
<img :src="getImgSrc(file, index)" :onerror="errorImg" />
</div>
<div
v-show="!autoUpload || (autoUpload && files.length < maxFile)"
class="img-selector"
:class="getSelector()"
>
<div class="selector" @click="handleClick">
<i class="el-icon-camera-solid"></i>
</div>
<div
v-if="!autoUpload"
class="s-btn"
:class="{ readonly: changed }"
@click="upload"
>
<div>{{ loadText }}</div>
</div>
</div>
</div>
<el-button v-else @click="handleClick"
>选择{{ img ? "图片" : "文件" }}</el-button
>
<el-button
v-if="!autoUpload && !img"
type="info"
:disabled="changed"
@click="upload(true)"
:loading="loadingStatus"
>上传文件</el-button
>
</div>
<slot></slot>
<div v-if="desc">
<el-alert
:title="getText() + '文件大小不超过' + (maxSize || 50) + 'M'"
type="info"
show-icon
>
</el-alert>
</div>
<slot name="content"></slot>
<div v-if="!img">
<ul class="upload-list" v-show="fileList">
<li class="list-file" v-for="(file, index) in files" :key="index">
<a>
<span @click="fileOnClick(index, file)">
<i :class="format(file)"></i>
{{ file.name }}
</span>
</a>
<span @click="removeFile(index)" class="file-remove">
<i class="el-icon-close"></i>
</span>
</li>
</ul>
</div>
<slot name="tip"></slot>
</div>
</div>
</template>
<script>
export default {
components: {},
props: {
desc: {
//是否显示默认介绍
//是否多选
type: Boolean,
default: false,
},
fileInfo: {
//用于接收上传的文件,也可以加以默认值,显示已上传的文件,用户上传后会覆盖默认值
type: Array,
default: () => {
return [];
}, //格式[{name:'1.jpg',path:'127.0.01/1.jpg'}]
},
downLoad: {
//是否可以点击文件下载
type: Boolean,
default: true,
},
multiple: {
//是否多选
type: Boolean,
default: false,
},
maxFile: {
//最多可选文件数量必须multiple=true才会生效
type: Number,
default: 5,
},
maxSize: {
//文件限制大小3M
type: Number,
default: 50,
},
autoUpload: {
//选择文件后是否自动上传
type: Boolean,
default: true,
},
img: {
//图片类型 img>excel>fileTypes三种文件类型优先级
type: Boolean,
default: false,
},
excel: {
//excel文件
type: Boolean,
default: false,
},
fileTypes: {
//指定上传文件的类型
type: Array,
default: () => {
return [];
},
},
url: {
//上传的url
type: String,
default: "",
},
uploadBefore: {
//返回false会中止执行
//上传前
type: Function,
default: (files) => {
return true;
},
},
uploadAfter: {
//返回false会中止执行
//上传后
type: Function,
default: (result, files) => {
return true;
},
},
onChange: {
//选择文件时 //返回false会中止执行
type: Function,
default: (files) => {
return true;
},
},
// clear: {
// //上传完成后是否清空文件列表
// type: Boolean,
// default: true
// },
fileList: {
//是否显示选择的文件列表
type: Boolean,
default: true,
},
fileClick: {
//点击文件事件
type: Function,
default: (index, file, files) => {
return true;
},
},
removeBefore: {
//移除文件事件
type: Function,
default: (index, file, files) => {
return true;
},
},
append: {
//此属性已废弃,多文件上传,默认追加文件
type: Boolean,
default: false,
},
compress: {
//开启图片压缩,后面根据需要再完善
type: Boolean,
default: false,
},
},
data() {
return {
errorImg: 'this.src="' + require("@/assets/imgs/error-img.png") + '"',
changed: false, //手动上传成功后禁止重复上传,必须重新选择
model: true,
files: [],
bigImg: "",
loadingStatus: false,
loadText: "上传文件",
};
},
created() {
//默认有图片的禁止上传操作
if (this.fileInfo) {
this.changed = true;
}
this.cloneFile(this.fileInfo);
},
watch: {
fileInfo: {
handler(files) {
this.cloneFile(files);
},
deep: true,
},
},
methods: {
cloneFile(files) {
this.files = files.map((x) => {
return {
name: x.name || this.getFileName(x.path),
path: x.path,
};
});
},
getFileName(path) {
if (!path) {
return "未定义文件名";
}
let _index = path.lastIndexOf("/");
return path.substring(_index + 1);
},
previewImg(index) {
//查看大图预览模式待完
this.base.previewImg(this.getImgSrc(this.files[index]));
// window.open(this.getImgSrc((this.files.length>0?this.files:this.fileInfo)[index]));
},
getSelector() {
if (this.autoUpload) {
return "auto-selector";
}
return "submit-selector";
},
getImgSrc(file, index) {
if (file.hasOwnProperty("path")) {
if (this.base.isUrl(file.path)) {
return file.path;
}
//2020.12.27增加base64图片操作
if (file.path.indexOf("/9j/") != -1) {
return "data:image/jpeg;base64," + file.path;
}
if (file.path.substr(0, 1) == "/") {
file.path = file.path.substr(1);
}
return this.http.ipAddress + file.path;
}
return window.URL.createObjectURL(file);
},
fileOnClick(index, file) {
if (!this.fileClick(index, file, this.files)) {
return;
}
//点击不下载
if (!this.downLoad) {
return;
}
if (!file.path) {
this.$message.error("请先上传文件");
return;
}
this.base.dowloadFile(
file.path,
file.name,
{
Authorization: this.$store.getters.getToken(),
},
this.http.ipAddress
);
},
getText() {
if (this.img) {
return "只能上传图片,";
} else if (this.excel) {
return "只能上传excel文件,";
}
},
handleClick() {
this.$refs.input.click();
},
handleChange(e) {
//this.compress开启图片压缩,后面根据需要再完善
// this.clearFiles();
var result = this.checkFile(e.target.files);
if (!result) {
return;
}
this.changed = false;
//如果传入了FileInfo需要自行处理移除FileInfo
if (!this.onChange(e.target.files)) {
return;
}
for (let index = 0; index < e.target.files.length; index++) {
const element = e.target.files[index];
element.input = true;
}
if (!this.multiple) {
this.files.splice(0);
}
this.files.push(...e.target.files);
this.$refs.input.value = null;
if (this.autoUpload && result) {
this.upload(false);
}
},
removeFile(index) {
//如果传入了FileInfo需要自行处理移除FileInfo
//t移除文件
let removeFile = this.files[index];
//删除的还没上传的文件
if (removeFile.input) {
this.files.splice(index, 1);
} else {
this.fileInfo.splice(index, 1);
}
if (!this.removeBefore(index, removeFile, this.fileInfo)) {
return;
}
},
clearFiles() {
this.files.splice(0);
},
getFiles() {
return this.files;
},
upload(vail) {
if (vail && !this.checkFile()) return false;
if (!this.url) {
return this.$message.error("没有配置好Url");
}
if (!this.files || this.files.length == 0) {
return this.$message.error("请选择文件");
}
if (!this.uploadBefore(this.files)) {
return;
}
var forms = new FormData();
this.files.forEach(function (file) {
if (file.input) {
forms.append("fileInput", file, file.name);
}
});
// forms.append("fileInput", this.files);
this.loadingStatus = true;
this.loadText = "上传中..";
this.http
.post(this.url, forms, this.autoUpload ? "正在上传文件" : "")
.then(
(x) => {
// this.$refs.uploadFile.clearFiles();
this.loadingStatus = false;
this.loadText = "上传文件";
if (!this.uploadAfter(x, this.files)) {
this.changed = false;
return;
} else {
this.changed = true;
}
this.$message.success(x.message);
this.changed = x.status;
if (!x.status) {
// this.files = null;
return;
}
//单选清除以前的数据
// if (!this.multiple) {
this.fileInfo.splice(0);
// }
let _files = this.files.map((file) => {
return {
name: file.name,
path: file.path || x.data + file.name,
};
});
this.fileInfo.push(..._files);
//2021.09.25修复文件上传后不能同时下载的问题
this.files = _files;
},
(error) => {
this.loadText = "上传文件";
this.loadingStatus = false;
}
);
},
format(file, checkFileType) {
const format = file.name.split(".").pop().toLocaleLowerCase() || "";
let fileIcon = "el-icon-document";
if (this.fileTypes.length > 0 && checkFileType != undefined) {
if (this.fileTypes.indexOf(format) != -1) {
return true;
}
return false;
}
if (
checkFileType &&
!(checkFileType instanceof Array) &&
checkFileType != "img" &&
checkFileType != "excel"
) {
if (checkFileType.indexOf(format) > -1) {
return true;
} else {
return false;
}
}
if (
checkFileType == "img" ||
["gif", "jpg", "jpeg", "png", "bmp", "webp"].indexOf(format) > -1
) {
if (checkFileType == "img") {
if (
["gif", "jpg", "jpeg", "png", "bmp", "webp"].indexOf(format) > -1
) {
return true;
} else {
return false;
}
}
fileIcon = "el-icon-picture-outline";
}
if (
["mp4", "m3u8", "rmvb", "avi", "swf", "3gp", "mkv", "flv"].indexOf(
format
) > -1
) {
fileIcon = "el-icon-document";
}
if (["mp3", "wav", "wma", "ogg", "aac", "flac"].indexOf(format) > -1) {
fileIcon = "el-icon-document";
}
if (["doc", "txt", "docx", "pages", "epub", "pdf"].indexOf(format) > -1) {
fileIcon = "el-icon-document";
}
if (
checkFileType == "excel" ||
["numbers", "csv", "xls", "xlsx"].indexOf(format) > -1
) {
if (checkFileType == "excel") {
if (["numbers", "csv", "xls", "xlsx"].indexOf(format) > -1) {
return true;
} else {
return false;
}
}
fileIcon = "el-icon-document";
}
return fileIcon;
},
beforeUpload() {},
checkFile(inputFiles) {
const files = this.files;
if (
this.multiple &&
files.length + (inputFiles || []).length > (this.maxFile || 5)
) {
this.$message.error(
"最多只能选【" +
(this.maxFile || 5) +
"】" +
(this.img ? "张图片" : "个文件") +
""
);
return false;
}
if (!inputFiles) {
inputFiles = this.files.filter((x) => {
return x.input;
});
}
let names = [];
for (let index = 0; index < inputFiles.length; index++) {
const file = inputFiles[index];
if (names.indexOf(file.name) != -1) {
file.name = "(" + index + ")" + file.name;
}
names.push(file.name);
if (this.img && !this.format(file, "img")) {
this.$message.error("选择的文件【" + file.name + "】只能是图片格式");
return false;
}
if (this.excel && !this.format(file, "excel")) {
this.$message.error("选择的文件【" + file.name + "】只能是excel文件");
return false;
}
if (
this.fileTypes &&
this.fileTypes.length > 0 &&
!this.format(file, this.fileTypes)
) {
this.$message.error(
"选择的文件【" +
file.name +
"】只能是【" +
this.fileTypes.join(",") +
"】格式"
);
return false;
}
if (file.size > (this.maxSize || 50) * 1024 * 1024) {
this.$message.error(
"选择的文件【" +
file.name +
"】不能超过:" +
(this.maxSize || 50) +
"M"
);
return false;
}
}
return true;
},
},
};
</script>
<style lang="less" scoped>
.upload-list {
padding-left: 0;
list-style: none;
.list-file {
line-height: 20px;
padding: 4px;
color: #515a6e;
border-radius: 4px;
transition: background-color 0.2s ease-in-out;
overflow: hidden;
position: relative;
font-size: 13px;
.file-remove {
display: none;
right: 0;
// margin-left: 50px;
color: #0e9286;
}
}
.list-file:hover {
cursor: pointer;
.file-remove {
display: initial;
}
color: #2d8cf0;
}
}
.upload-container {
display: inline-block;
width: 100%;
// padding: 10px;
// min-height: 250px;
border-radius: 5px;
.alert {
margin-top: 43px;
}
.button-group > * {
float: left;
margin-right: 10px;
}
.file-info > span {
margin-right: 20px;
}
}
.upload-img {
display: inline-block;
.img-item:hover .operation {
display: block;
}
.img-item,
.img-selector {
position: relative;
cursor: pointer;
margin: 0 10px 10px 0;
float: left;
width: 65px;
height: 65px;
border: 1px solid #c7c7c7;
overflow: hidden;
border-radius: 5px;
box-sizing: content-box;
img {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.operation {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
.action {
opacity: 0.6;
text-align: center;
background: #151515de;
font-size: 14px;
position: absolute;
z-index: 90;
width: 100%;
bottom: 3px;
bottom: 0;
color: #ded5d5;
padding-right: 7px;
padding-bottom: 3px;
line-height: 20px;
.el-icon-view {
margin: 0 10px;
}
}
.mask {
opacity: 0.6;
background: #9e9e9e;
top: 0;
width: 100%;
height: 100%;
position: absolute;
}
}
}
.img-selector {
font-size: 50px;
text-align: center;
i {
position: relative;
font-size: 40px;
color: #6f6f6f;
}
}
.auto-selector {
.selector {
line-height: 64px;
}
}
.selector {
color: #a0a0a0;
}
.submit-selector {
.s-btn {
line-height: 22px;
font-size: 12px;
top: -6px;
// padding: 2px;
position: relative;
background: #2db7f5;
color: white;
}
.selector {
line-height: 50px;
}
.readonly {
background: #8c8c8c;
}
}
}
.big-model {
width: 100%;
height: 100%;
position: relative;
.m-img {
}
.mask {
position: absolute;
opacity: 0.6;
background: #eee;
top: 0;
width: 100%;
height: 100%;
position: absolute;
}
}
.auto-upload {
z-index: 9999999;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
.j-content {
text-align: center;
font-size: 17px;
top: 40%;
position: absolute;
z-index: 999;
left: 0;
right: 0;
width: 240px;
/* height: 100%; */
margin: auto;
background: white;
/* bottom: 30px; */
line-height: 50px;
border-radius: 6px;
border: 1px solid #d2d2d2;
}
.mask {
cursor: pointer;
opacity: 0.6;
width: 100%;
height: 100%;
background: #101010;
}
}
</style>

View File

@@ -0,0 +1,152 @@
<template>
<div>
<el-select
style="width: 150px"
v-if="['select', 'selectList'].indexOf(singleSearch.type) != -1"
v-model="searchFormFields[singleSearch.field]"
:filterable="
singleSearch.filter || singleSearch.data.length > 10 ? true : false
"
:placeholder="'请选择' + singleSearch.title"
clearable
>
<el-option
v-for="item in singleSearch.data"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
<div
class="date-range"
v-else-if="['date', 'datetime'].indexOf(singleSearch.type) != -1"
>
<el-date-picker
style="width: 210px"
:clearable="false"
unlink-panels
v-model="searchFormFields[singleSearch.field]"
type="daterange"
:value-format="getDateFormat(singleSearch)"
:placeholder="singleSearch.title"
>
</el-date-picker>
<i
class="el-icon-circle-close"
@click="dateRangeClear(singleSearch.field)"
></i>
</div>
<el-cascader
style="width: 210px"
clearable
v-model="searchFormFields[singleSearch.field]"
v-else-if="singleSearch.type == 'cascader'"
:options="singleSearch.data"
:props="{ checkStrictly: true }"
>
</el-cascader>
<el-input
clearable
v-else
style="width: 150px"
size="default"
v-model="searchFormFields[singleSearch.field]"
:placeholder="singleSearch.title"
@keypress="tiggerPress"
/>
</div>
</template>
<script>
export default {
props: {
singleSearch: {
type: Object,
default: {},
},
searchFormFields: {
type: Object,
default: () => {
return {};
},
},
tiggerPress: {
type: Function,
default: () => {},
},
},
methods: {
compareDate(date1, date2) {
if (!date2) {
return true;
}
return (
date1.valueOf() <
(typeof date2 == "number" ? date2 : new Date(date2).valueOf())
);
},
getDateFormat(item) {
//见https://day.js.org/docs/zh-CN/display/format
return item.type == "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss";
},
getDateOptions(date, item) {
if ((!item.min && !item.max) || !date) {
return false;
}
if (item.min && item.min.indexOf(" ") == -1) {
//不设置时分秒,后面会自动加上 08:00
item.min = item.min + " 00:00:000";
}
return (
this.compareDate(date, item.min) || !this.compareDate(date, item.max)
);
},
dateRangeClear(field) {
this.searchFormFields[field]=[undefined,undefined];
},
},
created() {
this.singleSearch.dateType = this.singleSearch.type + "range";
if (
this.singleSearch.type == "date" ||
this.singleSearch.type == "datetime"
) {
var _dateVal = this.searchFormFields[this.singleSearch.field];
if (
typeof this.singleSearch.range == "boolean" &&
!this.singleSearch.range
) {
this.searchFormFields[this.singleSearch.field] = "";
this.singleSearch.dateType = this.singleSearch.type;
return this.singleSearch.dateType;
} else if (!(_dateVal instanceof Array)) {
this.searchFormFields[this.singleSearch.field] = ["", ""];
} else if (_dateVal.length != 2) {
_dateVal.splice(0);
_dateVal.push(...["", ""]);
}
}
},
};
</script>
<style lang="less" scoped>
.date-range{
position: relative;
> i{
display: none;
height: 27px;
line-height: 27px;
right: 1px;
top: 3px;
font-size: 13px;
color: #b4adad;
position: absolute;
padding: 0 6px 0 3px;
background: #ffff;
cursor: pointer;
}
}
.date-range:hover > i{
display: inline-block;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<div class="router-loading" style="background: #eeeeee5c;">
<div class="spanner">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</template>
<script>
export default {
data() {
return {};
}
};
</script>
<style scoped>
.router-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 100px;
text-align: center;
padding-top: 200px;
color: #808080;
z-index: 9999;
}
.spanner {
width: 100px;
height: 100px;
position: relative;
margin: 0 auto;
}
.router-loading span {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: #66b1ff;
position: absolute;
animation: r_load 1.04s ease infinite;
}
@keyframes r_load {
0% {
transform: scale(1.2);
opacity: 1;
}
100% {
transform: scale(0.3);
opacity: 0.5;
}
}
.router-loading span:nth-child(1) {
left: 0;
top: 50%;
margin-top: -10px;
animation-delay: 0.13s;
}
.router-loading span:nth-child(2) {
left: 14px;
top: 14px;
animation-delay: 0.26s;
}
.router-loading span:nth-child(3) {
left: 50%;
top: 0;
margin-left: -10px;
animation-delay: 0.39s;
}
.router-loading span:nth-child(4) {
top: 14px;
right: 14px;
animation-delay: 0.52s;
}
.router-loading span:nth-child(5) {
right: 0;
top: 50%;
margin-top: -10px;
animation-delay: 0.65s;
}
.router-loading span:nth-child(6) {
right: 14px;
bottom: 14px;
animation-delay: 0.78s;
}
.router-loading span:nth-child(7) {
bottom: 0;
left: 50%;
margin-left: -10px;
animation-delay: 0.91s;
}
.router-loading span:nth-child(8) {
bottom: 14px;
left: 14px;
animation-delay: 1.04s;
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div class="upload-container">
<a :href="template.url" ref="template"></a>
<div class="button-group">
<el-upload
style="float: left"
ref="uploadFile"
:max-size="maxSize"
:on-change="clearMsg"
:before-upload="beforeUpload"
:action="url"
>
<el-button size="small"
><i class="el-icon-folder-opened"></i>选择文件</el-button
>
</el-upload>
<el-button
v-if="template.url"
style="margin-left: 10px"
type="primary"
size="small"
@click="dowloadTemplate"
:loading="loadingStatus"
>
<i class="el-icon-bottom"></i>
下载模板</el-button
>
<el-button
type="success"
size="small"
@click="upload"
:loading="loadingStatus"
>
<i class="el-icon-top"></i>
上传文件</el-button
>
</div>
<div class="alert">
<el-alert title="上传说明" type="warning" :closable="false" show-icon
>只能上传excel文件,文件大小不超过{{ maxSize }}M</el-alert
>
</div>
<div v-if="file">
<h3>文件列表</h3>
<div class="file-info">
<span>文件名{{ file.name }}</span>
<span>大小{{ (file.size / 1024).toFixed(2) }}KB</span>
</div>
</div>
<div v-show="message" class="v-r-message">
<h3 class="title">上传结果</h3>
<div class="text" :class="resultClass" v-html="message"></div>
</div>
<slot></slot>
</div>
</template>
<script>
//目前只支持单个Excel上传其他功能开发中...
export default {
components: {},
props: {
url: {
type: String,
default: ''
},
template: {
//下载模板配置
type: Object,
default: () => {
return {
url: '', //模板下载路径,如果没有模板路径,则不显示下载模板功能
fileName: '未定义文件名' //下载模板的文件名
};
}
},
importExcelBefore: {
type: Function,
default: (file) => {
return true;
}
}
},
data() {
return {
maxSize: 100,
model: true,
file: null,
loadingStatus: false,
message: '',
resultClass: ''
};
},
methods: {
clearMsg() {
this.message = '';
},
reset() {
this.file = null;
this.message = '';
this.resultClass = '';
},
getFileType() {
let fileName =
this.file.name
.split('.')
.pop()
.toLocaleLowerCase() || '';
if (['numbers', 'csv', 'xls', 'xlsx'].indexOf(fileName) == -1) {
this.$Message.error('只能选择excel文件');
return false;
}
return true;
},
beforeUpload(file) {
this.file = file;
if (!this.getFileType()) {
return false;
}
return false;
},
upload() {
let _url = this.url;
if (!_url) {
return this.$Message.error('没有配置好Url');
}
if (!this.file) {
return this.$Message.error('请选择文件');
}
var formData = new FormData();
formData.append('fileInput', this.file);
if (!this.importExcelBefore(formData)) {
return;
}
this.loadingStatus = true;
this.http.post(_url, formData).then(
(x) => {
// this.$refs.uploadFile.clearFiles();
this.loadingStatus = false;
this.file = null;
if (x.status) {
this.$emit('importExcelAfter', x);
}
this.message = x.message;
this.resultClass = x.status ? 'v-r-success' : 'v-r-error';
},
(error) => {
this.loadingStatus = false;
}
);
},
dowloadTemplate() {
let url = this.template.url;
let xmlResquest = new XMLHttpRequest();
xmlResquest.open('GET', url, true);
xmlResquest.setRequestHeader('Content-type', 'application/json');
xmlResquest.setRequestHeader(
'Authorization',
this.$store.getters.getToken()
);
let fileName = this.template.fileName + '.xlsx';
let elink = this.$refs.template;
xmlResquest.responseType = 'blob';
let $_vue = this;
this.loadingStatus = true;
xmlResquest.onload = function(oEvent) {
$_vue.loadingStatus = false;
if (xmlResquest.response.type == 'application/json') {
return $_vue.message.error('未找到下载文件');
}
let content = xmlResquest.response;
elink.download = fileName;
let blob = new Blob([content]);
elink.href = URL.createObjectURL(blob);
elink.click();
};
xmlResquest.send();
}
}
};
</script>
<style lang="less" scoped>
.upload-container {
min-height: 270px !important;
display: inline-block;
width: 100%;
padding: 10px;
border: 1px dashed #989898;
min-height: 250px;
border-radius: 5px;
.alert {
margin-top: 12px;
}
.el-button-group > * {
display: flex;
}
h3 {
margin: 9px 0px;
}
.file-info > span {
margin-right: 20px;
}
.v-r-message {
margin-top: 10px;
.title {
margin-bottom: 2px;
}
> .text {
font-size: 13px;
}
.v-r-success {
color: #02b702;
}
.v-r-error {
color: #dc0909;
}
}
}
</style>

View File

@@ -0,0 +1,46 @@
<template>
<mes-table
:tableData="tableData"
:columns="columns"
:height="411"
:pagination-hide="true"
:load-key="false"
:text-inline="false"
:ck="false"
></mes-table>
</template>
<script>
import MesTable from '@/components/basic/MesTable.vue';
import {
defineComponent,
ref,
reactive,
toRefs,
getCurrentInstance
} from 'vue';
export default defineComponent({
components: {
MesTable
},
props: {
tableData: {
type: Array,
default: () => {
return [];
}
}
},
setup() {
const columns = reactive([
{ title: '节点', field: 'stepName' },
{ title: '审批人', field: 'auditor' },
{ title: '审批结果', field: 'auditStatus' },
{ title: '审批时间', field: 'auditDate',width:150 },
{ title: '备注', field: 'remark' }
]);
return {
columns
}
}
});
</script>

View File

@@ -0,0 +1,179 @@
.view-container {
// padding: 15px;
background: white;
.grid-search {
padding-top: 15px;
//padding: 15px 15px 0 15px;
}
.grid-container,
.grid-body {
padding: 0 15px;
}
.view-header {
padding-left: 15px;
padding-right: 15px;
}
.fs-line {
height: 9px;
background: #f1f1f1;
margin-top: -10px;
margin-bottom: 10px;
}
}
.view-header {
height: 45px;
position: relative;
padding-bottom: 11px;
display: flex;
.search-line {
min-width: 150px;
}
.search-line > div {
margin-left: 5px;
margin-right: 10px;
}
.search-line > div > div {
width: 200px;
text-align: left;
}
.search-line > div:first-child {
flex: 1;
}
.search-line > div .ivu-select-dropdown {
max-height: 300px;
}
.btn-group {
white-space: nowrap;
button {
margin-left: 10px;
// padding: 5px 16px;
}
}
.btn-group .ivu-dropdown-item {
text-align: left !important;
}
.btn-group .ivu-dropdown-item:not(:last-child) {
border-bottom: 1px dotted #eee;
}
.desc-text {
margin-top: 5px;
font-weight: bold;
margin-bottom: 3px;
font-size: 14px;
color: #313131;
white-space: nowrap;
border-bottom: 2px solid #646565;
i {
font-size: 16px;
position: relative;
top: 1px;
margin-right: 2px;
}
}
.search-box {
background: #fefefe;
margin-top: 45px;
border: 1px solid #ececec;
position: absolute;
z-index: 999;
left: 0;
right: 0;
padding: 25px 40px;
padding-bottom: 0;
box-shadow: 0px 7px 18px -12px #bdc0bb;
}
.notice {
font-size: 13px;
color: #6b6b6b;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
top: 12px;
flex: 1;
left: 10px;
margin-right: 20px;
}
}
.table-info-cell-title {
background-color: #f5f5f5 !important;
}
.box-com {
> div.item {
// margin-bottom: 10px;
padding: 15px 17px 0 8px;
margin-bottom: 12px;
background: white;
}
> div.form-item {
padding: 19px 16px 0px 5px;
//box-shadow: 0 1px 7px rgb(199, 199, 199);
}
> div.table-item {
padding: 0 10px;
border-top: 1.5px solid #eaeaea;
}
.v-text {
line-height: 27px;
}
.form-text {
position: relative;
border-bottom: 1px solid #eee;
font-size: 14px;
margin-bottom: 14px;
}
}
.form-closex {
text-align: right;
padding-bottom: 24px;
}
.form-closex button {
margin-left: 10px;
padding: 4px 13px;
}
.toolbar {
padding: 3px 0px;
width: 100%;
display: flex;
.title {
line-height: 29px;
border-bottom: none;
font-size: 13px;
font-weight: bolder;
margin-bottom: 0;
color: #5d5c5c;
.icon {
color: #009688;
font-size: 18px;
}
i {
line-height: 29px;
border-bottom: none;
font-weight: bolder;
margin-bottom: 0;
color: #5d5c5c;
position: relative;
margin-top: -4px;
font-size: 14px;
}
}
.btns {
line-height: 28px;
flex: 1;
text-align: right;
button {
// border: none;
// margin-left:15px;
border: 0px;
padding: 0px 9px;
}
// button:hover{
// color: #FF9800;
// border-color: #FF9800;
// border: none;
// }
}
}

View File

@@ -0,0 +1,804 @@
<template>
<div class="layout-container">
<a :href="exportHref" ref="export"></a>
<!--开启懒加载2020.12.06 -->
<mes-box
:on-model-close="closeCustomModel"
v-model="viewModel"
:height="520"
:width="500"
:padding="0"
:lazy="true"
title="设置"
>
<template #content>
<custom-column :view-columns="viewColumns"></custom-column>
</template>
<template #footer>
<div style="text-align: center">
<el-button type="default" size="small" @click="closeCustomModel"
><i class="el-icon-close"></i>取消</el-button
>
<el-button type="success" size="small" @click="initViewColumns(true)"
><i class="el-icon-refresh"></i>重置</el-button
>
<el-button type="primary" size="small" @click="saveColumnConfig"
><i class="el-icon-check"></i>确定</el-button
>
</div>
</template>
</mes-box>
<ViewGridAudit @auditClick="saveAudit" :option="table" ref="audit">
</ViewGridAudit>
<!--开启懒加载2020.12.06 -->
<!--审核(异步点击按钮时才加载待完)-->
<!-- <mes-box
v-model="auditParam.model"
:height="auditParam.height"
:width="750"
:lazy="true"
:padding="0"
title="审批"
>
<template #content>
<el-tabs type="card">
<el-tab-pane label="当前审批">
<div class="v-steps">
<div
:class="{ 'step-current': item.isCurrent }"
class="step-item"
v-for="(item, index) in workFlowSteps"
:key="index"
>
<div class="left-item">
<div>审批时间</div>
<div class="left-date">{{ item.auditDate || '待审批' }}</div>
</div>
<div class="right-item">
<div class="step-line"></div>
<i class="step-circle"></i>
<div class="step-title">
{{ item.stepName }}
</div>
<div class="step-text">审批人{{ item.auditor }}</div>
<div class="step-text">
{{ getAuditStatus(item.auditStatus) }}
</div>
<div class="step-text"> {{ item.remark || '-' }}</div>
</div>
</div>
<div
:style="{
'margin-top': workFlowSteps.length ? '20px' : '-17px'
}"
class="audit-content"
v-show="auditParam.showAction"
>
<div style="margin-bottom:10px;">
审批
<el-radio-group
style="margin-left:15px"
v-model="auditParam.value"
>
<el-radio
v-for="item in auditParam.data"
:key="item.value"
:label="item.value"
>
<span>{{ item.text }}</span>
</el-radio>
</el-radio-group>
</div>
<el-input
v-model="auditParam.reason"
type="textarea"
style="margin-right: 13px;"
:autosize="{ minRows: 4, maxRows: 10 }"
placeholder="请输入备注..."
></el-input>
</div>
</div>
</el-tab-pane>
<el-tab-pane v-if="workFlowSteps.length" label="审批记录">
<audit-his :table-data="auditParam.auditHis"></audit-his>
</el-tab-pane>
</el-tabs>
</template>
<template #footer>
<div style="text-align: center;">
<el-button size="small" @click="auditParam.model = false"
><i class="el-icon-close"></i>关闭</el-button
>
<el-button
type="primary"
v-show="auditParam.showAction"
size="small"
@click="saveAudit"
><i class="el-icon-check"></i>审核</el-button
>
</div>
</template>
</mes-box> -->
<!--导入excel功能-->
<!--2020.10.31添加导入前的方法-->
<!--开启懒加载2020.12.06 -->
<!-- 2022.01.08增加明细表导入判断 -->
<mes-box
v-if="upload.url"
v-model="upload.excel"
:height="350"
:width="600"
:lazy="true"
:title="(boxModel ? detailOptions.cnName : table.cnName) + '-导入'"
>
<UploadExcel
ref="upload_excel"
@importExcelAfter="importExcelAfter"
:importExcelBefore="importExcelBefore"
:url="upload.url"
:template="upload.template"
></UploadExcel>
</mes-box>
<!--头部自定义组件-->
<component
:is="dynamicComponent.gridHeader"
ref="gridHeader"
@parentCall="parentCall"
></component>
<!--主界面查询与table表单布局-->
<div class="view-container">
<!-- 2020.09.11增加固定查询表单 -->
<!--查询条件-->
<div class="grid-search">
<div
:class="[fiexdSearchForm ? 'fiexd-search-box' : 'search-box']"
v-show="searchBoxShow"
>
<!-- 2020.09.13增加formFileds拼写错误兼容处理 -->
<mes-form
ref="searchForm"
:load-key="false"
style="padding: 0 15px"
:label-width="labelWidth"
:formRules="searchFormOptions"
:formFields="searchFormFields"
:select2Count="select2Count"
>
<template #footer>
<div v-if="!fiexdSearchForm" class="form-closex">
<el-button size="small" type="primary" plain @click="search">
<i class="el-icon-search" />查询
</el-button>
<el-button
size="small"
type="success"
plain
@click="resetSearch"
>
<i class="el-icon-refresh-right" />重置
</el-button>
<el-button
size="small"
plain
@click="searchBoxShow = !searchBoxShow"
>
<i class="el-icon-switch-button" />关闭
</el-button>
</div>
</template>
</mes-form>
<div v-if="fiexdSearchForm" class="fs-line"></div>
</div>
<div class="view-header">
<div class="desc-text">
<i class="el-icon-s-grid" />
<span>{{ table.cnName }}</span>
</div>
<div class="notice">
<a class="text" :title="extend.text">{{ extend.text }}</a>
</div>
<!--快速查询字段-->
<div class="search-line" v-if="!fiexdSearchForm">
<QuickSearch
v-if="singleSearch"
:singleSearch="singleSearch"
:searchFormFields="searchFormFields"
:tiggerPress="quickSearchKeyPress"
></QuickSearch>
</div>
<!--操作按钮组-->
<!-- 2020.11.29增加查询界面hidden属性 -->
<div class="btn-group">
<template
:key="bIndex"
v-for="(btn, bIndex) in buttons.slice(0, maxBtnLength)"
>
<el-dropdown size="small" v-if="btn.data" :split-button="false">
<el-button
:color="btn.color"
:dark="false"
:type="btn.type"
:plain="btn.plain"
>
{{ btn.name }}<i class="el-icon-arrow-down el-icon--right"></i
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(item, index) in btn.data"
:key="index"
>
<div @click="onClick(item.onClick)">
<i :class="item.icon"></i>
{{ item.name }}
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
v-else
:type="btn.type"
size="small"
:color="btn.color"
:dark="false"
:class="btn.class"
:plain="btn.plain"
v-show="!btn.hidden"
@click="onClick(btn.onClick)"
>
<i :class="btn.icon"></i> {{ btn.name }}
</el-button>
</template>
<el-button
type="default"
style="padding: 0px 10px"
size="small"
:plain="true"
v-if="showCustom"
@click="showCustomModel"
>
<i class="el-icon-s-grid"></i>
</el-button>
<el-dropdown
size="small"
@click="changeDropdown"
v-if="buttons.length > maxBtnLength"
>
<el-button type="primary" plain size="small">
更多<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click="changeDropdown(item.name)"
:name="item.name"
v-show="!item.hidden"
v-for="(item, dIndex) in buttons.slice(
maxBtnLength,
buttons.length
)"
:key="dIndex"
>
<i :class="item.icon"></i> {{ item.name }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<!-- 分割位置 -->
<mes-box
v-if="boxInit"
v-model="boxModel"
:title="boxOptions.title"
:width="boxOptions.width"
:height="boxOptions.height"
:modal="boxOptions.modal"
:draggable="boxOptions.draggable"
:padding="0"
:on-model-close="onGridModelClose"
>
<!--明细头部自定义组件-->
<template #content>
<div class="box-com">
<component
:is="dynamicComponent.modelHeader"
ref="modelHeader"
@parentCall="parentCall"
></component>
<!-- <div v-show="isBoxAudit" class="flow-step">
<div v-for="(item, index) in workFlowSteps" :key="index">
{{ item.stepName }}
</div>
</div> -->
<div class="item form-item">
<mes-form
ref="form"
:editor="editor"
:load-key="false"
:label-width="boxOptions.labelWidth"
:formRules="editFormOptions"
:formFields="editFormFields"
:select2Count="select2Count"
></mes-form>
</div>
<!--明细body自定义组件-->
<component
:is="dynamicComponent.modelBody"
ref="modelBody"
@parentCall="parentCall"
></component>
<div
v-show="hasDetail"
v-if="detail.columns && detail.columns.length > 0"
class="grid-detail table-item item"
>
<div class="toolbar">
<div class="title form-text">
<span>
<i class="el-icon-menu" />
{{ detail.cnName }}
</span>
</div>
<!--明细表格按钮-->
<div class="btns" v-show="!isBoxAudit">
<el-button
v-for="(btn, bIndex) in detailOptions.buttons"
:key="bIndex"
:plain="btn.plain"
v-show="!(typeof btn.hidden == 'boolean' && btn.hidden)"
@click="onClick(btn.onClick)"
size="small"
><i :class="btn.icon"></i>{{ btn.name }}</el-button
>
</div>
</div>
<mes-table
ref="detail"
@loadBefore="loadInternalDetailTableBefore"
@loadAfter="loadDetailTableAfter"
@rowChange="detailRowOnChange"
@rowClick="detailRowOnClick"
:url="detailOptions.url"
:load-key="false"
:index="true"
:tableData="detailOptions.data"
:columns="detailOptions.columns"
:pagination="detailOptions.pagination"
:height="detailOptions.height"
:single="detailOptions.single"
:pagination-hide="false"
:defaultLoadPage="detailOptions.load"
:doubleEdit="detailOptions.doubleEdit"
:beginEdit="detailOptions.beginEdit"
:endEditBefore="detailOptions.endEditBefore"
:endEditAfter="detailOptions.endEditAfter"
:summary="detailOptions.summary"
:click-edit="detailOptions.clickEdit"
:column-index="detailOptions.columnIndex"
:ck="detailOptions.ck"
:text-inline="detailOptions.textInline"
:select2Count="select2Count"
:selectable="detailSelectable"
></mes-table>
</div>
<!--明细footer自定义组件-->
<component
:is="dynamicComponent.modelFooter"
ref="modelFooter"
@parentCall="parentCall"
></component>
</div>
</template>
<template #footer>
<div style="text-align: center;" v-show="isBoxAudit">
<el-button
size="small"
type="primary"
plain
@click="onGridModelClose(false)"
>
<i class="el-icon-close">关闭</i>
</el-button>
<el-button
size="small"
type="primary"
v-show="auditParam.showViewButton"
@click="auditParam.model = true"
>
<i class="el-icon-view">审批</i>
</el-button>
</div>
<div v-show="!isBoxAudit">
<el-button
v-for="(btn, bIndex) in boxButtons"
:key="bIndex"
:type="btn.type"
size="small"
:plain="btn.plain"
v-show="!(typeof btn.hidden == 'boolean' && btn.hidden)"
:disabled="btn.hasOwnProperty('disabled') && !!btn.disabled"
@click="onClick(btn.onClick)"
>
<i :class="btn.icon"> {{ btn.name }}</i>
</el-button>
<el-button
size="small"
type="primary"
plain
@click="onGridModelClose(false)"
>
<i class="el-icon-close">关闭</i>
</el-button>
</div>
</template>
</mes-box>
</div>
<!--body自定义组件-->
<div class="grid-body">
<component
:is="dynamicComponent.gridBody"
ref="gridBody"
@parentCall="parentCall"
></component>
</div>
<!--table表格-->
<div class="grid-container">
<!-- 2021.05.02增加树形结构 rowKey -->
<mes-table
ref="table"
:single="single"
:rowKey="rowKey"
:loadTreeChildren="loadTreeTableChildren"
@loadBefore="loadTableBefore"
@loadAfter="loadTableAfter"
@rowChange="rowOnChange"
@rowClick="rowOnClick"
@rowDbClick="rowOnDbClick"
:tableData="[]"
:linkView="linkData"
:columns="columns"
:pagination="pagination"
:height="height"
:max-height="tableMaxHeight"
:pagination-hide="false"
:url="url"
:load-key="false"
:defaultLoadPage="load"
:summary="summary"
:double-edit="doubleEdit"
:index="doubleEdit"
:beginEdit="tableBeginEdit"
:endEditBefore="tableEndEditBefore"
:click-edit="true"
:column-index="columnIndex"
:text-inline="textInline"
:ck="ck"
:select2Count="select2Count"
:selectable="selectable"
></mes-table>
</div>
</div>
<!--footer自定义组件-->
<component
:is="dynamicComponent.gridFooter"
ref="gridFooter"
@parentCall="parentCall"
></component>
</div>
</template>
<script>
const _const = {
EDIT: 'update',
ADD: 'Add',
VIEW: 'view',
PAGE: 'getPageData',
AUDIT: 'audit',
DEL: 'del',
EXPORT: 'Export', //导出操作返回加密后的路径
DOWNLOAD: 'DownLoadFile', //导出文件
DOWNLOADTEMPLATE: 'DownLoadTemplate', //下载导入模板
IMPORT: 'Import', //导入(导入表的Excel功能)
UPLOAD: 'Upload' //上传文件
};
import Empty from '@/components/basic/Empty.vue';
import MesTable from '@/components/basic/MesTable.vue';
import MesForm from '@/components/basic/MesForm.vue';
import {
defineAsyncComponent,
defineComponent,
ref,
shallowRef,
toRaw
} from 'vue';
var vueParam = {
components: {
'mes-form': MesForm,
'mes-table': MesTable,
MesBox: defineAsyncComponent(() => import('@/components/basic/MesBox.vue')),
QuickSearch: defineAsyncComponent(() =>
import('@/components/basic/QuickSearch.vue')
),
Audit: defineAsyncComponent(() => import('@/components/basic/Audit.vue')),
UploadExcel: defineAsyncComponent(() =>
import('@/components/basic/UploadExcel.vue')
),
'custom-column': defineAsyncComponent(() =>
import('./ViewGridCustomColumn.vue')
),
'mes-header': defineAsyncComponent(() => import('./../MesHeader.vue')),
ViewGridAudit: defineAsyncComponent(() => import('./ViewGridAudit.vue'))
},
props: {},
setup(props) {
//2021.07.17调整扩展组件组件
const dynamicCom = {
gridHeader: Empty,
gridBody: Empty,
gridFooter: Empty,
modelHeader: Empty,
modelBody: Empty,
modelFooter: Empty
};
//合并扩展组件
if (props.extend.components) {
for (const key in props.extend.components) {
if (props.extend.components[key]) {
dynamicCom[key] = toRaw(props.extend.components[key]);
}
}
}
const dynamicComponent = shallowRef(dynamicCom);
return { dynamicComponent };
},
data() {
return {
isBoxAudit: false,
formFieldsType: [],
workFlowSteps: [],
//树形结构的主键字段如果设置值默认会开启树形table注意rowKey字段的值必须是唯一2021.05.02
rowKey: undefined,
fiexdSearchForm: false, //2020.09.011是否固定查询表单true查询表单将固定显示在表单的最上面
_inited: false,
doubleEdit: false, //2021.03.19是否开启查询界面表格双击编辑
single: false, //表是否单选
const: _const, //增删改查导入导出等对应的action
boxInit: false, //新建或编辑的弹出框初化状态,默认不做初始化,点击新建或编辑才初始化弹出框
searchBoxShow: false, //高级查询(界面查询后的下拉框点击触发)
singleSearch: {}, //快速查询字段
exportHref: '',
currentAction: _const.ADD, //当新建或编辑时,记录当前的状态:如当前操作是新建
currentRow: {}, //当前编辑或查看数据的行
closable: false,
boxModel: false, //弹出新建、编辑框
width: 700, //弹出框查看表数据结构
labelWidth: 100, //高级查询的标签宽度
viewModel: false, //查看表结构的弹出框
viewColumns: [], //查看表结构的列数据
viewColumnsClone: [],
showCustom: true, //是否显示自定义配置列按钮2022.05.27
// viewData: [], //查看表结构信息
maxBtnLength: 6, //界面按钮最多显示的个数,超过的数量都显示在更多中
buttons: [], //查询界面按钮 如需要其他操作按钮,可在表对应的.js中添加(如:Sys_User.js中buttons添加其他按钮)
splitButtons: [],
uploadfiled: [], //上传文件图片的字段
boxButtons: [], //弹出框按钮 如需要其他操作按钮,可在表对应的.js中添加
dicKeys: [], //当前界面所有的下拉框字典编号及数据源
hasKeyField: [], //有字典数据源的字段
keyValueType: { _dinit: false },
url: '', //界面表查询的数据源的url
hasDetail: false, //是否有从表(明细)表格数据
initActivated: false,
load: true, //是否默认加载表数据
activatedLoad: false, //页面触发actived时是否刷新页面数据
summary: false, //查询界面table是否显示合计
//需要从远程绑定数据源的字典编号,如果字典数据源的查询结果较多请在onInit中将字典编号添加进来
//只对自定sql有效
remoteKeys: [],
columnIndex: false, //2020.11.01是否显示行号
ck: true, //2020.11.01是否显示checkbox
continueAdd: false, //2021.04.11新建时是否可以连续新建操作
continueAddName: '保存后继续添加', //2021.04.11按钮名称
// detailUrl: "",
detailOptions: {
//弹出框从表(明细)对象
//从表配置
buttons: [], //弹出框从表表格操作按钮,目前有删除行,添加行,刷新操作,如需要其他操作按钮,可在表对应的.js中添加
cnName: '', //从表名称
key: '', //从表主键名
data: [], //数据源
columns: [], //从表列信息
edit: true, //明细是否可以编辑
single: false, //明细表是否单选
load: false, //
delKeys: [], //当编辑时删除当前明细的行主键值
url: '', //从表加载数据的url
pagination: { total: 0, size: 100, sortName: '' }, //从表分页配置数据
height: 0, //默认从表高度
textInline: true, //明细表行内容显示在一行上如果需要换行显示请设置为false
doubleEdit: true, //使用双击编辑
clickEdit: false, //是否开启点击单元格编辑,点击其他行时结束编辑
currentReadonly: false, //当前用户没有编辑或新建权限时,表单只读(可用于判断用户是否有编辑或新建权限)
//开启编辑时
beginEdit: (row, column, index) => {
return true;
},
//结束编辑前
endEditBefore: (row, column, index) => {
return true;
},
//结束编辑后
endEditAfter: (row, column, index) => {
return true;
},
columnIndex: false, //2020.11.01明细是否显示行号
ck: true //2020.11.01明细是否显示checkbox
},
auditParam: {
//审核对象
rows: 0, //当前选中审核的行数
model: false, //审核弹出框
value: -1, //审核结果
status: -1,
reason: '', //审核原因
height: 500,
showViewButton: true,
auditHis: [],
showAction: false, //是否显示审批操作(当前节点为用户审批时显示)
//审核选项(可自行再添加)
data: [
{ text: '通过', value: 1 },
{ text: '拒绝', value: 2 },
{ text: '驳回', value: 3 }
]
},
upload: {
//导入上传excel对象
excel: false, //导入的弹出框是否显示
url: '', //导入的路径,如果没有值,则不渲染导入功能
template: {
//下载模板对象
url: '', //下载模板路径
fileName: '' //模板下载的中文名
},
init: false //是否有导入权限,有才渲染导入组件
},
height: 0, //表高度
tableHeight: 0, //查询页面table的高度
tableMaxHeight: 0, //查询页面table的最大高度
textInline: true, //table内容超出后是否不换行2020.01.16
pagination: { total: 0, size: 30, sortName: '' }, //从分页配置数据
boxOptions: {
title: '', //弹出框显示的标题2022.08.01
saveClose: true,
labelWidth: 100,
height: 0,
width: 0,
summary: false, //弹出框明细table是否显示合计
draggable: true, //2022.09.12弹出框拖动功能
modal: true //2022.09.12弹出框背景遮罩层
}, //saveClose新建或编辑成功后是否关闭弹出框//弹出框的标签宽度labelWidth
editor: {
uploadImgUrl: '', //上传路径
upload: null //上传方法
},
numberFields: [],
//2022.09.26增加自定义导出文件名
downloadFileName: null,
select2Count: 500 //超出500数量显示select2组件
};
},
methods: {},
activated() {
this.initFlowQuery();
//2020.06.25增加activated方法
this.onActivated && this.onActivated();
if (!this._inited) {
this._inited = true;
return;
}
if (this.activatedLoad) {
this.refresh();
}
},
mounted() {
this.mounted();
// this.$refs.searchForm.forEach()
},
unmounted() {
this.destroyed();
},
created: function() {
//合并自定义业务扩展方法
Object.assign(this, this.extend.methods);
//如果没有指定排序字段,则用主键作为默认排序字段
this.pagination.sortName = this.table.sortName || this.table.key;
this.initBoxButtons(); //初始化弹出框与明细表格按钮
this.initAuditColumn();
this.onInit(); //初始化前,如果需要做其他处理在扩展方法中覆盖此方法
this.getButtons();
//初始化自定义表格列
this.initViewColumns();
//初始编辑框等数据
this.initBoxHeightWidth();
this.initDicKeys(); //初始下框数据源
this.onInited(); //初始化后,如果需要做其他处理在扩展方法中覆盖此方法
},
beforeUpdate: function() {},
updated: function() {}
};
import props from './props.js';
import methods from './methods.js';
//合并属性
vueParam.props = Object.assign(vueParam.props, props);
//合并方法
vueParam.methods = Object.assign(
vueParam.methods,
methods,
props.extend.methods
);
export default defineComponent(vueParam);
</script>
<style lang="less" scoped>
@import './ViewGrid.less';
</style>
<style lang="less" scoped>
.btn-group ::v-deep(.ivu-select-dropdown) {
padding: 0px !important;
right: 3px;
}
.btn-group ::v-deep(.ivu-select-dropdown .ivu-dropdown-menu) {
min-width: 100px;
right: -2px;
position: absolute;
background: white;
width: 130px;
border-radius: 5px;
border: 1px solid #e7e5e5;
}
.vertical-center-modal ::v-deep(.srcoll-content) {
padding: 0;
}
.view-model-content {
background: #eee;
}
.grid-detail ::v-deep(.v-table .el-table__header th) {
height: 44px;
}
</style>
<style lang="less" scoped>
.grid-search {
position: relative;
.search-box {
background: #fefefe;
margin-top: 33px;
border: 1px solid #eae8e8;
position: absolute;
z-index: 999;
left: 15px;
right: 15px;
padding: 25px 20px;
padding-bottom: 0;
border-top: 0;
box-shadow: 0 7px 18px -12px #bdc0bb;
}
}
</style>

View File

@@ -0,0 +1,366 @@
<template>
<mes-box :footer="false" v-model="model" :height="height" :width="width" :padding="0" :lazy="true" title="审核">
<div class="audit-model-content" :style="{ height: height - 70 + 'px' }">
<el-radio-group v-show="hasFlow" style="padding-left: 15px;" v-model="activeName" class="ml-4">
<el-radio label="audit" size="large">审核</el-radio>
<el-radio label="log" size="large">审核记录</el-radio>
</el-radio-group>
<div v-show="activeName == 'audit' || !hasFlow" class="audit-content">
<div class="fx-left" v-if="hasFlow">
<div class="v-steps">
<div v-for="(item, index) in workFlowSteps" :key="index">
<div class="step-item" v-if="item.stepAttrType == 'start'">
<div class="left-item">
<div>流程开始</div>
<div class="left-date">{{ item.createDate }}</div>
</div>
<div class="right-item">
<div class="step-line"></div>
<i class="step-circle"></i>
<div class="step-title">
{{ item.stepName }}
</div>
<div class="step-text">发起人{{ item.creator }}</div>
</div>
</div>
<div class="step-item" v-else-if="item.stepAttrType == 'end'">
<div class="left-item">
<div>流程结束</div>
</div>
<div class="right-item">
<div class="step-line"></div>
<i class="step-circle"></i>
<div class="step-title">
{{ item.stepName }}
</div>
</div>
</div>
<div v-else :class="{ 'step-current': item.isCurrent }" class="step-item">
<div class="left-item">
<div>审批时间</div>
<div class="left-date">{{ item.auditDate || '待审批' }}</div>
</div>
<div class="right-item">
<div class="step-line"></div>
<i class="step-circle"></i>
<div class="step-title">
{{ item.stepName }}
</div>
<div class="step-text">审批人{{ item.auditor }}</div>
<div class="step-text">
{{ getAuditStatus(item.auditStatus) }}
</div>
<div class="step-text"> {{ item.remark || '-' }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="fx-right" :style="{ width: !hasFlow ? '100%' : '400px' }" v-if="isCurrentUser || !hasFlow">
<div v-if="!hasFlow">
<el-alert :title="'当前选中【' +rowLen + '】条记录待审核..'" type="success" :closable="false"/>
</div>
<div class="rd">
<span>审批</span>
<el-radio-group style="margin-left:15px" v-model="auditParam.value">
<el-radio v-for="item in auditParam.data" :key="item.value" :label="item.value">
<span>{{ item.text }}</span>
</el-radio>
</el-radio-group>
</div>
<el-input style="padding-top: 10px;" v-model="auditParam.reason" type="textarea"
:autosize="{ minRows: 4, maxRows: 10 }" placeholder="请输入备注..."></el-input>
<div class="btn">
<el-button type="primary" @click="auditClick" icon="Check">审批</el-button>
</div>
</div>
</div>
<div v-show="activeName == 'log'">
<mes-table :tableData="tableData" :columns="columns" :height="370" :pagination-hide="true" :load-key="false"
:text-inline="false" :ck="false"></mes-table>
</div>
</div>
</mes-box>
</template>
<script>
import MesTable from '@/components/basic/MesTable.vue';
import MesBox from '@/components/basic/MesBox.vue';
import http from '@/../src/api/http.js';
import { defineComponent, ref, reactive, getCurrentInstance } from 'vue';
export default defineComponent({
components: {
MesTable,
MesBox
},
props: {
option: { //生成vue文件的table参数
type: Object,
default: {
key: '',
cnName: '',
name: '',
url: ""
}
}
},
setup(props, { emit }) {
const height = ref(500);
const width = ref(820);
const model = ref(false)
const workFlowSteps = reactive([]);
const hasFlow = ref(false)
const auditParam = reactive({
//审核对象
rows: 0, //当前选中审核的行数
model: false, //审核弹出框
value: -1, //审核结果
reason: '', //审核原因
//审核选项(main.js里面可以添加其他选项)
data: []
})
const { proxy } = getCurrentInstance();
auditParam.data = proxy.$global.audit.data;
const tableData = reactive([]);
const columns = reactive([
{ title: '节点', field: 'stepName', width: 100 },
{ title: '审批人', field: 'auditor', width: 80 },
{ title: '审批结果', field: 'auditStatus', width: 70, bind: { data: [] } },
{ title: '审批时间', field: 'auditDate', width: 145 },
{ title: '备注', field: 'remark', width: 120 }
]);
const isCurrentUser = ref(null);
const activeName = ref('audit')
const auditDic = reactive([]);
const getAuditStatus = (key) => {
return (auditDic.find(x => { return x.key === key + '' }) || { value: key }).value;
}
const rowLen=ref(0)
let currentRows = []
const getAuditInfo = () => {
const table = props.option.url.replaceAll('/', '');
const url = `api/Sys_WorkFlow/getSteps?tableName=${table}`
let ids = currentRows.map(x => { return x[props.option.key] });
// ['498043c1-fbd0-4a35-a870-523823912a9b']
http.post(url, ids, true).then(result => {
if (!result.status) {
proxy.$message.error(result.message);
return;
}
hasFlow.value = !!(result.list || []).length;
if (!hasFlow.value) {
let auditStatus = Object.keys(currentRows[0]).find(x => { return x.toLowerCase() === 'auditstatus' });
let checkStatus = currentRows.every((x) => {
return proxy.$global.audit.status.some(c => { return c === x[auditStatus] || !x[auditStatus] })
});
if (!checkStatus) {
proxy.$message.error('只能选择待审批或审核中的数据');
return;
}
rowLen.value=currentRows.length;
model.value = true;
width.value = 430;
height.value = 330;
isCurrentUser.value = true;
//没有审批流程的数据只显示
return;
}
model.value = true;
height.value = document.body.clientHeight*0.9;
width.value = 820;
if (!auditDic.length) {
auditDic.push(...(result.auditDic || []))
columns.forEach(item => {
if (item.field == 'auditStatus') {
item.bind.data = auditDic;
}
})
}
isCurrentUser.value = result.list.some(x => { return x.isCurrentUser })
workFlowSteps.length = 0;
workFlowSteps.push(...result.list);
tableData.length = 0;
tableData.push(...result.log)
})
}
//
const auditClick = () => {
if (auditParam.value == -1) {
proxy.$message.error('请选择审批项');
return;
}
emit("auditClick", auditParam, currentRows, (result) => {
if (result.status) {
model.value = false;
tableData.length = 0;
}
});
}
const open = (rows) => {
currentRows = rows;
activeName.value = 'audit'
auditParam.reason = '';
auditParam.value = -1;
getAuditInfo();
}
return {
columns,
height,
width,
model,
workFlowSteps,
getAuditInfo,
getAuditStatus,
activeName,
reactive,
tableData,
auditParam,
auditClick,
open,
isCurrentUser,
hasFlow,
rowLen
}
}
});
</script>
<style lang="less" scoped>
.audit-model-content {
padding: 10px;
}
.step-item {
background: #fff;
display: flex;
}
.left-item {
min-width: 180px;
text-align: right;
padding-right: 25px;
padding-top: 8px;
.left-date {
font-size: 13px;
padding-top: 7px;
color: #6c6c6c;
}
}
.right-item {
cursor: pointer;
position: relative;
border-bottom: 1px solid #f3f3f3;
padding: 5px 0 5px 5px;
}
.left-item,
.right-item {
padding-bottom: 10px;
}
.right-item:last-child {
border-bottom: 0;
}
.step-line {
top: 16px;
left: -10px;
width: 1px;
height: 100%;
position: absolute;
background-color: #ebedf0;
}
.step-circle {
position: absolute;
top: 17px;
left: -9px;
z-index: 2;
font-size: 12px;
line-height: 1;
transform: translate(-50%, -50%);
width: 7px;
height: 7px;
background-color: #a1a1a1;
border-radius: 50%;
}
.right-item::before {
content: '';
}
.step-content {
padding-top: 2px;
font-size: 14px;
color: #828282;
line-height: 1.5;
}
.step-title {
font-weight: bold;
padding-top: 3px;
}
.step-text {
font-size: 13px;
color: #999999;
padding-top: 6px;
}
.step-current {
* {
color: #2f95ff !important;
}
.step-circle {
background: #2f95ff !important;
}
// border-radius: 5px;
// border: 1px solid #d6eaff;
font-size: 13px;
padding-top: 6px;
// background-color: #eff7ffd9;
color: black;
}
.audit-content {
// background: #f9f9f9;
padding: 10px;
border-radius: 4px;
display: flex;
.fx-left {
flex: 1;
width: 0;
.rd {
display: flex;
align-items: baseline;
}
}
.fx-right {
// width: 400px;
.btn {
margin-top: 10px;
text-align: center;
}
}
}
</style>

View File

@@ -0,0 +1,145 @@
export default {
initViewColumns(isReset) {
//初始化自定列配置
if (isReset) {
this.resetViewColumns();
}
if (!this.orginColumnFields) {
this.orginColumnFields = this.columns.map((c) => {
return c.field;
});
}
this.viewColumns = this.columns
.filter((c) => {
return !c.hidden && !c.render;
})
.map((c) => {
return { field: c.field, title: c.title, show: !c.hidden };
});
if (isReset) {
return;
}
this.getCacheViewColumn();
},
getViewCacheKey(){
return 'custom:column'+this.table.name;
},
getCacheViewColumn() {
try {
let columns = localStorage.getItem(this.getViewCacheKey());
if (!columns) return;
columns = JSON.parse(columns);
let sortTableColumns = [];
//弹出框的列
let _viewColumns = [];
columns.forEach((column) => {
let _column = this.viewColumns.find((c) => {
return c.field == column.field;
});
if (_column) {
_column.show = column.show;
_viewColumns.push(_column);
}
let tableColumn = this.columns.find((c) => {
return c.field == column.field;
});
if (tableColumn) {
tableColumn.hidden = !column.show;
sortTableColumns.push(tableColumn);
}
});
//重新排版弹出框自定义列
let otherColumns = this.viewColumns.filter((c) => {
return !_viewColumns.some((s) => {
return c.field == s.field;
});
});
//重新排版弹出框自定义列
_viewColumns.push(...otherColumns);
this.viewColumns.splice(0);
this.viewColumns.push(..._viewColumns);
this.sortViewColumns(sortTableColumns);
} catch (error) {
console.log('设置默认自定义列异常:' + error.message);
}
},
sortViewColumns(sortColumns) {
if (sortColumns.length) {
let hiddenColumns = this.columns.filter((c) => {
return !sortColumns.some((s) => {
return c.field == s.field;
});
});
sortColumns.push(...hiddenColumns);
this.columns.splice(0);
this.columns.push(...sortColumns);
}
},
resetViewColumns() {
if (!this.orginColumnFields) {
return;
}
let _columns = [];
this.orginColumnFields.forEach((x) => {
_columns.push(
this.columns.find((c) => {
return c.field == x;
})
);
});
let otherColumns = this.columns.filter((c) => {
return !this.orginColumnFields.some((s) => {
return c.field == s;
});
});
_columns.push(...otherColumns);
this.columns.splice(0);
this.columns.push(..._columns);
},
showCustomModel() {
if (!this.viewColumns.length) {
this.initViewColumns();
}
this.viewColumnsClone = JSON.parse(JSON.stringify(this.viewColumns));
this.viewModel = true;
},
closeCustomModel() {
this.viewModel=false;
if (this.checkColumnChanged()) {
this.viewColumns = JSON.parse(JSON.stringify(this.viewColumnsClone));
}
},
checkColumnChanged() {
return (
JSON.stringify(this.viewColumns) != JSON.stringify(this.viewColumnsClone)
);
},
saveColumnConfig() {
let hasShowColumn = this.viewColumns.some((x) => {
return x.show;
});
if (!hasShowColumn) {
return this.$message.error('至少选择一列显示');
}
this.viewModel = false;
if (this.checkColumnChanged()) {
let sortColumns = [];
this.viewColumns.forEach((column) => {
let _column = this.columns.find((c) => {
return c.field == column.field;
});
if (_column) {
_column.hidden = !column.show;
sortColumns.push(_column);
}
});
this.sortViewColumns(sortColumns);
}
try {
localStorage.setItem(this.getViewCacheKey(), JSON.stringify(this.viewColumns));
} catch (error) {
console.log('获取自定义列异常:' + error.message);
}
}
};

View File

@@ -0,0 +1,95 @@
<template>
<el-alert
title="拖动列名可调整表格列显示顺序"
type="success"
:show-icon="false"
>
</el-alert>
<div class="view-column view-column-title">
<div class="view-column-index">#</div>
<div class="view-column-left">列名</div>
<div class="view-column-right">是否显示</div>
</div>
<draggable
class="list-group"
tag="transition-group"
:component-data="componentData"
:list="viewColumns"
v-bind="dragOptions"
item-key="order"
>
<transition-group class="drag-center-item">
<div
class="view-column"
v-for="(column, index) in viewColumns"
:key="index"
>
<div class="view-column-index">{{ index + 1 }}</div>
<div class="view-column-left">{{ column.title }}</div>
<div class="view-column-right">
<el-checkbox v-model="column.show">
<div style="height: 100%; width: 250px"></div
></el-checkbox>
</div>
</div>
</transition-group>
</draggable>
</template>
<script>
import { VueDraggableNext } from 'vue-draggable-next';
import { defineComponent, ref, reactive } from 'vue';
export default defineComponent({
props: {
viewColumns: {
type: Array,
default: () => {
return [];
}
}
},
components: {
draggable: VueDraggableNext
},
data() {
return {};
},
setup(props, context) {
const dragOptions = reactive({
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost'
});
const componentData = reactive({
tag: 'ul',
type: 'transition-group'
});
return { dragOptions, componentData };
}
});
</script>
<style lang="less" scoped>
.view-column {
cursor: pointer;
display: flex;
padding: 10px;
border-bottom: 1px solid #f3f3f3;
.view-column-index {
width: 50px;
}
.view-column-left {
width: 120px;
padding: 0 10px;
}
.view-column-right {
flex: 1;
}
}
.view-column-title {
font-weight: bold;
}
.view-column:last-child {
border-bottom: 0;
}
</style>

View File

@@ -0,0 +1,97 @@
//从表方法
let detailMethods = {
//查询从表前先做内部处理
loadInternalDetailTableBefore(param, callBack) {
//加载明细表数据之前,需要设定查询的主表的ID
//每次只要加载明细表格数据就重置删除明细的值
if (this.detailOptions.delKeys.length > 0) {
this.detailOptions.delKeys = [];
}
let key = this.table.key;
if (this.currentRow && this.currentRow.hasOwnProperty(key)) {
param.value = this.currentRow[key];
}
return this.loadDetailTableBefore(param, callBack);
},
detailRowOnChange(row) {
this.detailRowChange(row);
},
detailRowChange(row) {
//checkbox选中行事件
},
detailRowOnClick({ row, column, event }) {
//明细表点击行事件2020.11.07
this.detailRowClick({ row, column, event });
},
detailRowClick({ row, column, event }) {},
resetDetailTable(row) {
//编辑和查看明细时重置从表数据
if (!this.detailOptions.columns || this.detailOptions.columns.length == 0) {
return;
}
let key = this.table.key;
let query = { value: row ? row[key] : this.currentRow[key] };
this.$nextTick(() => {
if (this.$refs.detail) {
this.$refs.detail.reset();
this.$refs.detail.load(query);
}
});
},
//从后面加载从表数据
refreshRow() {
this.resetDetailTable();
},
addRow() {
this.$refs.detail.addRow({});
this.updateDetailTableSummaryTotal();
},
delRow() {
let rows = this.$refs.detail.getSelected();
if (!rows || rows.length == 0) {
return this.$message.error('请选择要删除的行!');
}
if (!this.delDetailRow(rows)) {
return false;
}
let tigger = false;
this.$confirm('确认要删除选择的数据吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
if (tigger) return;
tigger = true;
rows = this.$refs.detail.delRow();
let key = this.detailOptions.key;
//记录删除的行数据
rows.forEach((x) => {
if (x.hasOwnProperty(key) && x[key]) {
this.detailOptions.delKeys.push(x[key]);
}
});
this.updateDetailTableSummaryTotal();
});
},
updateDetailTableSummaryTotal() {
//2021.09.25增加明细表删除、修改时重新计算行数与汇总
//2021.12.12增加明细表判断(强制刷新合计时会用到)
if (!this.$refs.detail) {
return;
}
//删除或新增行时重新设置显示的总行数
this.$refs.detail.paginations.total = this.$refs.detail.rowData.length;
//重新设置合计
if (this.$refs.detail.summary) {
this.$refs.detail.columns.forEach((column) => {
if (column.summary) {
this.$refs.detail.getInputSummaries(null, null, null, column);
}
});
}
}
};
export default detailMethods;

View File

@@ -0,0 +1,7 @@
import Grid from './ViewGrid.vue'
const ViewGrid = {
install: function (app) {
app.component('ViewGrid', Grid)
}
}
export default ViewGrid

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
let props = {
columns: {//当前表的配置信息
type: Array,
default: () => {
return [];
}
},
detail: {//从表明细配置
type: Object,
default: () => {
return {
columns: [],//从表列
sortName: ""//从表排序字段
};
}
},
editFormFields: {//新建、编辑字段(key/value)
type: Object,
default: () => {
return {};
}
},
editFormOptions: {//新建、编辑配置信息
type: Array,
default: () => {
return [];
}
},
searchFormFields: {//查询字段(key/value)
type: Object,
default: () => {
return {};
}
},
searchFormOptions: {//查询配置信息(key/value)
type: Array,
default: () => {
return [];
}
},
table: {//表的配置信息:主键、排序等
type: Object,
default: () => {
return {};
}
},
extend: {//表的扩展方法与组件都合并到此属性中
type: Object,
default: () => {
return {};
}
}
}
export default props;

View File

@@ -0,0 +1,106 @@
let serviceFilter = {
onInit () { //对应created
console.log('Create执行前')
},
onInited () { //对应created在onInit与onInited中间会初始化界面数据对象
console.log('Create执行后')
},
mounted () {
console.log('mounted');
},
searchBefore (param) { //查询ViewGird表数据前,param查询参数
// console.log('表' + this.table.cnName + '触发loadTableBefore');
return true;
},
//2020.10.30增加查询后返回所有的查询信息
searchAfter (param, result) { //查询ViewGird表数据后param查询参数,result回返查询的结果
// console.log('表' + this.table.cnName + '触发loadTableAfter');
return true;
},
searchDetailBefore (param) {//查询从表表数据前,param查询参数
//console.log(this.detailOptions.cnName + '触发loadDetailTableBefore');
return true;
},
searchDetailAfter (param, data) {//查询从表后param查询参数,result回返查询的结果
// console.log(this.detailOptions.cnName + '触发loadDetailTableAfter');
return true;
},
delBefore (ids, rows) { //查询界面的表删除前 ids为删除的id数组,,rows删除的行
return true;
},
delAfter (result) {//查询界面的表删除后
return true;
},
delDetailRow (rows) { //弹出框删除明细表的行数据(只是对table操作并没有操作后台)
return true;
},
addBefore (formData) { //新建保存前formData为对象包括明细表
return true;
},
async addBeforeAsync (formData) { //异步处理,功能同上(2020.12.06)
return true;
},
addAfter (result) {//新建保存后result返回的状态及表单对象
return true;
},
updateBefore (formData) { //编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
async updateBeforeAsync (formData) { //异步处理,功能同上(2020.12.06)
return true;
},
updateAfter (result) {//编辑保存后result返回的状态及表单对象
return true;
},
auditBefore (ids, rows) {//审核前
return true;
},
auditAfter (result, rows) {// 审核后
return true;
},
resetAddFormBefore () { //重置新建表单前的内容
return true;
},
resetAddFormAfter () { //重置新建表单后的内容
return true;
},
resetUpdateFormBefore () { //重置编辑表单前的内容
return true;
},
resetUpdateFormAfter () { //重置编辑表单后的内容
return true;
},
modelOpenBefore (row) { //点击编辑/新建按钮弹出框前,可以在此处写逻辑,如,从后台获取数据
},
modelOpenAfter (row) { //点击编辑/新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
},
importAfter (data) { //导入excel后刷新table表格数据
this.search();
},
//2020.10.31添加导入前的方法
importExcelBefore (formData) { //导入excel导入前
//往formData写一些其他参数提交到后台
// formData.append("val2", "xxx");
//后台按下面方法获取请求的参数
// Core.Utilities.HttpContext.Current.Request("val2");
return true;
},
reloadDicSource () { //重新加载字典绑定的数据源
this.initDicKeys();
},
exportBefore (param) { //2020.06.25增加导出前处理
return true;
},
onModelClose(iconClick){
//iconClick=true为点击左中上角X触发的关闭事件
//如果返回 false不会关闭弹出框
//return false;
this.boxModel=false;
}
}
export default serviceFilter;

View File

@@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,154 @@
<template>
<div class="hello" ref="mesWangEditor"></div>
</template>
<script>
import E from "wangeditor";
export default {
props: {
url: {
//上传图片的url
type: String,
default: "",
},
upload: {
//上传方法
type: Function,
// (file, insertImgFn) => {}
default: null,
},
uploadCount: {
//最多可以上传(图片)的数量
type: Number,
default: 3,
},
modelValue: "",
width: {
type: String,
default: "100%",
},
height: {
type: Number,
default: 250,
},
minWidth: {
type: Number,
default: 650,
},
minHeight: {
type: Number,
default: 100,
},
},
name: "wang-editor",
data() {
return {
lastHtml: "",
change: false,
editor: null,
init: false,
};
},
watch: {
modelValue(newVal, val) {
if (
(newVal !== val &&
this.lastHtml !== "" &&
val === this.lastHtml &&
this.editor.txt.html() === this.lastHtml) ||
this.editor.txt.html() === ""
) {
this.editor.txt.html(newVal);
}
this.lastHtml = newVal;
},
},
destroyed() {
this.editor = null;
},
mounted() {
this.editor = null;
let editor = new E(this.$refs.mesWangEditor);
this.editor = editor;
let $this = this;
editor.config.zIndex = 500;
editor.config.height = this.height;
editor.config.onchange = (html) => {
if (!this.init && this.lastHtml === "") {
this.lastHtml = html;
this.init = true;
}
this.$emit("update:modelValue", html);
};
// editor.config.uploadFileName = "fileInput";
// //设置header
// editor.config.uploadImgHeaders = {
// Accept: "application/json",
// Authorization: this.$store.getters.getToken(),
// };
//上传地址
editor.config.uploadImgServer = this.http.ipAddress + this.url;
// console.log(editor.config.uploadImgServer);
editor.config.customUploadImg = function (resultFiles, insertImgFn) {
// 自定义上传
if ($this.upload) {
console.log("调用自定义的上传方法");
console.log(resultFiles);
// resultFiles 是 input 中选中的文件列表
// insertImgFn 是获取图片 url 后,插入到编辑器的方法
//有可能会上传多张图片,上传多张图片就需要进行遍历
resultFiles.map((item) => {
// _this.getUploadImg(item, insertImgFn);
$this.upload(item, insertImgFn);
});
} else {
let formData = new FormData();
let nameArr = [];
resultFiles.forEach(function (file) {
formData.append("fileInput", file, file.name);
nameArr.push(file.name);
});
if (!$this.url) {
$this.$message.error("未配置url");
return;
}
$this.http.post($this.url, formData, true).then((x) => {
if (!x.status) {
return $this.$message.error(x.message);
}
nameArr.forEach(m=>{
insertImgFn($this.http.ipAddress + x.data + m);
})
// let imgs = nameArr
// .map((m) => {
// return $this.http.ipAddress + x.data + m;
// })
// .join(",");
// insertImgFn(imgs);
});
}
};
editor.create();
editor.txt.html(this.modelValue);
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1,
h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<div style="height: 100%">
<redirect-error :text="text" message="请求确认是否配置权限" :errorNumber="errorNumber"></redirect-error>
</div>
</template>
<script>
import RedirectError from "./RedirectError";
export default {
components: {
RedirectError,
},
data() {
return {
errorNumber: "401",
text: "抱歉,您没有权限进行此操作~",
};
},
};
</script>

View File

@@ -0,0 +1,21 @@
<template>
<div style="height:100%;">
<redirect-error :text="text" :errorNumber="errorNumber"></redirect-error>
</div>
</template>
<script>
import RedirectError from "./RedirectError";
export default {
components: {
RedirectError
},
data() {
return {
errorNumber:'404',
text: "抱歉,页面好像去火星了~"
};
}
};
</script>

View File

@@ -0,0 +1,39 @@
<template>
<div class="middle-box">
<div class="text-center animated fadeInDown">
<i style="font-size: 50px;color: #67c23a;margin-top:40px;" class="el-icon-circle-check"></i>
<div style="font-size: 20px;margin-top: 10px;" class="error-desc">{{ text }}</div>
</div>
</div>
</template>
<script>
export default {
props: {
text: {
type: String,
default: "操作成功!",
},
},
methods: {
},
};
</script>
<style lang="less" scoped>
body {
background-color: #fff;
}
.middle-box {
text-align: center;
padding-top: 80px;
height: 100%;
// background: #eee;
h1 {
font-size: 140px;
font-weight: 100;
}
.back {
padding: 10px;
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div class="middle-box">
<div class="text-center animated fadeInDown">
<h1>{{ errorNumber }}</h1>
<h3 class="font-bold">{{message}}</h3>
<slot></slot>
<div class="error-desc">{{ text }}</div>
<div class="back">
<el-button type="primary" @click="backHome" icon="md-arrow-round-back"
>返回首页</el-button >
</div>
</div>
</div>
</template>
<script>
import { Script } from "vm";
export default {
props: {
errorNumber: {
type: String,
default: "500",
},
message: {
type: String,
default: "页面未找到!",
},
text: {
type: String,
default: "唉...好像出了点问题~",
},
},
methods: {
backHome: function () {
this.$router.push({
path: "/home",
});
},
},
};
</script>
<style lang="less" scoped>
body {
background-color: #fff;
}
.middle-box {
text-align: center;
padding-top: 80px;
height: 100%;
// background: #eee;
h1 {
font-size: 140px;
font-weight: 100;
}
.back {
padding: 10px;
}
}
</style>

View File

@@ -0,0 +1,25 @@
<template>
<div style="height:100%;">
<redirect-error :text="text" :errorNumber="errorNumber">
<div>
<router-link to="SellOrder">
<Button>点击查看[测试完整示例]</Button>
</router-link>
</div>
</redirect-error>
</div>
</template>
<script>
import RedirectError from "./RedirectError";
export default {
components: {
RedirectError
},
data() {
return {
errorNumber: "用例正在整理中",
text: "详细用例在正准备中,目前可参考[测试完整示例]的使用方法"
};
}
};
</script>

View File

@@ -0,0 +1,54 @@
let dataA = {
"name": "流程A",
"nodeList": [
{
"id": "0",
"name": "流程开始",
"type": "start",
"left": "346px",
"top": "22px",
"ico": "el-icon-user-solid"
},
{
"id": "nodeA",
"name": "流程A-节点A",
"type": "node",
"left": "346px",
"top": "132px",
"ico": "el-icon-user-solid"
},
// {
// "id": "nodeB",
// "name": "流程A-节点B",
// "type": "node",
// "left": "347px",
// "top": "235px",
// "ico": "el-icon-goods"
// },
// {
// "id": "nodeC",
// "name": "流程A-节点C",
// "type": "node",
// "left": "323px",
// "top": "399px",
// "ico": "el-icon-present"
// }
],
"lineList": [
{
"from": "0",
"to": "nodeA"
},
// {
// "from": "nodeA",
// "to": "nodeB"
// },
// {
// "from": "nodeB",
// "to": "nodeC"
// }
]
}
export function getDataA () {
return dataA
}

View File

@@ -0,0 +1,182 @@
/**
* 感谢 https://github.com/chaangliu/ForceDirectedLayout/blob/master/javascript/force-directed.js
* A force directed graph layout implementation by liuchang on 2018/05/10.
*/
const CANVAS_WIDTH = 1000
const CANVAS_HEIGHT = 1000
let k
let mNodeList = []
let mEdgeList = []
let mDxMap = {}
let mDyMap = {}
let mNodeMap = {}
export function ForceDirected(data = {}) {
// generate nodes and edges
// for (let i = 0; i < 20; i++) {
// mNodeList.push(new Node(i))
// }
k = 0
mNodeList = []
mEdgeList = []
mDxMap = {}
mDyMap = {}
mNodeMap = {}
let nodeList = data.nodeList
for (let i = 0; i < nodeList.length; i++) {
let node = nodeList[i]
mNodeList.push(node)
}
// for (let i = 0; i < 20; i++) {
// let edgeCount = Math.random() * 8 + 1
// for (let j = 0; j < edgeCount; j++) {
// let targetId = Math.floor(Math.random() * 20)
// let edge = new Edge(i, targetId)
// mEdgeList.push(edge)
// }
// }
// line 转 edge
let lineList = data.lineList
for (let i = 0; i < lineList.length; i++) {
let line = lineList[i]
let edge = new Edge(line.from, line.to)
mEdgeList.push(edge)
}
if (mNodeList && mEdgeList) {
k = Math.sqrt(CANVAS_WIDTH * CANVAS_HEIGHT / mNodeList.length)
}
for (let i = 0; i < mNodeList.length; i++) {
let node = mNodeList[i]
if (node) {
mNodeMap[node.id] = node
}
}
// 随机生成坐标. Generate coordinates randomly.
let initialX, initialY, initialSize = 40.0
for (let i in mNodeList) {
initialX = CANVAS_WIDTH * 0.5
initialY = CANVAS_HEIGHT * 0.5
mNodeList[i].x = initialX + initialSize * (Math.random() - 0.5)
mNodeList[i].y = initialY + initialSize * (Math.random() - 0.5)
}
// 迭代200次. Iterate 200 times.
for (let i = 0; i < 200; i++) {
calculateRepulsive()
calculateTraction()
updateCoordinates()
}
// console.log(JSON.stringify(new Result(mNodeList, mEdgeList)))
// 坐标添加px
for (let i = 0; i < mNodeList.length; i++) {
let node = mNodeList[i]
node.left = node.x + 'px'
node.top = node.y + 'px'
node.x = undefined
node.y = undefined
}
data.nodeList = mNodeList
// console.log(data)
return data
}
function Node(id = null) {
this.id = id
this.x = 22
this.y = null
}
function Edge(source = null, target = null) {
this.source = source
this.target = target
}
/**
* 计算两个Node的斥力产生的单位位移。
* Calculate the displacement generated by the repulsive force between two nodes.*
*/
function calculateRepulsive() {
let ejectFactor = 6
let distX, distY, dist
for (let i = 0; i < mNodeList.length; i++) {
mDxMap[mNodeList[i].id] = 0.0
mDyMap[mNodeList[i].id] = 0.0
for (let j = 0; j < mNodeList.length; j++) {
if (i !== j) {
distX = mNodeList[i].x - mNodeList[j].x
distY = mNodeList[i].y - mNodeList[j].y
dist = Math.sqrt(distX * distX + distY * distY)
}
if (dist < 30) {
ejectFactor = 5
}
if (dist > 0 && dist < 250) {
let id = mNodeList[i].id
mDxMap[id] = mDxMap[id] + distX / dist * k * k / dist * ejectFactor
mDyMap[id] = mDyMap[id] + distY / dist * k * k / dist * ejectFactor
}
}
}
}
/**
* 计算Edge的引力对两端Node产生的引力。
* Calculate the traction force generated by the edge acted on the two nodes of its two ends.
*/
function calculateTraction() {
let condenseFactor = 3
let startNode, endNode
for (let e = 0; e < mEdgeList.length; e++) {
let eStartID = mEdgeList[e].source
let eEndID = mEdgeList[e].target
startNode = mNodeMap[eStartID]
endNode = mNodeMap[eEndID]
if (!startNode) {
console.log('Cannot find start node id: ' + eStartID + ', please check it out.')
return
}
if (!endNode) {
console.log('Cannot find end node id: ' + eEndID + ', please check it out.')
return
}
let distX, distY, dist
distX = startNode.x - endNode.x
distY = startNode.y - endNode.y
dist = Math.sqrt(distX * distX + distY * distY)
mDxMap[eStartID] = mDxMap[eStartID] - distX * dist / k * condenseFactor
mDyMap[eStartID] = mDyMap[eStartID] - distY * dist / k * condenseFactor
mDxMap[eEndID] = mDxMap[eEndID] + distX * dist / k * condenseFactor
mDyMap[eEndID] = mDyMap[eEndID] + distY * dist / k * condenseFactor
}
}
/**
* 更新坐标。
* update the coordinates.
*/
function updateCoordinates() {
let maxt = 4, maxty = 3 // Additional coefficients.
for (let v = 0; v < mNodeList.length; v++) {
let node = mNodeList[v]
let dx = Math.floor(mDxMap[node.id])
let dy = Math.floor(mDyMap[node.id])
if (dx < -maxt) dx = -maxt
if (dx > maxt) dx = maxt
if (dy < -maxty) dy = -maxty
if (dy > maxty) dy = maxty
node.x = node.x + dx >= CANVAS_WIDTH || node.x + dx <= 0 ? node.x - dx : node.x + dx
node.y = node.y + dy >= CANVAS_HEIGHT || node.y + dy <= 0 ? node.y - dy : node.y + dy
}
}
function Result(nodes = null, links = null) {
this.nodes = nodes
this.links = links
}

View File

@@ -0,0 +1,258 @@
/*画布容器*/
.efContainer {
position: relative;
overflow: scroll;
flex: 1;
}
.tools {
position: absolute;
left: 220px;
/* background: #fff; */
/* border: 1px solid #d8d7d7; */
/* border-radius: 5px; */
padding: 5px 5px;
display: flex;
z-index: 99;
background: #fcfcfc;
}
/*顶部工具栏*/
.ef-tooltar {
padding-left: 10px;
box-sizing: border-box;
height: 42px;
line-height: 42px;
z-index: 3;
border-bottom: 1px solid #dadce0;
}
.jtk-overlay {
cursor: pointer;
color: #4a4a4a;
}
.ef-node-pmenu-item {
padding: 10px;
background: #f8f8f8;
font-size: 13px;
font-weight: bold;
letter-spacing: 1px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
border-right: 1px solid #eee;
}
/*节点菜单*/
.ef-node-pmenu {
cursor: pointer;
height: 32px;
line-height: 32px;
width: 225px;
display: block;
font-weight: bold;
color: #4a4a4a;
padding-left: 5px;
}
.ef-node-pmenu:hover {
background-color: #e0e0e0;
}
.ef-node-menu-item {
padding: 10px;
}
.ef-node-menu-li {
cursor: move;
border: 1px solid #eee;
padding: 2px 13px;
text-align: left;
line-height: 28px;
margin: 4px;
border-radius: 3px;
background: #f0f9eb;
font-size: 12px;
float: left;
width: 98px;
}
.ef-node-menu-li > div:first-child{
display: inline-block;
padding: 4px;
}
.ef-node-menu-li:hover {
/* 设置移动样式*/
cursor: move;
border: 1px dashed #787be8;
color: #787be8;
/* background-color: #F0F7FF;
border: 1px dashed #1879FF;
border-left: 4px solid #1879FF;
padding-left: 5px; */
}
.ef-node-menu-ul {
list-style: none;
padding-left: 0;
margin: 0;
}
/*节点的最外层容器*/
.ef-node-container {
position: absolute;
display: flex;
width: 170px;
height: 32px;
border: 1px solid #e0e3e7;
border-radius: 5px;
background-color: #fff;
}
.ef-node-container:hover {
/* 设置移动样式*/
cursor: move;
background-color: #f0f7ff;
/*box-shadow: #1879FF 0px 0px 12px 0px;*/
background-color: #f0f7ff;
border: 1px dashed #1879ff;
}
/*节点激活样式*/
.ef-node-active {
background-color: #f0f7ff;
/*box-shadow: #1879FF 0px 0px 12px 0px;*/
background-color: #f0f7ff;
border: 1px solid #1879ff;
}
/*节点左侧的竖线*/
.ef-node-left {
width: 4px;
background-color: #1879ff;
border-radius: 4px 0 0 4px;
}
/*节点左侧的图标*/
.ef-node-left-ico {
line-height: 32px;
margin-left: 8px;
}
.ef-node-left-ico:hover {
/* 设置拖拽的样式 */
cursor: crosshair;
}
/*节点显示的文字*/
.ef-node-text {
color: #565758;
font-size: 12px;
line-height: 32px;
margin-left: 8px;
width: 100px;
/* 设置超出宽度文本显示方式*/
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
/*节点右侧的图标*/
.ef-node-right-ico {
line-height: 32px;
position: absolute;
right: 5px;
color: #84cf65;
cursor: default;
}
/*节点的几种状态样式*/
.el-node-state-success {
line-height: 32px;
position: absolute;
right: 5px;
color: #84cf65;
cursor: default;
}
.el-node-state-error {
line-height: 32px;
position: absolute;
right: 5px;
color: #f56c6c;
cursor: default;
}
.el-node-state-warning {
line-height: 32px;
position: absolute;
right: 5px;
color: #e6a23c;
cursor: default;
}
.el-node-state-running {
line-height: 32px;
position: absolute;
right: 5px;
color: #84cf65;
cursor: default;
}
/*node-form*/
.ef-node-form-header {
height: 32px;
border-top: 1px solid #dce3e8;
border-bottom: 1px solid #dce3e8;
background: #f1f3f4;
color: #000;
line-height: 32px;
padding-left: 12px;
font-size: 14px;
}
.ef-node-form-body {
margin-top: 10px;
padding-right: 10px;
padding-bottom: 20px;
}
/* 连线中的label 样式*/
.jtk-overlay.flowLabel:not(.aLabel) {
/* padding: 4px 10px; */
padding: 1px 8px 2px 8px;
background-color: white;
color: #a9aaaa !important;
border: 1px solid #e0e3e7;
border-radius: 3px;
}
/* label 为空的样式 */
.emptyFlowLabel {
}
.ef-dot {
background-color: #1879ff;
border-radius: 10px;
}
.ef-dot-hover {
background-color: red;
}
.ef-rectangle {
background-color: #1879ff;
}
.ef-rectangle-hover {
background-color: red;
}
.ef-img {
}
.ef-img-hover {
}
.ef-drop-hover {
border: 1px dashed #1879ff;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
export const easyFlowMixin = {
data() {
return {
jsplumbSetting: {
// 动态锚点、位置自适应
Anchors: ['Top', 'TopCenter', 'TopRight', 'TopLeft', 'Right', 'RightMiddle', 'Bottom', 'BottomCenter', 'BottomRight', 'BottomLeft', 'Left', 'LeftMiddle'],
// 容器ID
Container: 'efContainer',
// 连线的样式,直线或者曲线等,可选值: StateMachine、FlowchartBezier、Straight
Connector: ['Bezier', {curviness: 100}],
// Connector: ['Straight', {stub: 20, gap: 1}],
// Connector: ['Flowchart', {stub: 30, gap: 1, alwaysRespectStubs: false, midpoint: 0.5, cornerRadius: 10}],
// Connector: ['StateMachine', {margin: 5, curviness: 10, proximityLimit: 80}],
// 鼠标不能拖动删除线
ConnectionsDetachable: false,
// 删除线的时候节点不删除
DeleteEndpointsOnDetach: false,
/**
* 连线的两端端点类型:圆形
* radius: 圆的半径,越大圆越大
*/
// Endpoint: ['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}],
/**
* 连线的两端端点类型:矩形
* height: 矩形的高
* width: 矩形的宽
*/
// Endpoint: ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}],
/**
* 图像端点
*/
// Endpoint: ['Image', {src: 'https://www.easyicon.net/api/resizeApi.php?id=1181776&size=32', cssClass: 'ef-img', hoverClass: 'ef-img-hover'}],
/**
* 空白端点
*/
Endpoint: ['Blank', {Overlays: ''}],
// Endpoints: [['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}], ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}]],
/**
* 连线的两端端点样式
* fill: 颜色值,如:#12aabb为空不显示
* outlineWidth: 外边线宽度
*/
EndpointStyle: {fill: '#1879ffa1', outlineWidth: 1},
// 是否打开jsPlumb的内部日志记录
LogEnabled: true,
/**
* 连线的样式
*/
PaintStyle: {
// 线的颜色
stroke: '#E0E3E7',
// 线的粗细,值越大线越粗
strokeWidth: 1,
// 设置外边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
outlineStroke: 'transparent',
// 线外边的宽,值越大,线的点击范围越大
outlineWidth: 10
},
DragOptions: {cursor: 'pointer', zIndex: 2000},
/**
* 叠加 参考: https://www.jianshu.com/p/d9e9918fd928
*/
Overlays: [
// 箭头叠加
['Arrow', {
width: 10, // 箭头尾部的宽度
length: 8, // 从箭头的尾部到头部的距离
location: 1, // 位置建议使用01之间
direction: 1, // 方向默认值为1表示向前可选-1表示向后
foldback: 0.623 // 折回也就是尾翼的角度默认0.623当为1时为正三角
}],
// ['Diamond', {
// events: {
// dblclick: function (diamondOverlay, originalEvent) {
// console.log('double click on diamond overlay for : ' + diamondOverlay.component)
// }
// }
// }],
['Label', {
label: '',
location: 0.1,
cssClass: 'aLabel'
}]
],
// 绘制图的模式 svg、canvas
RenderMode: 'svg',
// 鼠标滑过线的样式
HoverPaintStyle: {stroke: '#b0b2b5', strokeWidth: 1},
// 滑过锚点效果
// EndpointHoverStyle: {fill: 'red'}
Scope: 'jsPlumb_DefaultScope' // 范围具有相同scope的点才可连接
},
/**
* 连线参数
*/
jsplumbConnectOptions: {
isSource: true,
isTarget: true,
// 动态锚点、提供了4个方向 Continuous、AutoDefault
anchor: 'Continuous',
// 设置连线上面的label样式
labelStyle: {
cssClass: 'flowLabel'
},
// 修改了jsplumb 源码支持label 为空传入自定义style
emptyLabelStyle: {
cssClass: 'emptyFlowLabel'
}
},
/**
* 源点配置参数
*/
jsplumbSourceOptions: {
// 设置可以拖拽的类名只要鼠标移动到该类名上的DOM就可以拖拽连线
filter: '.flow-node-drag',
filterExclude: false,
anchor: 'Continuous',
// 是否允许自己连接自己
allowLoopback: true,
maxConnections: -1,
onMaxConnections: function (info, e) {
console.log(`超过了最大值连线: ${info.maxConnections}`)
}
},
// 参考 https://www.cnblogs.com/mq0036/p/7942139.html
jsplumbSourceOptions2: {
// 设置可以拖拽的类名只要鼠标移动到该类名上的DOM就可以拖拽连线
filter: '.flow-node-drag',
filterExclude: false,
// anchor: 'Continuous',
// 是否允许自己连接自己
allowLoopback: true,
connector: ['Flowchart', {curviness: 50}],
connectorStyle: {
// 线的颜色
stroke: 'red',
// 线的粗细,值越大线越粗
strokeWidth: 1,
// 设置外边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
outlineStroke: 'transparent',
// 线外边的宽,值越大,线的点击范围越大
outlineWidth: 10
},
connectorHoverStyle: {stroke: 'red', strokeWidth: 2}
},
jsplumbTargetOptions: {
// 设置可以拖拽的类名只要鼠标移动到该类名上的DOM就可以拖拽连线
filter: '.flow-node-drag',
filterExclude: false,
// 是否允许自己连接自己
anchor: 'Continuous',
allowLoopback: true,
dropOptions: {hoverClass: 'ef-drop-hover'}
}
}
}
}

View File

@@ -0,0 +1,94 @@
<template>
<div ref="node" class="node-item" :style="nodeContainerStyle" @click="clickNode" @mouseup="changeNodeSite" :class="nodeContainerClass">
<!-- 最左侧的那条竖线 -->
<div class="ef-node-left"></div>
<!-- 节点类型的图标 -->
<div class="ef-node-left-ico flow-node-drag">
<i :class="nodeIcoClass"></i>
</div>
<!-- 节点名称 -->
<div class="ef-node-text" :show-overflow-tooltip="true">
{{ node.name }}
</div>
<i @click.stop="delNode" v-if="node.type=='node'" style="display: none" class="el-icon-delete"></i>
<!-- 节点状态图标 -->
<div class="ef-node-right-ico">
<i class="el-icon-circle-check el-node-state-success" v-show="node.state === 'success'"></i>
<i class="el-icon-circle-close el-node-state-error" v-show="node.state === 'error'"></i>
<i class="el-icon-warning-outline el-node-state-warning" v-show="node.state === 'warning'"></i>
<i class="el-icon-loading el-node-state-running" v-show="node.state === 'running'"></i>
</div>
</div>
</template>
<script>
export default {
props: {
node: Object,
activeElement: Object
},
data() {
return {}
},
computed: {
nodeContainerClass() {
return {
'ef-node-container': true,
'ef-node-active': this.activeElement.type == 'node' ? this.activeElement.nodeId === this.node.id : false
}
},
// 节点容器样式
nodeContainerStyle() {
return {
top: this.node.top,
left: this.node.left
}
},
nodeIcoClass() {
var nodeIcoClass = {}
nodeIcoClass[this.node.ico] = true
// 添加该class可以推拽连线出来viewOnly 可以控制节点是否运行编辑
nodeIcoClass['flow-node-drag'] = this.node.viewOnly ? false : true
return nodeIcoClass
}
},
methods: {
// 点击节点
clickNode() {
this.$emit('clickNode', this.node.id)
},
// 鼠标移动后抬起
changeNodeSite() {
// 避免抖动
if (this.node.left == this.$refs.node.style.left && this.node.top == this.$refs.node.style.top) {
return;
}
this.$emit('changeNodeSite', {
nodeId: this.node.id,
left: this.$refs.node.style.left,
top: this.$refs.node.style.top,
})
}, delNode() {
this.$emit("delNode");
},
}
}
</script>
<style scoped>
/* .node-item{
position: relative;
} */
.node-item:hover .el-icon-delete {
display: inline-block !important;
}
.el-icon-delete {
cursor: pointer;
position: relative;
top: -18px;
padding-left: 5px;
right: -16px;
color: #f61313;
height: 20px;
}
</style>

View File

@@ -0,0 +1,198 @@
<template>
<div class="node-filter-container">
<!-- <div class="add-btn">
<span class="name">条件设置</span> <el-button @click="addItem" link><i>+</i>添加字段</el-button>
</div> -->
<!-- {{ $store.getters.data().flowTable.WorkTable }} -->
<div class="ef-node-pmenu-item" style="display: flex;">
<div style="flex:1;">
<span class="name"><i class="el-icon-news"></i>条件设置</span>
</div>
<div><el-button link size="small" @click="addItem" type="primary">
+ 添加字段</el-button></div>
</div>
<div>
<table>
<tr>
<td>字段</td>
<td style="width:90px">条件</td>
<td class="value"></td>
<td style="width: 40px;">操作</td>
</tr>
<tr v-for="(item, index) in filters" :key="index">
<td><el-select @change="(field) => { fieldChange(field, index) }" size="small" v-model="item.field"
placeholder="请选择">
<el-option v-for="data in fieldsOptions" :key="data.field" :label="data.name"
:value="data.field" />
</el-select></td>
<td><el-select size="small" v-model="item.filterType" placeholder="请选择">
<el-option v-for="data in filterType" :key="data.value" :label="data.name"
:value="data.value" />
</el-select></td>
<td>
<template v-if="item.data">
<el-select v-if="item.data.length >= 300" multiple size="small" v-model="item.value"
placeholder="请选择">
<el-option v-for="data in item.data" :key="data.key" :label="data.value"
:value="data.key" />
</el-select>
<el-select-v2 style="width: 100%;" v-else multiple size="small" :options="item.data"
v-model="item.value" placeholder="请选择">
</el-select-v2>
</template>
<el-input v-else v-model="item.value" size="small"></el-input>
</td>
<td @click="delItem(index)" class="item-del"><i class="el-icon-delete"></i></td>
</tr>
</table>
</div>
<!-- <div>
<label>自定义sql</label>
<div><el-input type="textarea" v-model="customSql"></el-input></div>
</div> -->
</div>
</template>
<script>
let _this = this;
export default {
props: {
tableName: {
type: String,
default: ""
},
filters: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
filter: this.$store.getters.data().flowTable,
customSql: "",
value: "",
//{ field: "名称", value: "", filterType: "=" },
// filters: [],
fieldsOptions: [
],
t: [],
filterType: [{ name: "等于(=)", value: "=" },
{ name: "不等于(!=)", value: "!=" },
{ name: "大于(>)", value: ">" },
{ name: "大于等于(>=)", value: ">=" },
{ name: "小于(<)", value: "<" },
{ name: "小于等于(<=)", value: "<=" },
{ name: "包括(in)", value: "in" },
// { name: "不包括(not in)", value: "notin" },
{ name: "模糊匹配(like)", value: "like" },
{ name: "或者(or)", value: "or" }
]
}
},
methods: {
delItem(index) {
this.$confirm('确认要删除字配置条件配置吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
this.filters.splice(index, 1);
});
},
addItem() {
this.filters.push({ field: "", value: "", filterType: "", data: null })
},
fieldChange(field, index) {
let option = this.fieldsOptions.find(x => { return x.field == field });
this.filters[index].field = option.field;
this.filters[index].value = option.data ? [] : null;
this.filters[index].data = option.data;;
},
convertOptions(result) {
},
getOptions(tableName) {
const url = 'api/Sys_WorkFlow/getFields?table=' + tableName;
this.http.post(url, {}, false).then(result => {
result.forEach(c => {
if (c.data && c.data.length < 300) {
c.data = c.data.map(x => {
return {
value: x.key,
label: x.value,
key: x.key
}
})
}
})
_this.fieldsOptions = result;
})
}
},
watch: {
'filter.WorkTable': {
handler(newvalue, oldvalue) {
if (newvalue) {
this.getOptions(newvalue);
} else {
// this.fieldsOptions.splice(0)
}
}
}
// deep:true,
// filter(newVal,oldVal){
// alert(1)
// }
},
created() {
_this = this;
},
}
</script>
<style lang="less" scoped>
.node-filter-container {
margin-top: 15px;
table {
width: 100%;
padding-left: 6px;
td {
font-size: 13px;
padding: 5px;
}
tr:first-child {
font-size: 12px;
font-weight: bolder;
}
.item-del {
text-align: center;
color: rgb(226, 4, 4);
cursor: pointer;
}
.value {
width: 150px;
}
}
.add-btn {
text-align: right;
padding-right: 10px;
border-bottom: 1px solid #e8e8e8;
padding-bottom: 5px;
}
.node-filter-item {}
}</style>

View File

@@ -0,0 +1,340 @@
<template>
<div>
<div class="ef-node-form">
<div class="ef-node-pmenu-item">
<div style="flex:1;">
<span class="name"><i class="el-icon-news"></i>节点属性</span>
<!-- <span @click="nameClick(1)" :class="{ active: index === 1 }" class="name">审批条件</span> -->
</div>
<!-- <div><el-button link size="small" type="primary" @click="save"><i class="el-icon-check"></i>
保存配置</el-button></div> -->
</div>
<div class="ef-node-form-body">
<div class="form-info">
<MesForm ref="form" style="padding:0 10px;" :label-width="130" :loadKey="false" :formFields="node"
:formRules="formRules">
</MesForm>
</div>
<div>
<node-filter :filters="node.filters" :tableName="tableName" ref="filter">
</node-filter>
</div>
</div>
<!-- <div class="el-node-form-tag"></div>-->
</div>
</div>
</template>
<script>
// import { cloneDeep } from 'lodash'
import MesForm from '@/components/basic/MesForm.vue';
import nodeFilter from './node_filter.vue';
export default {
components: {
MesForm,
'node-filter': nodeFilter
},
props: {
// node: {
// type: Object,
// default: () => {
// return {
// name: '',
// auditType: 1,//审核类型
// userId: null,
// roleId: null,
// deptId: null,
// auditRefuse: null,//审核未通过
// auditBack: null, //驳回
// auditMethod: 0,//审批方式(会签)
// stepValue: null,
// sendMail: 0,
// filters: [] //字段过滤条件
// }
// }
// }
},
created() {
this.http.get('api/Sys_WorkFlow/getNodeDic').then((result) => {
this.formRules.forEach((options) => {
options.forEach((option) => {
if (option.dataKey && !option.data.length) {
option.data = result[option.dataKey] || [];
}
});
});
});
},
data() {
return {
tableName:"",
index: 1,
visible: true,
// node 或 line
type: 'node',
node: {},
line: {},
data: {},
node: {
name: '',
auditType: 1,//审核类型
userId: null,
roleId: null,
deptId: null,
auditRefuse: null,//审核未通过
auditBack: null, //驳回
auditMethod: 0,//审批方式(会签)
// nodeValue: null,
sendMail: 0,
filters: []
},
formRules: [
[
{
title: '节点名称',
field: 'name',
required: true,
colSize: 12
}],
[
{
dataKey: '',
title: '审批类型',
required: true,
hidden: false,
field: 'auditType',
data: [
{ key: 1, value: '按用户审批' },
{ key: 2, value: '按角色审批' },
{ key: 3, value: '按部门审批' }
],
type: 'select',
onChange: this.nodeTypeChange
},
{
dataKey: 'users',
hidden: false,
title: '审批用户',
required: true,
field: 'userId',
data: [],
type: 'select'
}
,
{
dataKey: 'roles',
hidden: true,
title: '角色信息',
required: true,
field: 'roleId',
data: [],
type: 'select'
}
,
{
dataKey: 'dept',
hidden: true,
title: '部门信息',
required: true,
field: 'deptId',
data: [],
type: 'select'
}
], [
{
dataKey: '',
title: '审批未通过',
required: false,
field: 'auditRefuse',
hidden: false,
data: [
{ key: 1, value: '返回上一节点' },
{ key: 2, value: '流程重新开始' },
{ key: 0, value: '流程结束' },
],
type: 'select',
colSize: 6
}
,
{
dataKey: '',
title: '审批驳回',
required: false,
hidden: false,
field: 'auditBack',
data: [
{ key: 1, value: '返回上一节点' },
{ key: 2, value: '流程重新开始' },
{ key: 0, value: '流程结束' },
],
type: 'select',
colSize: 6
}
],
[
{
dataKey: '',
title: '审核后发送邮件通知',
required: false,
hidden: false,
field: 'sendMail',
data: [
{ key: 1, value: '是' },
{ key: 0, value: '否' },
],
type: 'switch'
},
{
dataKey: '',
title: '启用会签',
required: false,
hidden: false,
field: 'auditMethod',//审批方式
data: [
{ key: 1, value: '是' },
{ key: 0, value: '否' }
],
type: 'radio'
}
],
],
}
},
methods: {
nameClick(index) {
this.index = index;
},
/**
* 表单修改这里可以根据传入的ID进行业务信息获取
* @param data
* @param id
*/
nodeInit(data, id,tableName) {
this.tableName=tableName;
this.type = 'node'
this.data = data;
// this.tableName=data.
data.nodeList.filter((node) => {
if (node.id === id) {
this.formRules.forEach(options => {
options.forEach(c => {
if (c.field != 'name') {
c.hidden = node.type == 'start' || node.type == 'end';
}
})
})
if (!node.filters) {
node.filters=[];
}
this.node = node;// cloneDeep(node)
if (node.type != 'start' && node.type != 'end') {
this.nodeTypeChange(node.auditType);
}
}
})
// data.nodeList.filter((node) => {
// if (node.id === id) {
// let _node = cloneDeep(node);
// _node.roleId = _node.roleId || null;
// _node.userId = _node.userId || null;
// _node.nodeType = (_node.nodeType || 1) * 1;
// if (!node.filters) {
// node.filters = [];
// }
// _node.filters = node.filters;
// this.nodeTypeChange(_node.nodeType);
// Object.assign(this.node, _node);
// }
// });
},
nodeTypeChange(value) {
// { key: 1, value: '按用户审批' },
// { key: 2, value: '按角色审批' },
// { key: 3, value: '按部门审批' }
this.formRules.forEach((options) => {
options.forEach((option) => {
if (option.field == 'userId') {
option.hidden = value != 1;
} else if (option.field == 'roleId') {
option.hidden = value != 2;
} else if (option.field == 'deptId') {
option.hidden = value != 3;
}
});
});
},
lineInit(line) {
this.type = 'line'
this.line = line
},
// 修改连线
saveLine() {
this.$emit('setLineLabel', this.line.from, this.line.to, this.line.label)
},
save() {
this.data.nodeList.filter((node) => {
if (node.id === this.node.id) {
node.name = this.node.name;
node.left = this.node.left;
node.top = this.node.top;
node.ico = this.node.ico;
node.state = this.node.state;
node.stepValue = this.node.stepValue;
this.$emit('repaintEverything', this.node);
}
});
this.$message.success('保存成功')
}
}
}
</script>
<style lang="less" scoped>
.el-node-form-tag {
position: absolute;
top: 50%;
margin-left: -15px;
height: 40px;
width: 15px;
background-color: #fbfbfb;
border: 1px solid rgb(220, 227, 232);
border-right: none;
z-index: 0;
}
.btns {
text-align: center;
padding: 10px;
buttton {
flex: 1;
}
}
.ef-node-pmenu-item {
display: flex;
.name {
cursor: pointer;
margin-right: 15px;
}
.active {
color: #0659e8;
}
}
.form-info ::v-deep(.mes-form-item) {
display: flex;
.el-form-item:nth-child(2),
.el-form-item:nth-child(3),
.el-form-item:nth-child(4) {
margin-left: 12px;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<div class="flow-menu" ref="tool">
<div v-for="menu in menuList" :key="menu.id">
<div class="ef-node-pmenu-item"><i class="el-icon-notebook-2"></i>节点配置</div>
<ul v-show="menu.open" class="ef-node-menu-ul">
<draggable @end="end" @start="move" v-model="menu.children" :options="draggableOptions">
<li v-for="subMenu in menu.children" class="ef-node-menu-li" :key="subMenu.id" :type="subMenu.type">
<i :class="subMenu.ico"></i> {{ subMenu.name }}
</li>
</draggable>
</ul>
</div>
</div>
</template>
<script>
import { VueDraggableNext as draggable } from "vue-draggable-next";
var mousePosition = {
left: -1,
top: -1
}
export default {
data() {
return {
activeNames: '1',
// draggable配置参数参考 https://www.cnblogs.com/weixin186/p/10108679.html
draggableOptions: {
preventOnFilter: false,
sort: false,
disabled: false,
ghostClass: 'tt',
// 不使用H5原生的配置
forceFallback: true,
// 拖拽的时候样式
// fallbackClass: 'flow-node-draggable'
},
// 默认打开的左侧菜单的id
defaultOpeneds: ['1', '2'],
menuList: [
{
id: '1',
type: 'group',
name: '开始节点',
ico: 'el-icon-video-play',
open: true,
children: [
{
id: '0',
type: 'start',
name: '流程开始',
ico: 'el-icon-time',
// 自定义覆盖样式
style: {}
},
{
id: '1',
type: 'end',
name: '流程结束',
ico: 'el-icon-switch-button',
// 自定义覆盖样式
style: {}
}, {
id: '2',
type: 'node',
name: '流程节点',
ico: 'el-icon-news',
// 自定义覆盖样式
style: {}
}
]
}],
nodeMenu: {}
}
},
components: {
draggable
},
created() {
/**
* 以下是为了解决在火狐浏览器上推拽时弹出tab页到搜索问题
* @param event
*/
if (this.isFirefox()) {
document.body.ondrop = function (event) {
// 解决火狐浏览器无法获取鼠标拖拽结束的坐标问题
mousePosition.left = event.layerX
mousePosition.top = event.clientY - 50
event.preventDefault();
event.stopPropagation();
}
}
},
methods: {
// 根据类型获取左侧菜单对象
getMenuByType(type) {
for (let i = 0; i < this.menuList.length; i++) {
let children = this.menuList[i].children;
for (let j = 0; j < children.length; j++) {
if (children[j].type === type) {
return children[j]
}
}
}
},
// 拖拽开始时触发
move(evt, a, b, c) {
var type = evt.item.attributes.type.nodeValue
this.nodeMenu = this.getMenuByType(type)
},
// 拖拽结束时触发
end(evt, e) {
this.$emit('addNode', evt, this.nodeMenu, mousePosition)
},
// 是否是火狐浏览器
isFirefox() {
var userAgent = navigator.userAgent
if (userAgent.indexOf("Firefox") > -1) {
return true
}
return false
}
}
}
</script>

View File

@@ -0,0 +1,594 @@
<!-- 审核流程插件基于https://gitee.com/xiaoka2017/easy-flow修改-->
<!--感谢萌级小菜鸟 / easy-flow -->
<template>
<div v-if="easyFlowVisible" class="flow-panel">
<div style="display: flex;height: 100%;position: relative;">
<el-scrollbar style="height: 100%;border-right: 1px solid rgb(220, 227, 232);">
<div style="width: 220px;">
<div class="ef-node-pmenu-item"><i class="el-icon-warning-outline"></i>基础信息</div>
<MesForm ref="form" style="padding: 10px;" :label-width="180" :loadKey="true" :formFields="formFields"
:formRules="formRules"></MesForm>
<node-menu @addNode="addNode" ref="nodeMenu"></node-menu>
</div>
</el-scrollbar>
<div class="tools">
<el-button circle @click="zoomAdd"><i class="el-icon-zoom-in"></i></el-button>
<el-button circle @click="zoomSub"><i class="el-icon-zoom-out"></i></el-button>
</div>
<div style="flex: 1;" id="efContainer" ref="efContainer" class="container efContainer" v-flowDrag>
<template :key="node.id" v-for="node in data.nodeList">
<flow-node :id="node.id" @delNode="deleteNode(node.id)" :node="node" :activeElement="activeElement"
@changeNodeSite="changeNodeSite" @nodeRightMenu="nodeRightMenu" @clickNode="clickNode">
</flow-node>
</template>
<!-- 给画布一个默认的宽度和高度 -->
<div style="position:absolute;top: 3000px;left: 4000px;">&nbsp;</div>
</div>
<!-- 右侧表单 -->
<div style="width: 400px;border-left: 1px solid #dce3e8;background-color: #FBFBFB">
<el-scrollbar style="height: 100%;padding-bottom: 10px;">
<flow-node-form @delNode="deleteNode" ref="nodeForm" @setLineLabel="setLineLabel"
@repaintEverything="repaintEverything"></flow-node-form>
</el-scrollbar>
</div>
</div>
</div>
</template>
<script>
import { VueDraggableNext as draggable } from "vue-draggable-next";
// import { jsPlumb } from 'jsplumb'
// 使用修改后的jsplumb
import './jsplumb'
import { easyFlowMixin } from './mixins'
import flowNode from './node'
import nodeMenu from './node_menu'
import FlowNodeForm from './node_form'
import lodash from 'lodash'
// import { getDataA } from './data_A'
import MesForm from '@/components/basic/MesForm.vue';
export default {
data() {
return {
formFields: {
WorkName: '',
WorkTable: '',
WorkTableName: '',
Weight:1,
AuditingEdit: 0,
Remark: ''
},
formRules: [
[
{
dataKey: '流程名称',
title: '流程名称',
field: 'WorkName',
required: true
}],
[{
dataKey: '',
title: '流程实例',
required: true,
field: 'WorkTable',
data: [],
readonly: false,
type: 'select',
onChange: (value, item) => {
this.formRules.forEach((options) => {
options.forEach((option) => {
if (option.field == 'WorkTable') {
this.formFields.WorkTableName = option.data.find((x) => {
return x.key == value;
}).value;
}
});
});
}
}],
[{
title: '权重(相同条件权重大优先)',
field: 'Weight',
type: "number",
}
],
[{
title: '审核中数据是否可以编辑',
field: 'AuditingEdit',
type: "switch",
data: [{ key: 0, value: "否" }, { key: 1, value: "是" }]
}
],
[{
title: '备注',
field: 'Remark'
}
]
],
// jsPlumb 实例
jsPlumb: null,
// 控制画布销毁
easyFlowVisible: true,
// 是否加载完毕标志位
loadEasyFlowFinish: false,
// 数据
data: {},
// 激活的元素、可能是节点、可能是连线
activeElement: {
// 可选值 node 、line
type: undefined,
// 节点ID
nodeId: undefined,
// 连线ID
sourceId: undefined,
targetId: undefined
},
zoom: 1
}
},
// 一些基础配置移动该文件中
mixins: [easyFlowMixin],
components: {
draggable, flowNode, nodeMenu, FlowNodeForm, MesForm
},
directives: {
'flowDrag': {
mounted(el, binding, vnode, oldNode) {
if (!binding) {
return
}
el.onmousedown = (e) => {
if (e.button == 2) {
// 右键不管
return
}
// 鼠标按下,计算当前原始距离可视区的高度
let disX = e.clientX
let disY = e.clientY
el.style.cursor = 'move'
document.onmousemove = function (e) {
// 移动时禁止默认事件
e.preventDefault()
const left = e.clientX - disX
disX = e.clientX
el.scrollLeft += -left
const top = e.clientY - disY
disY = e.clientY
el.scrollTop += -top
}
document.onmouseup = function (e) {
el.style.cursor = 'auto'
document.onmousemove = null
document.onmouseup = null
}
}
}
}
},
mounted() {
this.jsPlumb = jsPlumb.getInstance()
// this.$nextTick(() => {
// // 默认加载流程A的数据、在这里可以根据具体的业务返回符合流程数据格式的数据即可
// this.dataReload(getDataA())
// })
},
created() {
this.http.get('api/Sys_WorkFlow/getTableInfo').then((result) => {
this.formRules.forEach((options) => {
options.forEach((option) => {
if (option.field == 'WorkTable') {
option.data = result;
}
});
});
});
this.$store.getters.data().flowTable = this.formFields;
},
methods: {
// 返回唯一标识
getUUID() {
return Math.random().toString(36).substr(3, 10)
},
jsPlumbInit() {
this.jsPlumb.ready(() => {
// 导入默认配置
this.jsPlumb.importDefaults(this.jsplumbSetting)
// 会使整个jsPlumb立即重绘。
this.jsPlumb.setSuspendDrawing(false, true);
// 初始化节点
this.loadEasyFlow()
// 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
this.jsPlumb.bind('click', (conn, originalEvent) => {
this.activeElement.type = 'line'
this.activeElement.sourceId = conn.sourceId
this.activeElement.targetId = conn.targetId
this.$refs.nodeForm.lineInit({
from: conn.sourceId,
to: conn.targetId,
label: conn.getLabel()
})
this.deleteElement();
})
// 连线
this.jsPlumb.bind("connection", (evt) => {
let from = evt.source.id
let to = evt.target.id
if (this.loadEasyFlowFinish) {
this.data.lineList.push({ from: from, to: to })
}
})
// 删除连线回调
this.jsPlumb.bind("connectionDetached", (evt) => {
this.deleteLine(evt.sourceId, evt.targetId)
})
// 改变线的连接节点
this.jsPlumb.bind("connectionMoved", (evt) => {
this.changeLine(evt.originalSourceId, evt.originalTargetId)
})
// 连线右击
this.jsPlumb.bind("contextmenu", (evt) => {
console.log('contextmenu', evt)
})
// 连线
this.jsPlumb.bind("beforeDrop", (evt) => {
let from = evt.sourceId
let to = evt.targetId
if (from === to) {
this.$message.error('节点不支持连接自己')
return false
}
if (this.hasLine(from, to)) {
this.$message.error('该关系已存在,不允许重复创建')
return false
}
if (this.hashOppositeLine(from, to)) {
this.$message.error('不支持两个节点之间连线回环');
return false
}
this.$message.success('连接成功')
setTimeout(() => { this.setLineLabel(from, to, 'x') }, 50)
return true
})
// beforeDetach
this.jsPlumb.bind("beforeDetach", (evt) => {
console.log('beforeDetach', evt)
})
this.jsPlumb.setContainer(this.$refs.efContainer)
})
},
// 加载流程图
loadEasyFlow() {
// 初始化节点
for (var i = 0; i < this.data.nodeList.length; i++) {
let node = this.data.nodeList[i]
// 设置源点,可以拖出线连接其他节点
this.jsPlumb.makeSource(node.id, lodash.merge(this.jsplumbSourceOptions, {}))
// // 设置目标点,其他源点拖出的线可以连接该节点
this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
if (!node.viewOnly) {
this.jsPlumb.draggable(node.id, {
containment: 'parent',
stop: function (el) {
// 拖拽节点结束后的对调
console.log('拖拽结束: ', el)
}
})
}
}
// 初始化连线
for (var i = 0; i < this.data.lineList.length; i++) {
let line = this.data.lineList[i]
var connParam = {
source: line.from,
target: line.to,
label: line.label ? line.label : 'x',
connector: line.connector ? line.connector : '',
anchors: line.anchors ? line.anchors : undefined,
paintStyle: line.paintStyle ? line.paintStyle : undefined,
}
this.jsPlumb.connect(connParam, this.jsplumbConnectOptions)
}
this.$nextTick(function () {
this.loadEasyFlowFinish = true
})
},
// 设置连线条件
setLineLabel(from, to, label) {
var conn = this.jsPlumb.getConnections({
source: from,
target: to
})[0]
if (!label || label === '') {
conn.removeClass('flowLabel ')
conn.addClass('emptyFlowLabel')
} else {
conn.addClass('flowLabel')
}
conn.setLabel({
label: 'x' //label,
})
this.data.lineList.forEach(function (line) {
if (line.from == from && line.to == to) {
line.label = 'x'// label
}
})
},
// 删除激活的元素
deleteElement() {
if (this.activeElement.type === 'node') {
this.deleteNode(this.activeElement.nodeId)
} else if (this.activeElement.type === 'line') {
this.$confirm('确定删除所点击的线吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
var conn = this.jsPlumb.getConnections({
source: this.activeElement.sourceId,
target: this.activeElement.targetId
})[0]
this.jsPlumb.deleteConnection(conn)
}).catch(() => {
})
}
},
// 删除线
deleteLine(from, to) {
this.data.lineList = this.data.lineList.filter(function (line) {
if (line.from == from && line.to == to) {
return false
}
return true
})
},
// 改变连线
changeLine(oldFrom, oldTo) {
this.deleteLine(oldFrom, oldTo)
},
// 改变节点的位置
changeNodeSite(data) {
for (var i = 0; i < this.data.nodeList.length; i++) {
let node = this.data.nodeList[i]
if (node.id === data.nodeId) {
node.left = data.left
node.top = data.top
}
}
},
/**
* 拖拽结束后添加新的节点
* @param evt
* @param nodeMenu 被添加的节点对象
* @param mousePosition 鼠标拖拽结束的坐标
*/
addNode(evt, nodeMenu, mousePosition) {
if (nodeMenu.type == 'start' && this.data.nodeList.some(x => { return x.type == 'start' })) {
this.$message.error('【流程结束】节点已存在,只有选择一个流程开始节点');
return
}
if (nodeMenu.type == 'end' && this.data.nodeList.some(x => { return x.type == 'end' })) {
this.$message.error('【流程结束】节点已存在,只有选择一个流程开始节点');
return
}
var screenX = evt.originalEvent.clientX, screenY = evt.originalEvent.clientY
let efContainer = this.$refs.efContainer
var containerRect = efContainer.getBoundingClientRect()
var left = screenX, top = screenY
// 计算是否拖入到容器中
if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
this.$message.error("请把节点拖入到画布中")
return
}
left = left - containerRect.x + efContainer.scrollLeft
top = top - containerRect.y + efContainer.scrollTop
// 居中
left -= 85
top -= 16
var nodeId = this.getUUID()
// 动态生成名字
var origName = nodeMenu.name
var nodeName = origName
var index = 1
while (index < 10000) {
var repeat = false
for (var i = 0; i < this.data.nodeList.length; i++) {
let node = this.data.nodeList[i]
if (node.name === nodeName) {
nodeName = origName + index
repeat = true
}
}
if (repeat) {
index++
continue
}
break
}
var node = {
id: nodeId,
name: nodeName,
type: nodeMenu.type,
left: left + 'px',
top: top + 'px',
ico: nodeMenu.ico,
state: 'success'
}
/**
* 这里可以进行业务判断、是否能够添加该节点
*/
this.data.nodeList.push(node)
this.$nextTick(function () {
this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions)
this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions)
this.jsPlumb.draggable(nodeId, {
containment: 'parent',
stop: function (el) {
// 拖拽节点结束后的对调
console.log('拖拽结束: ', el)
}
})
})
},
/**
* 删除节点
* @param nodeId 被删除节点的ID
*/
deleteNode(nodeId) {
this.$confirm('确定要删除节点' + nodeId + '?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}).then(() => {
/**
* 这里需要进行业务判断,是否可以删除
*/
this.data.nodeList = this.data.nodeList.filter(function (node) {
if (node.id === nodeId) {
// 伪删除,将节点隐藏,否则会导致位置错位
// node.show = false
return false
}
return true
})
this.$nextTick(function () {
this.jsPlumb.removeAllEndpoints(nodeId);
})
}).catch(() => {
})
return true
},
clickNode(nodeId) {
this.activeElement.type = 'node'
this.activeElement.nodeId = nodeId
this.$refs.nodeForm.nodeInit(this.data, nodeId, this.formFields.WorkTable)
},
// 是否具有该线
hasLine(from, to) {
for (var i = 0; i < this.data.lineList.length; i++) {
var line = this.data.lineList[i]
if (line.from === from && line.to === to) {
return true
}
}
return false
},
// 是否含有相反的线
hashOppositeLine(from, to) {
return this.hasLine(to, from)
},
nodeRightMenu(nodeId, evt) {
this.menu.show = true
this.menu.curNodeId = nodeId
this.menu.left = evt.x + 'px'
this.menu.top = evt.y + 'px'
},
repaintEverything(node) {
let _node = this.data.nodeList.find((x) => {
return x.id == node.id;
});
Object.assign(_node, node);
console.log(_node);
this.jsPlumb.repaint();
},
// 加载流程图
dataReload(data, isAdd) {
this.easyFlowVisible = false
this.data.nodeList = []
this.data.lineList = []
this.$nextTick(() => {
data = lodash.cloneDeep(data)
this.easyFlowVisible = true
this.data = data
this.$nextTick(() => {
this.jsPlumb = jsPlumb.getInstance()
this.$nextTick(() => {
this.jsPlumbInit()
})
})
})
this.formRules.forEach(options => {
options.forEach(option => {
if (option.field == "WorkTable") {
option.readonly = !isAdd;
}
})
})
},
zoomAdd() {
if (this.zoom >= 1) {
return
}
this.zoom = this.zoom + 0.1
this.$refs.efContainer.style.zoom = this.zoom;
// this.jsPlumb.setZoom(this.zoom)
},
zoomSub() {
if (this.zoom <= 0) {
return
}
this.zoom = this.zoom - 0.1;
if (this.zoom < 0.3) {
this.zoom = 0.3;
}
this.$refs.efContainer.style.zoom = this.zoom;
// this.jsPlumb.setZoom(this.zoom)
}
}
}
</script>
<style scoped lang="less">
@import './index.css';
.flow-panel {
position: absolute;
height: 100%;
width: 100%;
}
.flow-panel ::v-deep(.el-form-item__label) {
margin-bottom: -2px !important;
text-align: left;
padding: 0 !important;
justify-content: flex-start;
}
.flow-panel ::v-deep(.el-form-item) {
display: flex;
flex-direction: column;
margin-bottom: 7px !important;
}
.ef-node-menu-form {
padding: 0px;
}
::-webkit-scrollbar {
width: 0px;
height: 0px;
}
::-webkit-scrollbar-thumb {
border-radius: 0px;
background: #e0e3e7;
height: 20px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
</style>

View File

@@ -0,0 +1,29 @@
// 是否具有该线
export function hasLine(data, from, to) {
for (let i = 0; i < data.lineList.length; i++) {
let line = data.lineList[i]
if (line.from === from && line.to === to) {
return true
}
}
return false
}
// 是否含有相反的线
export function hashOppositeLine(data, from, to) {
return hasLine(data, to, from)
}
// 获取连线
export function getConnector(jsp, from, to) {
let connection = jsp.getConnections({
source: from,
target: to
})[0]
return connection
}
// 获取唯一标识
export function uuid() {
return Math.random().toString(36).substr(3, 10)
}

View File

@@ -0,0 +1,73 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,88 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
this.buttons.splice(3,0,{ //也可以用push或者splice方法来修改buttons数组
name: '设计', //按钮名称
icon: 'el-icon-s-data', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
type: 'warning', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
onClick: function () {
let selectRow = this.$refs.table.getSelected();
if (selectRow.length == 0) {
return this.$error('请选择要设计的行!');
}
if (selectRow.length != 1) {
return this.$error('只能选择一行数据进行设计!');
}
window.open(this.http.ipAddress + 'BiManage/AppPage/DATABI/DSETEDIT.html?ID=' + selectRow[0].ID + "&token=" + this.$store.getters.getToken(),'_blank')
}
});
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,123 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: {
view: [], box:
[
{
name: "连接测试",
icon: 'el-icon-question',
value: 'Edit',
class: '',
type: 'success',
index: 1,//显示的位置
onClick: function () {
//this.editFormFields.ID = 0;
var param = {
P1: JSON.stringify(this.editFormFields),
Action: "DATABI_TESTBIDBSOURCE"
};
this.http.post("/api/Bll/ExeAction", param, "正在执行....").then((resultData) => {
if (resultData.ErrorMsg == "" && resultData.Result == '1') {
this.$Message.success('连接成功');
}
else {
this.$Message.error(resultData.ErrorMsg);
}
});
}
}], detail: []
}, //扩展的按钮
methods: {
getFormOption(field) {
let option;
this.editFormOptions.forEach(x => {
x.forEach(item => {
if (item.field == field) {
option = item;
}
})
})
return option;
},
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
var dbType = this.getFormOption('DBType');
this.boxOptions.labelWidth = 120;
dbType.onChange = (val, item) => {
if (val == "MYSQL") {
this.editFormFields["Port"] = 3306;
}
else {
this.editFormFields["Port"] = 1433;
}
};
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
this.boxOptions.height = 500;
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,103 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
this.buttons.splice(3,0,{ //也可以用push或者splice方法来修改buttons数组
name: '设计', //按钮名称
icon: 'el-icon-s-data', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
type: 'success', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
onClick: function () {
let selectRow = this.$refs.table.getSelected();
if (selectRow.length == 0) {
return this.$error('请选择要设计的行!');
}
if (selectRow.length != 1) {
return this.$error('只能选择一行数据进行设计!');
}
window.open(this.http.ipAddress + 'BiManage/AppPage/DATABI/YBPBuild.html?id=' + selectRow[0].ID + "&token=" + this.$store.getters.getToken(),'_blank')
}
});
this.buttons.splice(4,0,{ //也可以用push或者splice方法来修改buttons数组
name: '预览', //按钮名称
icon: 'el-icon-s-data', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
type: 'warning', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
onClick: function () {
let selectRow = this.$refs.table.getSelected();
if (selectRow.length == 0) {
return this.$error('请选择要预览的行!');
}
if (selectRow.length != 1) {
return this.$error('只能选择一行数据进行预览!');
}
window.open(this.http.ipAddress + 'BiManage/AppPage/DATABI/YBPVIEW.html?ID=' + selectRow[0].ID + "&token=" + this.$store.getters.getToken(),'_blank')
}
});
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,88 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
let column = this.columns.find((x) => {
return x.field == 'IsDefault';
});
column.edit = {
type: 'switch',
keep: true
};
//是否可用字段设置切换事件并保存到数据库
column.onChange = (value, row, tableData) => {
let url = `api/Bi_desktop/updateStatus?desktopId=${row.DesktopId}&statusFlag=${row.IsDefault}`;
this.http.get(url, {}, true).then((result) => {
this.search();
this.$Message.success(result);
});
};
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,174 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
import { h, resolveComponent, defineAsyncComponent } from 'vue';
import modelBody from "./calendar/Cal_PlanModelBody.vue"
import modelHeader from "./calendar/Cal_PlanModelHeader.vue"
import modelFooter from "./calendar/Cal_PlanModelFooter.vue"
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: modelHeader,
modelBody: modelBody,
modelFooter: modelFooter
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
getFormOption(field) {
let option;
this.editFormOptions.forEach(x => {
x.forEach(item => {
if (item.field == field) {
option = item;
}
})
})
return option;
},
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
this.boxOptions.height = 450;
this.height = this.height - 100;
//自定义弹出框的高与宽
this.boxOptions.height = document.body.clientHeight * 0.9;
this.boxOptions.width = document.body.clientWidth * 0.8;
var shiftType = this.getFormOption('ShiftType');
shiftType.onChange = (value, option) => {
if(value=="BB")
{
this.$refs.modelBody.$refs.table1.rowData = [
{PlanShiftName: "白班",StartTime: "08:00", EndTime: "18:00"}
]
}
else if(value=="LBD")
{
this.$refs.modelBody.$refs.table1.rowData = [
{PlanShiftName: "白班",StartTime: "08:00", EndTime: "20:00"},
{PlanShiftName: "夜班",StartTime: "20:00", EndTime: "08:00"}
]
}
else
{
this.$refs.modelBody.$refs.table1.rowData = [
{PlanShiftName: "白班",StartTime: "08:00", EndTime: "16:00"},
{PlanShiftName: "中班",StartTime: "16:00", EndTime: "24:00"},
{PlanShiftName: "夜班",StartTime: "00:00", EndTime: "08:00"}
]
}
};
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
this.setFormData(formData);
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
this.setFormData(formData);
return true;
},
setFormData(formData) { //新建或编辑时将从表1、2的数据提交到后台,见后台Equip_SpotMaintPlanService的新建方法
//后台从对象里直接取extra的值
let extra = {
table1List: this.$refs.modelBody.$refs.table1.rowData,//获取从表1的行数据
table2List: this.$refs.modelBody.$refs.table2.rowData//获取从表2的行数据
}
formData.extra = JSON.stringify(extra);
},
resetUpdateFormAfter() { //编辑弹出框时,点重置时,可自定义重置
console.log('resetUpdateFormAfter')
return true;
},
resetAddFormAfter() { //新建弹出框时,点重置时,可自定义重置
console.log('resetAddFormAfter')
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
if (this.currentAction == 'Add') {
this.editFormFields.Status = "1"
}
this.editFormFields.ChangeShiftType = "DAY"
var _button = this.boxButtons.find((x) => {
return x.value == 'save';
});
if(this.currentAction =="update" && row.Status == "2")
{
_button.disabled= true;
}
else
{
_button.disabled= false;
};
this.editFormOptions.forEach(item => {
item.forEach(x => {
//如果是编辑设置为只读
if (x.field == "PlanCode") {
x.placeholder = "请输入,忽略将自动生成";
}
if (this.currentAction =="update" && row.Status == "2" && (x.field == 'PlanName' || x.field == 'PlanCode' ||x.field == 'TeamType' ||x.field == 'Status' ||x.field == 'StartDate' ||x.field == 'EndDate' ||x.field == 'ShiftType' ||x.field == 'ChangeShiftType' ||x.field == 'Remark') ) {
x.readonly = true;
}
else
{
x.readonly = false;
}
if (x.field == "ChangeShiftType") {
x.readonly = true;
}
})
})
this.$nextTick(() => {
//这里没有给弹出框中的表格传参,如果需要参数可以通过 this.$emit("parentCall", 获取页面的参数
//具体见自定义页面Equip_SpotMaintPlanModelBody.vue中的modelOpen方法的使用 this.$emit("parentCall", ($this) => {
this.$refs.modelBody.modelOpen();
})
}
}
};
export default extension;

View File

@@ -0,0 +1,73 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,73 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,102 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
import modelHeader from "./calendar/UserModelBody.vue"
import gridFooter from './calendar/UserGridFooter.vue';
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: gridFooter,
//新建、编辑弹出框扩展组件
modelHeader: modelHeader,
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.single = true;
this.columnIndex = true;
//点击单元格编辑与结束编辑(默认是点击单元格编辑,鼠标离开结束编辑)
this.detailOptions.clickEdit = true;
this.tableMaxHeight = (document.body.clientHeight - 260) / 2;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
//明细表选择数据源操作
//获取明细表备注列,给备注列添加选择数据操作
this.detailOptions.buttons.unshift({
name: '选择用户', //按钮名称
icon: 'el-icon-plus', //按钮图标参照iview图标
hidden: false, //是否隐藏按钮(如果想要隐藏按钮在onInited方法中遍历buttons设置hidden=true)
onClick: function () {
//触发事件
this.$refs.modelHeader.open();
}
});
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
this.$nextTick(() => {
this.$refs.gridFooter.rowClick(result[0], "wareHouse");
});
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
//调用Doc_Order1GridFooter.vue文件中(订单明细)的查询
this.$refs.gridFooter.rowClick(row);
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
this.editFormOptions.forEach(item => {
item.forEach(x => {
//如果是编辑设置为只读
if (x.field == "TeamCode") {
x.placeholder = "请输入,忽略将自动生成";
}
})
})
}
}
};
export default extension;

View File

@@ -0,0 +1,73 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

View File

@@ -0,0 +1,73 @@
/*****************************************************************************************
** Author:COCO 2022
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() { //框架初始化配置前,
//示例:在按钮的最前面添加一个按钮
// this.buttons.unshift({ //也可以用push或者splice方法来修改buttons数组
// name: '按钮', //按钮名称
// icon: 'el-icon-document', //按钮图标vue2版本见iview文档iconvue3版本见element ui文档icon(注意不是element puls文档)
// type: 'primary', //按钮样式vue2版本见iview文档buttonvue3版本见element ui文档button
// onClick: function () {
// this.$Message.success('点击了按钮');
// }
// });
//示例:设置修改新建、编辑弹出框字段标签的长度
// this.boxOptions.labelWidth = 150;
//显示序号(默认隐藏)
this.columnIndex = true;
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false则不会执行查询
return true;
},
searchAfter(result) {
//查询后result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象包括明细表可以给给表单设置值自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
// this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看console.log(this.editFormOptions)
}
}
};
export default extension;

Some files were not shown because too many files have changed in this diff Show More