From 11d305bc30d3a12ed25ac55d3c6cce6f93162dda Mon Sep 17 00:00:00 2001 From: duyongjia <adu_555@163.com> Date: 星期三, 30 十月 2024 17:34:06 +0800 Subject: [PATCH] 1 --- 代码管理/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js | 58 代码管理/WCS/WIDESEAWCS_Client/src/router/redirect.js | 22 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue | 3 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js | 26 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue | 217 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenu.vue | 198 代码管理/WCS/WIDESEAWCS_Client/src/uitils/common.js | 344 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue | 152 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js | 102 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/RouterLoading.vue | 105 代码管理/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js | 13 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenuChild.vue | 56 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js | 15711 ++++++++++++++++++++++ 代码管理/WCS/WIDESEAWCS_Client/src/router/charts.js | 17 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue | 70 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.js | 151 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/methods.js | 1684 ++ 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/index.js | 7 代码管理/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue | 236 代码管理/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue | 265 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/builder/builderData.js | 195 代码管理/WCS/WIDESEAWCS_Client/src/views/index/index.less | 644 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js | 212 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/node.vue | 99 代码管理/WCS/WIDESEAWCS_Client/src/extension/quartzJob/extend/importDevicePro.vue | 116 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue | 3 代码管理/WCS/WIDESEAWCS_Client/src/components/redirect/Message.vue | 39 代码管理/WCS/WIDESEAWCS_Client/public/wcslogo.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/App.vue | 63 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/DownloadForm.js | 156 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue | 359 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/mixins.js | 157 代码管理/WCS/WIDESEAWCS_Client/src/views/system/test.vue | 18 代码管理/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js | 15 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js | 49 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less | 197 代码管理/WCS/WIDESEAWCS_Client/public/index.html | 135 代码管理/WCS/WIDESEAWCS_Client/src/views/Home.vue | 24 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue | 101 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js | 86 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/AuditHis.vue | 46 代码管理/WCS/WIDESEAWCS_Client/src/components/redirect/coding.vue | 25 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolHeader.vue | 67 代码管理/WCS/WIDESEAWCS_Client/src/extension/taskinfo/extend/taskExecuteDetail.vue | 259 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js | 61 代码管理/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/routerview.vue | 96 代码管理/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js | 58 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolTable.vue | 1873 ++ 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue | 118 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue | 325 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.vue | 806 + 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/Icons.vue | 345 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue | 386 代码管理/WCS/WIDESEAWCS_Client/src/assets/script/extend.js | 5 代码管理/WCS/WIDESEAWCS_Client/src/assets/element-icon/icon.css | 1 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/node_form.vue | 350 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js | 3 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js | 65 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/formTemplate.js | 664 代码管理/WCS/WIDESEAWCS_Client/src/api/useTest.js | 9 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/node_filter.vue | 201 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js | 551 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormDraggable.vue | 1159 + 代码管理/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js | 72 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue | 369 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/UploadExcel.vue | 221 代码管理/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff | 0 代码管理/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/addrouters.vue | 351 代码管理/WCS/WIDESEAWCS_Client/src/views/Index.vue | 716 + 代码管理/WCS/WIDESEAWCS_Client/src/components/redirect/404.vue | 21 代码管理/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue | 365 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/redirect/RedirectError.vue | 59 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue | 618 代码管理/WCS/WIDESEAWCS_Client/src/api/http.js | 324 代码管理/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js | 248 代码管理/WCS/WIDESEAWCS_Client/public/static/login_bg.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.js | 152 代码管理/WCS/WIDESEAWCS_Client/src/assets/script/common.js | 2 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue | 10 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js | 109 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/panel.vue | 612 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormPreview.vue | 206 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/node_menu.vue | 126 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico | 0 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/store/index.js | 90 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue | 65 代码管理/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue | 207 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolUpload.vue | 880 + 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue | 102 代码管理/WCS/WIDESEAWCS_Client/src/assets/css/common.less | 75 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/options.js | 226 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/props.js | 55 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolBox.vue | 200 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolForm.vue | 1487 ++ 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js | 22 代码管理/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js | 28 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue | 258 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/Login.vue | 386 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue | 72 代码管理/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue | 658 代码管理/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User/Sys_UserGridHeader.vue | 85 代码管理/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue | 317 代码管理/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.js | 78 代码管理/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue | 247 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/data_A.js | 54 代码管理/WCS/WIDESEAWCS_Client/src/api/permission.js | 53 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridAudit.vue | 427 代码管理/WCS/WIDESEAWCS_Client/src/components/redirect/401.vue | 19 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.less | 178 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/utils.js | 29 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.vue | 95 代码管理/WCS/WIDESEAWCS_Client/public/wms_d.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js | 148 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/templateCode.js | 95 代码管理/WCS/WIDESEAWCS_Client/src/assets/logo.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf | 0 代码管理/WCS/WIDESEAWCS_Client/src/components/editor/VolWangEditor.vue | 154 代码管理/WCS/WIDESEAWCS_Client/src/router/index.js | 86 代码管理/WCS/WIDESEAWCS_Client/src/router/viewGird.js | 62 代码管理/WCS/WIDESEAWCS_Client/src/main.js | 63 代码管理/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js | 82 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolTable/VolTableRender.js | 14 代码管理/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue | 13 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg | 0 代码管理/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png | 0 代码管理/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue | 229 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/force-directed.js | 182 代码管理/WCS/WIDESEAWCS_Client/src/components/workflow/index.css | 258 代码管理/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js | 58 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/VolForm/VolFormRender.js | 14 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/Audit.vue | 59 代码管理/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue | 71 代码管理/WCS/WIDESEAWCS_Client/src/components/basic/QuickSearch.vue | 152 代码管理/WCS/WIDESEAWCS_Client/src/views/index/Message.vue | 45 147 files changed, 42,337 insertions(+), 0 deletions(-) diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/index.html" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/index.html" new file mode 100644 index 0000000..548664e --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/index.html" @@ -0,0 +1,135 @@ +<!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="" /> + <link rel="icon" href="<%= BASE_URL %>wcslogo.png"> + <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 --> +</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: 0px 15px !important; + height: 32px; + } +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/static/login_bg.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/static/login_bg.png" new file mode 100644 index 0000000..925a5da --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/static/login_bg.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wcslogo.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wcslogo.png" new file mode 100644 index 0000000..2ef8e9b --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wcslogo.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wms_d.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wms_d.png" new file mode 100644 index 0000000..1e198e9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/public/wms_d.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/App.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/App.vue" new file mode 100644 index 0000000..1645abf --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/App.vue" @@ -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: "vol_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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/http.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/http.js" new file mode 100644 index 0000000..f73725b --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/http.js" @@ -0,0 +1,324 @@ +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://127.0.0.1:9291/'; +} +else if (process.env.NODE_ENV == 'debug') { + axios.defaults.baseURL = 'http://127.0.0.1:8098/'; +} + +else if (process.env.NODE_ENV == 'production') { + axios.defaults.baseURL = 'http://127.0.0.1:8098/'; +} +if (!axios.defaults.baseURL.endsWith('/')) { + axios.defaults.baseURL+="/"; +} + + +let ipAddress = axios.defaults.baseURL; +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) { + if (!localStorage.getItem('user')) { + Message.error({ + showClose: true, + message: '鐧婚檰宸茶繃鏈�', + type: 'error' + }); + } + 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("wideseawcs_exp") == "1") { + replaceToken(); + } + } + else if (res.headers.wideseawcs_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 : '姝e湪澶勭悊.....', + 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 => { + // console.log(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')) { // 闈濱E涓嬭浇 + 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() } }); +} +//鍔ㄦ�佸埛鏂皌oken +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 } diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/permission.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/permission.js" new file mode 100644 index 0000000..8dc7b02 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/permission.js" @@ -0,0 +1,53 @@ + +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鑷畾棰濆鎸夐挳 + //extra鑷畾棰濆鎸夐挳 + //table鑾峰彇鎸囧畾琛ㄧ殑鏉冮檺 + if (table) { + table = '/' + table; + } + let permission = store.getters.getPermission(table || path); + if (!permission) { + permission = store.getters.getPermission(path.substring(1)); + if (!permission) { + if ((tableName || '').indexOf('/') != -1) { + let arr = tableName.split('/'); + tableName = arr[arr.length - 1]; + } + permission = store.getters.getPermission('/' + tableName); + if (!permission) { + permission = (store.state.permission||[]).find(x => x.tableName ==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; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/useTest.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/useTest.js" new file mode 100644 index 0000000..d4d7be1 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/api/useTest.js" @@ -0,0 +1,9 @@ +const tipxx = { + install: function (vue) { + alert(1); + vue.prototype.$tip = function () { + alert('娴嬭瘯use') + }; + } +} +export default { tipxx } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/css/common.less" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/css/common.less" new file mode 100644 index 0000000..ac7a179 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/css/common.less" @@ -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; + } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf" new file mode 100644 index 0000000..91b74de --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff" new file mode 100644 index 0000000..02b9a25 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/icon.css" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/icon.css" new file mode 100644 index 0000000..bf3ae3e --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/element-icon/icon.css" @@ -0,0 +1 @@ +@font-face{font-family:element-icons;src:url(fonts/element-icons.woff) format("woff"),url(fonts/element-icons.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\e6a0"}.el-icon-ice-cream-square:before{content:"\e6a3"}.el-icon-lollipop:before{content:"\e6a4"}.el-icon-potato-strips:before{content:"\e6a5"}.el-icon-milk-tea:before{content:"\e6a6"}.el-icon-ice-drink:before{content:"\e6a7"}.el-icon-ice-tea:before{content:"\e6a9"}.el-icon-coffee:before{content:"\e6aa"}.el-icon-orange:before{content:"\e6ab"}.el-icon-pear:before{content:"\e6ac"}.el-icon-apple:before{content:"\e6ad"}.el-icon-cherry:before{content:"\e6ae"}.el-icon-watermelon:before{content:"\e6af"}.el-icon-grape:before{content:"\e6b0"}.el-icon-refrigerator:before{content:"\e6b1"}.el-icon-goblet-square-full:before{content:"\e6b2"}.el-icon-goblet-square:before{content:"\e6b3"}.el-icon-goblet-full:before{content:"\e6b4"}.el-icon-goblet:before{content:"\e6b5"}.el-icon-cold-drink:before{content:"\e6b6"}.el-icon-coffee-cup:before{content:"\e6b8"}.el-icon-water-cup:before{content:"\e6b9"}.el-icon-hot-water:before{content:"\e6ba"}.el-icon-ice-cream:before{content:"\e6bb"}.el-icon-dessert:before{content:"\e6bc"}.el-icon-sugar:before{content:"\e6bd"}.el-icon-tableware:before{content:"\e6be"}.el-icon-burger:before{content:"\e6bf"}.el-icon-knife-fork:before{content:"\e6c1"}.el-icon-fork-spoon:before{content:"\e6c2"}.el-icon-chicken:before{content:"\e6c3"}.el-icon-food:before{content:"\e6c4"}.el-icon-dish-1:before{content:"\e6c5"}.el-icon-dish:before{content:"\e6c6"}.el-icon-moon-night:before{content:"\e6ee"}.el-icon-moon:before{content:"\e6f0"}.el-icon-cloudy-and-sunny:before{content:"\e6f1"}.el-icon-partly-cloudy:before{content:"\e6f2"}.el-icon-cloudy:before{content:"\e6f3"}.el-icon-sunny:before{content:"\e6f6"}.el-icon-sunset:before{content:"\e6f7"}.el-icon-sunrise-1:before{content:"\e6f8"}.el-icon-sunrise:before{content:"\e6f9"}.el-icon-heavy-rain:before{content:"\e6fa"}.el-icon-lightning:before{content:"\e6fb"}.el-icon-light-rain:before{content:"\e6fc"}.el-icon-wind-power:before{content:"\e6fd"}.el-icon-baseball:before{content:"\e712"}.el-icon-soccer:before{content:"\e713"}.el-icon-football:before{content:"\e715"}.el-icon-basketball:before{content:"\e716"}.el-icon-ship:before{content:"\e73f"}.el-icon-truck:before{content:"\e740"}.el-icon-bicycle:before{content:"\e741"}.el-icon-mobile-phone:before{content:"\e6d3"}.el-icon-service:before{content:"\e6d4"}.el-icon-key:before{content:"\e6e2"}.el-icon-unlock:before{content:"\e6e4"}.el-icon-lock:before{content:"\e6e5"}.el-icon-watch:before{content:"\e6fe"}.el-icon-watch-1:before{content:"\e6ff"}.el-icon-timer:before{content:"\e702"}.el-icon-alarm-clock:before{content:"\e703"}.el-icon-map-location:before{content:"\e704"}.el-icon-delete-location:before{content:"\e705"}.el-icon-add-location:before{content:"\e706"}.el-icon-location-information:before{content:"\e707"}.el-icon-location-outline:before{content:"\e708"}.el-icon-location:before{content:"\e79e"}.el-icon-place:before{content:"\e709"}.el-icon-discover:before{content:"\e70a"}.el-icon-first-aid-kit:before{content:"\e70b"}.el-icon-trophy-1:before{content:"\e70c"}.el-icon-trophy:before{content:"\e70d"}.el-icon-medal:before{content:"\e70e"}.el-icon-medal-1:before{content:"\e70f"}.el-icon-stopwatch:before{content:"\e710"}.el-icon-mic:before{content:"\e711"}.el-icon-copy-document:before{content:"\e718"}.el-icon-full-screen:before{content:"\e719"}.el-icon-switch-button:before{content:"\e71b"}.el-icon-aim:before{content:"\e71c"}.el-icon-crop:before{content:"\e71d"}.el-icon-odometer:before{content:"\e71e"}.el-icon-time:before{content:"\e71f"}.el-icon-bangzhu:before{content:"\e724"}.el-icon-close-notification:before{content:"\e726"}.el-icon-microphone:before{content:"\e727"}.el-icon-turn-off-microphone:before{content:"\e728"}.el-icon-position:before{content:"\e729"}.el-icon-postcard:before{content:"\e72a"}.el-icon-message:before{content:"\e72b"}.el-icon-chat-line-square:before{content:"\e72d"}.el-icon-chat-dot-square:before{content:"\e72e"}.el-icon-chat-dot-round:before{content:"\e72f"}.el-icon-chat-square:before{content:"\e730"}.el-icon-chat-line-round:before{content:"\e731"}.el-icon-chat-round:before{content:"\e732"}.el-icon-set-up:before{content:"\e733"}.el-icon-turn-off:before{content:"\e734"}.el-icon-open:before{content:"\e735"}.el-icon-connection:before{content:"\e736"}.el-icon-link:before{content:"\e737"}.el-icon-cpu:before{content:"\e738"}.el-icon-thumb:before{content:"\e739"}.el-icon-female:before{content:"\e73a"}.el-icon-male:before{content:"\e73b"}.el-icon-guide:before{content:"\e73c"}.el-icon-news:before{content:"\e73e"}.el-icon-price-tag:before{content:"\e744"}.el-icon-discount:before{content:"\e745"}.el-icon-wallet:before{content:"\e747"}.el-icon-coin:before{content:"\e748"}.el-icon-money:before{content:"\e749"}.el-icon-bank-card:before{content:"\e74a"}.el-icon-box:before{content:"\e74b"}.el-icon-present:before{content:"\e74c"}.el-icon-sell:before{content:"\e6d5"}.el-icon-sold-out:before{content:"\e6d6"}.el-icon-shopping-bag-2:before{content:"\e74d"}.el-icon-shopping-bag-1:before{content:"\e74e"}.el-icon-shopping-cart-2:before{content:"\e74f"}.el-icon-shopping-cart-1:before{content:"\e750"}.el-icon-shopping-cart-full:before{content:"\e751"}.el-icon-smoking:before{content:"\e752"}.el-icon-no-smoking:before{content:"\e753"}.el-icon-house:before{content:"\e754"}.el-icon-table-lamp:before{content:"\e755"}.el-icon-school:before{content:"\e756"}.el-icon-office-building:before{content:"\e757"}.el-icon-toilet-paper:before{content:"\e758"}.el-icon-notebook-2:before{content:"\e759"}.el-icon-notebook-1:before{content:"\e75a"}.el-icon-files:before{content:"\e75b"}.el-icon-collection:before{content:"\e75c"}.el-icon-receiving:before{content:"\e75d"}.el-icon-suitcase-1:before{content:"\e760"}.el-icon-suitcase:before{content:"\e761"}.el-icon-film:before{content:"\e763"}.el-icon-collection-tag:before{content:"\e765"}.el-icon-data-analysis:before{content:"\e766"}.el-icon-pie-chart:before{content:"\e767"}.el-icon-data-board:before{content:"\e768"}.el-icon-data-line:before{content:"\e76d"}.el-icon-reading:before{content:"\e769"}.el-icon-magic-stick:before{content:"\e76a"}.el-icon-coordinate:before{content:"\e76b"}.el-icon-mouse:before{content:"\e76c"}.el-icon-brush:before{content:"\e76e"}.el-icon-headset:before{content:"\e76f"}.el-icon-umbrella:before{content:"\e770"}.el-icon-scissors:before{content:"\e771"}.el-icon-mobile:before{content:"\e773"}.el-icon-attract:before{content:"\e774"}.el-icon-monitor:before{content:"\e775"}.el-icon-search:before{content:"\e778"}.el-icon-takeaway-box:before{content:"\e77a"}.el-icon-paperclip:before{content:"\e77d"}.el-icon-printer:before{content:"\e77e"}.el-icon-document-add:before{content:"\e782"}.el-icon-document:before{content:"\e785"}.el-icon-document-checked:before{content:"\e786"}.el-icon-document-copy:before{content:"\e787"}.el-icon-document-delete:before{content:"\e788"}.el-icon-document-remove:before{content:"\e789"}.el-icon-tickets:before{content:"\e78b"}.el-icon-folder-checked:before{content:"\e77f"}.el-icon-folder-delete:before{content:"\e780"}.el-icon-folder-remove:before{content:"\e781"}.el-icon-folder-add:before{content:"\e783"}.el-icon-folder-opened:before{content:"\e784"}.el-icon-folder:before{content:"\e78a"}.el-icon-edit-outline:before{content:"\e764"}.el-icon-edit:before{content:"\e78c"}.el-icon-date:before{content:"\e78e"}.el-icon-c-scale-to-original:before{content:"\e7c6"}.el-icon-view:before{content:"\e6ce"}.el-icon-loading:before{content:"\e6cf"}.el-icon-rank:before{content:"\e6d1"}.el-icon-sort-down:before{content:"\e7c4"}.el-icon-sort-up:before{content:"\e7c5"}.el-icon-sort:before{content:"\e6d2"}.el-icon-finished:before{content:"\e6cd"}.el-icon-refresh-left:before{content:"\e6c7"}.el-icon-refresh-right:before{content:"\e6c8"}.el-icon-refresh:before{content:"\e6d0"}.el-icon-video-play:before{content:"\e7c0"}.el-icon-video-pause:before{content:"\e7c1"}.el-icon-d-arrow-right:before{content:"\e6dc"}.el-icon-d-arrow-left:before{content:"\e6dd"}.el-icon-arrow-up:before{content:"\e6e1"}.el-icon-arrow-down:before{content:"\e6df"}.el-icon-arrow-right:before{content:"\e6e0"}.el-icon-arrow-left:before{content:"\e6de"}.el-icon-top-right:before{content:"\e6e7"}.el-icon-top-left:before{content:"\e6e8"}.el-icon-top:before{content:"\e6e6"}.el-icon-bottom:before{content:"\e6eb"}.el-icon-right:before{content:"\e6e9"}.el-icon-back:before{content:"\e6ea"}.el-icon-bottom-right:before{content:"\e6ec"}.el-icon-bottom-left:before{content:"\e6ed"}.el-icon-caret-top:before{content:"\e78f"}.el-icon-caret-bottom:before{content:"\e790"}.el-icon-caret-right:before{content:"\e791"}.el-icon-caret-left:before{content:"\e792"}.el-icon-d-caret:before{content:"\e79a"}.el-icon-share:before{content:"\e793"}.el-icon-menu:before{content:"\e798"}.el-icon-s-grid:before{content:"\e7a6"}.el-icon-s-check:before{content:"\e7a7"}.el-icon-s-data:before{content:"\e7a8"}.el-icon-s-opportunity:before{content:"\e7aa"}.el-icon-s-custom:before{content:"\e7ab"}.el-icon-s-claim:before{content:"\e7ad"}.el-icon-s-finance:before{content:"\e7ae"}.el-icon-s-comment:before{content:"\e7af"}.el-icon-s-flag:before{content:"\e7b0"}.el-icon-s-marketing:before{content:"\e7b1"}.el-icon-s-shop:before{content:"\e7b4"}.el-icon-s-open:before{content:"\e7b5"}.el-icon-s-management:before{content:"\e7b6"}.el-icon-s-ticket:before{content:"\e7b7"}.el-icon-s-release:before{content:"\e7b8"}.el-icon-s-home:before{content:"\e7b9"}.el-icon-s-promotion:before{content:"\e7ba"}.el-icon-s-operation:before{content:"\e7bb"}.el-icon-s-unfold:before{content:"\e7bc"}.el-icon-s-fold:before{content:"\e7a9"}.el-icon-s-platform:before{content:"\e7bd"}.el-icon-s-order:before{content:"\e7be"}.el-icon-s-cooperation:before{content:"\e7bf"}.el-icon-bell:before{content:"\e725"}.el-icon-message-solid:before{content:"\e799"}.el-icon-video-camera:before{content:"\e772"}.el-icon-video-camera-solid:before{content:"\e796"}.el-icon-camera:before{content:"\e779"}.el-icon-camera-solid:before{content:"\e79b"}.el-icon-download:before{content:"\e77c"}.el-icon-upload2:before{content:"\e77b"}.el-icon-upload:before{content:"\e7c3"}.el-icon-picture-outline-round:before{content:"\e75f"}.el-icon-picture-outline:before{content:"\e75e"}.el-icon-picture:before{content:"\e79f"}.el-icon-close:before{content:"\e6db"}.el-icon-check:before{content:"\e6da"}.el-icon-plus:before{content:"\e6d9"}.el-icon-minus:before{content:"\e6d8"}.el-icon-help:before{content:"\e73d"}.el-icon-s-help:before{content:"\e7b3"}.el-icon-circle-close:before{content:"\e78d"}.el-icon-circle-check:before{content:"\e720"}.el-icon-circle-plus-outline:before{content:"\e723"}.el-icon-remove-outline:before{content:"\e722"}.el-icon-zoom-out:before{content:"\e776"}.el-icon-zoom-in:before{content:"\e777"}.el-icon-error:before{content:"\e79d"}.el-icon-success:before{content:"\e79c"}.el-icon-circle-plus:before{content:"\e7a0"}.el-icon-remove:before{content:"\e7a2"}.el-icon-info:before{content:"\e7a1"}.el-icon-question:before{content:"\e7a4"}.el-icon-warning-outline:before{content:"\e6c9"}.el-icon-warning:before{content:"\e7a3"}.el-icon-goods:before{content:"\e7c2"}.el-icon-s-goods:before{content:"\e7b2"}.el-icon-star-off:before{content:"\e717"}.el-icon-star-on:before{content:"\e797"}.el-icon-more-outline:before{content:"\e6cc"}.el-icon-more:before{content:"\e794"}.el-icon-phone-outline:before{content:"\e6cb"}.el-icon-phone:before{content:"\e795"}.el-icon-user:before{content:"\e6e3"}.el-icon-user-solid:before{content:"\e7a5"}.el-icon-setting:before{content:"\e6ca"}.el-icon-s-tools:before{content:"\e7ac"}.el-icon-delete:before{content:"\e6d7"}.el-icon-delete-solid:before{content:"\e7c9"}.el-icon-eleme:before{content:"\e7c7"}.el-icon-platform-eleme:before{content:"\e7ca"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}} \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png" new file mode 100644 index 0000000..d479f96 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png" new file mode 100644 index 0000000..d479f96 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico" new file mode 100644 index 0000000..9215c89 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg" new file mode 100644 index 0000000..e0b3351 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg" new file mode 100644 index 0000000..980d84c --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg" new file mode 100644 index 0000000..60e1e4c --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg" new file mode 100644 index 0000000..69a4fb9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg" new file mode 100644 index 0000000..324d403 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg" new file mode 100644 index 0000000..e7e56f7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg" new file mode 100644 index 0000000..00797c6 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png" new file mode 100644 index 0000000..8397330 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png" new file mode 100644 index 0000000..165d86a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png" new file mode 100644 index 0000000..bb7c9f9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png" new file mode 100644 index 0000000..1e198e9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png" new file mode 100644 index 0000000..a6a28ca --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/logo.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/logo.png" new file mode 100644 index 0000000..f3d2503 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/logo.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/common.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/common.js" new file mode 100644 index 0000000..8e8e8eb --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/common.js" @@ -0,0 +1,2 @@ +var test1 = function () { alert(11) } +export { test1 } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/extend.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/extend.js" new file mode 100644 index 0000000..d34da65 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/extend.js" @@ -0,0 +1,5 @@ +//瀵箆ue鍙傛暟杩涜鎵╁睍 +var extend = function (param) { + console.log(param) + } +export { extend } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js" new file mode 100644 index 0000000..67216df --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js" @@ -0,0 +1,15 @@ +//瀵箆ue鍙傛暟杩涜鎵╁睍 +var extend = function ($vueParam) { + $vueParam.methods.volBoxFrom = function () { + this.$Message.info("鎵╁睍js,澧炲姞寮瑰嚭娑堟伅"); + this.$refs.volBoxFrom.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 } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue" new file mode 100644 index 0000000..1ccf5c7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue" @@ -0,0 +1,10 @@ +<template> + <div style="text-align: center;font-size: 16px;padding: 20px;">姝e湪鍔犺浇璧勬簮...</div> +</template> +<script> +export default { + created() { + // console.log('loading') + } +}; +</script> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Audit.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Audit.vue" new file mode 100644 index 0000000..431d6c5 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Audit.vue" @@ -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;">澶� 娉細</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> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue" new file mode 100644 index 0000000..e5e6a79 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue" @@ -0,0 +1,3 @@ +<template> + <div></div> +</template> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue" new file mode 100644 index 0000000..3885ed5 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue" @@ -0,0 +1,3 @@ +<template> + <div id="test"></div> +</template> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Icons.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Icons.vue" new file mode 100644 index 0000000..afd4250 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/Icons.vue" @@ -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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/QuickSearch.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/QuickSearch.vue" new file mode 100644 index 0000000..185cbf2 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/QuickSearch.vue" @@ -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) { + //瑙乭ttps://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> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/RouterLoading.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/RouterLoading.vue" new file mode 100644 index 0000000..134a454 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/RouterLoading.vue" @@ -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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/UploadExcel.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/UploadExcel.vue" new file mode 100644 index 0000000..230fdad --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/UploadExcel.vue" @@ -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> +//鐩墠鍙敮鎸佸崟涓狤xcel涓婁紶锛屽叾浠栧姛鑳藉紑鍙戜腑... +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('娌℃湁閰嶇疆濂経rl'); + } + + 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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/AuditHis.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/AuditHis.vue" new file mode 100644 index 0000000..d1fcb48 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/AuditHis.vue" @@ -0,0 +1,46 @@ +<template> + <vol-table + :tableData="tableData" + :columns="columns" + :height="411" + :pagination-hide="true" + :load-key="false" + :text-inline="false" + :ck="false" + ></vol-table> +</template> +<script> +import VolTable from '@/components/basic/VolTable.vue'; +import { + defineComponent, + ref, + reactive, + toRefs, + getCurrentInstance +} from 'vue'; +export default defineComponent({ + components: { + VolTable + }, + 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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.less" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.less" new file mode 100644 index 0000000..adc13c3 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.less" @@ -0,0 +1,178 @@ +.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; + color: #292929; + } + button:hover{ + color: #0089f6; + } + } +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.vue" new file mode 100644 index 0000000..e4bbcd3 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.vue" @@ -0,0 +1,806 @@ +<template> + <div class="layout-container"> + <a :href="exportHref" ref="export"></a> + <!--寮�鍚噿鍔犺浇2020.12.06 --> + <vol-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> + </vol-box> + <ViewGridAudit @auditClick="saveAudit" :option="table" ref="audit"> + + </ViewGridAudit> + <!--寮�鍚噿鍔犺浇2020.12.06 --> + <!--瀹℃牳(寮傛鐐瑰嚮鎸夐挳鏃舵墠鍔犺浇寰呭畬)--> + <!-- <vol-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> + </vol-box> --> + + <!--瀵煎叆excel鍔熻兘--> + <!--2020.10.31娣诲姞瀵煎叆鍓嶇殑鏂规硶--> + <!--寮�鍚噿鍔犺浇2020.12.06 --> + <!-- 2022.01.08澧炲姞鏄庣粏琛ㄥ鍏ュ垽鏂� --> + <vol-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> + </vol-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鎷煎啓閿欒鍏煎澶勭悊 --> + <vol-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> + </vol-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> + + <!-- 鍒嗗壊浣嶇疆 --> + <vol-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" + @fullscreen="fullscreen" + > + <!--鏄庣粏澶撮儴鑷畾涔夌粍浠�--> + <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"> + <vol-form + ref="form" + :editor="editor" + :load-key="false" + :label-width="boxOptions.labelWidth" + :formRules="editFormOptions" + :formFields="editFormFields" + :select2Count="select2Count" + ></vol-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> + <vol-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" + :beginEdit="detailOptions.beginEdit" + :endEditBefore="detailOptions.endEditBefore" + :endEditAfter="detailOptions.endEditAfter" + :summary="detailOptions.summary" + :click-edit="detailOptions.clickEdit" + :double-edit="detailOptions.doubleEdit" + :column-index="detailOptions.columnIndex" + :ck="detailOptions.ck" + :text-inline="detailOptions.textInline" + :select2Count="select2Count" + :selectable="detailSelectable" + ></vol-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> + </vol-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 --> + <vol-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" + ></vol-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 VolTable from '@/components/basic/VolTable.vue'; +import VolForm from '@/components/basic/VolForm.vue'; +import { + defineAsyncComponent, + defineComponent, + ref, + shallowRef, + toRaw +} from 'vue'; +var vueParam = { + components: { + 'vol-form': VolForm, + 'vol-table': VolTable, + VolBox: defineAsyncComponent(() => import('@/components/basic/VolBox.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') + ), + 'vol-header': defineAsyncComponent(() => import('./../VolHeader.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: [], + //鏍戝舰缁撴瀯鐨勪富閿瓧娈碉紝濡傛灉璁剧疆鍊奸粯璁や細寮�鍚爲褰able锛涙敞鎰弐owKey瀛楁鐨勫�煎繀椤绘槸鍞竴锛�2021.05.02锛� + rowKey: undefined, + fiexdSearchForm: false, //2020.09.011鏄惁鍥哄畾鏌ヨ琛ㄥ崟锛宼rue鏌ヨ琛ㄥ崟灏嗗浐瀹氭樉绀哄湪琛ㄥ崟鐨勬渶涓婇潰 + _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: 8, //鐣岄潰鎸夐挳鏈�澶氭樉绀虹殑涓暟锛岃秴杩囩殑鏁伴噺閮芥樉绀哄湪鏇村涓� + buttons: [], //鏌ヨ鐣岄潰鎸夐挳 濡傞渶瑕佸叾浠栨搷浣滄寜閽紝鍙湪琛ㄥ搴旂殑.js涓坊鍔�(濡�:Sys_User.js涓璪uttons娣诲姞鍏朵粬鎸夐挳) + splitButtons: [], + uploadfiled: [], //涓婁紶鏂囦欢鍥剧墖鐨勫瓧娈� + boxButtons: [], //寮瑰嚭妗嗘寜閽� 濡傞渶瑕佸叾浠栨搷浣滄寜閽紝鍙湪琛ㄥ搴旂殑.js涓坊鍔� + dicKeys: [], //褰撳墠鐣岄潰鎵�鏈夌殑涓嬫媺妗嗗瓧鍏哥紪鍙峰強鏁版嵁婧� + hasKeyField: [], //鏈夊瓧鍏告暟鎹簮鐨勫瓧娈� + keyValueType: { _dinit: false }, + url: '', //鐣岄潰琛ㄦ煡璇㈢殑鏁版嵁婧愮殑url + hasDetail: false, //鏄惁鏈変粠琛�(鏄庣粏)琛ㄦ牸鏁版嵁 + initActivated: false, + load: true, //鏄惁榛樿鍔犺浇琛ㄦ暟鎹� + activatedLoad: false, //椤甸潰瑙﹀彂actived鏃舵槸鍚﹀埛鏂伴〉闈㈡暟鎹� + summary: false, //鏌ヨ鐣岄潰table鏄惁鏄剧ず鍚堣 + //闇�瑕佷粠杩滅▼缁戝畾鏁版嵁婧愮殑瀛楀吀缂栧彿,濡傛灉瀛楀吀鏁版嵁婧愮殑鏌ヨ缁撴灉杈冨锛岃鍦╫nInit涓皢瀛楀吀缂栧彿娣诲姞杩涙潵 + //鍙鑷畾sql鏈夋晥 + remoteKeys: [], + columnIndex: true, //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: '', //浠庤〃鍔犺浇鏁版嵁鐨剈rl + 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, //寮瑰嚭妗嗘槑缁唗able鏄惁鏄剧ず鍚堣 + draggable: false, //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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridAudit.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridAudit.vue" new file mode 100644 index 0000000..24cddc7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridAudit.vue" @@ -0,0 +1,427 @@ +<template> + <vol-box :footer="false" v-model="model" :height="height" :width="width" :padding="0" :lazy="true" title="瀹℃牳"> + + <div class="audit-model-content" :style="{ height: height - 100 + 'px' }"> + <el-descriptions class="desc-top" :column="3" size="default" :border="true"> + <el-descriptions-item v-for="(item, index) in formData" :key="index"> + <template #label> + <div class="cell-item"> + {{ item.name }} + </div> + </template> + {{ item.value }} + </el-descriptions-item> + </el-descriptions> + <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" :class="{'step-item-ad':item.auditId||item.stepAttrType=='start'}" 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'"> + <vol-table :tableData="tableData" :columns="columns" :height="height - 250" :pagination-hide="true" + :load-key="false" :text-inline="false" :ck="false"></vol-table> + </div> + </div> + </vol-box> +</template> +<script> +import VolTable from '@/components/basic/VolTable.vue'; +import VolBox from '@/components/basic/VolBox.vue'; +import http from '@/../src/api/http.js'; +import { defineComponent, ref, reactive, getCurrentInstance } from 'vue'; +export default defineComponent({ + components: { + VolTable, + VolBox + }, + props: { + option: { //鐢熸垚vue鏂囦欢鐨則able鍙傛暟 + 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 formData = reactive([]); + + 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 = (option) => { + const table = option.table; //props.option.url.replaceAll('/', ''); + const url = `api/Sys_WorkFlow/getSteps?tableName=${table}` + // let ids = currentRows.map(x => { return x[props.option.key] }); + let ids = currentRows.map(x => { return x[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.95; + 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) + formData.length = 0; + formData.push(...(result.form || [])) + }) + } + // + + const auditClick = () => { + if (auditParam.value == -1) { + proxy.$message.error('璇烽�夋嫨瀹℃壒椤�'); + return; + } + + if (!isFlow.value) { + emit("auditClick", auditParam, currentRows, (result) => { + if (result.status) { + model.value = false; + tableData.length = 0; + } + }); + return; + } + //鎴戠殑娴佺▼涓偣鍑诲鎵� + //淇濆瓨瀹℃牳 + let keys = currentRows.map(x => { return x[currentOption.key] }); + let url = `api/${currentOption.table}/audit?auditReason=${auditParam.reason}&auditStatus=${auditParam.value}` + http.post(url, keys, '瀹℃牳涓�....').then((x) => { + if (!x.status) { + proxy.$message.error(x.message); + return; + } + model.value = false; + proxy.$parent.search() + proxy.$message.success(x.message) + }); + } + const isFlow = ref(false); + let currentOption = {}; + const open = (rows, flow) => { + isFlow.value = !!flow; + currentRows = rows; + activeName.value = 'audit' + auditParam.reason = ''; + auditParam.value = -1; + + if (flow) { + currentOption = { + table: rows[0].WorkTable, + key: "WorkTableKey"// rows[0].WorkTableKey + } + } else { + currentOption = { + table: props.option.url.replaceAll('/', ''), + key: props.option.key + } + } + getAuditInfo(currentOption); + + } + + return { + columns, + height, + width, + model, + workFlowSteps, + getAuditInfo, + getAuditStatus, + activeName, + reactive, + tableData, + auditParam, + auditClick, + open, + isCurrentUser, + hasFlow, + rowLen, + formData, + isFlow + } + } +}); +</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; + } + } + +} + +.cell-item { + font-weight: 500; +} + +.desc-top { + padding: 5px 10px 0 10px; +} +.step-item-ad{ + *{ + color: #9f9898 !important; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.js" new file mode 100644 index 0000000..16a49e0 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.js" @@ -0,0 +1,151 @@ +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); + if (columns.some(x=>{return !this.viewColumns.some(c=> {return c.field==x.field})})|| + this.viewColumns.some(x=>{return !columns.some(c=> {return c.field==x.field})}) + ) { + localStorage.removeItem(this.getViewCacheKey()) + return; + } + 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); + } + } +}; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.vue" new file mode 100644 index 0000000..97feb80 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.vue" @@ -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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js" new file mode 100644 index 0000000..77d3907 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js" @@ -0,0 +1,102 @@ +//浠庤〃鏂规硶 +let detailMethods = { + //鏌ヨ浠庤〃鍓嶅厛鍋氬唴閮ㄥ鐞� + loadInternalDetailTableBefore(param, callBack) { + //鍔犺浇鏄庣粏琛ㄦ暟鎹箣鍓�,闇�瑕佽瀹氭煡璇㈢殑涓昏〃鐨処D + //姣忔鍙鍔犺浇鏄庣粏琛ㄦ牸鏁版嵁灏遍噸缃垹闄ゆ槑缁嗙殑鍊� + 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.$refs.detail.edit.rowIndex=-1; + 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); + } + }); + } + }, + detailSelectable(row, index){ + //鏄庣粏琛–heckBox 鏄惁鍙互鍕鹃�� + return true; + } +}; + +export default detailMethods; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/index.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/index.js" new file mode 100644 index 0000000..bd90205 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/index.js" @@ -0,0 +1,7 @@ +import Grid from './ViewGrid.vue' +const ViewGrid = { + install: function (app) { + app.component('ViewGrid', Grid) + } +} +export default ViewGrid \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/methods.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/methods.js" new file mode 100644 index 0000000..fa02eca --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/methods.js" @@ -0,0 +1,1684 @@ +import detailMethods from './detailMethods.js'; +//涓氬姟澶勭悊鏂规硶,鍏ㄩ儴鍙互鐢卞紑鍙戣鐩� +import serviceFilter from './serviceFilter.js'; +let methods = { + //褰撴坊鍔犳墿灞曠粍浠秅ridHeader/gridBody/gridFooter鍙婃槑缁唌odelHeader/modelBody/modelFooter鏃讹紝 + //濡傛灉瑕佽幏鍙栫埗绾ue瀵硅薄,璇蜂娇鐢ㄦ鏂规硶杩涜鍥炶皟 + parentCall(fun) { + if (typeof fun != 'function') { + return console.log('鎵╁睍缁勪欢闇�瑕佷紶鍏ヤ竴涓洖璋冩柟娉曟墠鑳借幏鍙栫埗绾ue瀵硅薄'); + } + fun(this); + }, + getCurrentAction() { + if (this.currentReadonly) { + return ''; + } + return '--' + (this.currentAction == this.const.ADD ? '鏂板' : '缂栬緫'); + }, + quickSearchKeyPress($event) { + //鏌ヨ瀛楁涓篿nput鏃讹紝鎸夊洖杞︽煡璇� + if ($event.keyCode == 13) { + if (this.searchFormFields[this.singleSearch.field] != '') { + this.search(); + } + } + }, + getButtons() { + //鐢熸垚ViewGrid鐣岄潰鐨勬搷浣滄寜閽強鏇村閫夐」 + let searchIndex = this.buttons.findIndex((x) => { + return x.value == 'Search'; + }); + //娣诲姞楂樼骇鏌ヨ + let hasOneFormItem = + this.searchFormOptions.length == 1 && + this.searchFormOptions[0].length == 1; + if (searchIndex != -1 && !hasOneFormItem) { + this.buttons.splice(searchIndex + 1, 0, { + icon: this.fiexdSearchForm ? 'el-icon-refresh-left' : 'el-icon-search', + name: this.fiexdSearchForm ? '閲嶇疆' : '楂樼骇鏌ヨ', + plain: true, + type: this.buttons[searchIndex].type, + onClick: () => { + if (this.fiexdSearchForm) { + return this.resetSearch(); + } + this.searchBoxShow = !this.searchBoxShow; + } + }); + } + if (hasOneFormItem) { + this.fiexdSearchForm = false; + } + this.maxBtnLength += searchIndex == -1 ? 0 : 1; + // if (this.buttons.length <= this.maxBtnLength) { + // return this.buttons; + // } + // let btns = this.buttons.slice(0, this.maxBtnLength); + // btns[this.maxBtnLength - 1].last = true; + // return btns; + }, + extendBtn(btns, source) { + //btns鏉冮檺鎸夐挳锛宻ource涓烘墿灞曟寜閽� + if (!btns || !(source && source instanceof Array)) { + return; + } + //source閫氳繃鍦ㄨ〃鐨勬墿灞昷s鏂囦欢涓璪uttons瀵瑰簲鎸夐挳鐨勫睘鎬ndex鍐冲畾鎸夐挳鎵�鏀句綅缃� + source.forEach((x) => { + //閫氳繃鎸夐挳鐨処ndex灞炴�э紝鏀惧埌鎸囧畾鐨勪綅缃� + btns.splice(x.index == undefined ? btns.length : x.index, 0, x); + }); + // if (this.extend.buttons.view) { + // this.extend.buttons.view.forEach((x) => { + // //閫氳繃鎸夐挳鐨処ndex灞炴�э紝鏀惧埌鎸囧畾鐨勪綅缃� + // this.buttons.splice(x.index == undefined ? this.buttons.length : x.index, 0, x); + // }) + // } + }, + initBoxButtons() { + //鍒濆鍖朧iewGird涓庡脊鍑烘/鏄庣粏琛ㄦ寜閽� + let path = this.$route.path; + //閫氳繃鑿滃崟鑾峰彇鐢ㄦ埛鎵�瀵瑰簲鑿滃崟闇�瑕佹樉绀虹殑鎸夐挳 + let permissionButtons = this.permission.getButtons( + path, + null, + this.extend.tableAction, + this.table.name + ); + if (permissionButtons) { + //2020.03.31娣诲姞娣辨嫹璐濇寜閽粍 + permissionButtons.forEach((p) => { + let _obj = {}; + for (const key in p) { + _obj[key] = p[key]; + } + this.buttons.push(_obj); + }); + // this.buttons.push(...permissionButtons); + } + if (!this.extend) { + this.extend = {}; + } + if (!this.extend.buttons) { + this.extend.buttons = {}; + } + //鏌ヨ鐣岄潰鎵╁睍鎸夐挳(鎵╁睍鎸夐挳鍙嚜琛岄�氳繃璁剧疆鎸夐挳鐨処ndex灞炴�ф樉绀哄埌鍏蜂綋浣嶇疆) + if (this.extend.buttons.view) { + this.extendBtn(this.buttons, this.extend.buttons.view); + } + + //寮瑰嚭妗嗘寜閽� + let boxButtons = []; + + let saveBtn = this.buttons.some((x) => { + if ( + x.value && + (x.value.toLowerCase() == this.const.ADD.toLowerCase() || + x.value.toLowerCase() == this.const.EDIT.toLowerCase()) + ) + return true; + }); + this.currentReadonly = !saveBtn; + //浠庤〃琛ㄦ牸鎿嶄綔鎸夐挳 + let detailGridButtons = { + name: '鍒锋柊', + type: 'info', + icon: 'el-icon-refresh', + onClick() { + //濡傛灉鏄庣粏琛ㄥ綋鍓嶇殑鐘舵�佷负鏂板缓鏃讹紝绂佹鍒锋柊 + if (this.currentAction == this.const.ADD) { + return; + } + this.refreshRow(); + } + }; + + let importExcel = this.buttons.some((x) => { + if (x.value == this.const.IMPORT) return true; + }); + //濡傛灉鏈夊鍏ユ潈闄�,鍒欓渶瑕佸垵濮嬪寲瀵煎叆缁勪欢 + if (importExcel) { + this.upload.url = this.getUrl(this.const.IMPORT); + //瀹氫箟涓嬭浇妯℃澘鐨勬枃浠跺悕 + this.upload.template.fileName = this.table.cnName; + //瀹氫箟涓嬭浇妯℃澘鐨刄rl璺緞 + this.upload.template.url = + this.http.ipAddress + this.getUrl(this.const.DOWNLOADTEMPLATE, true); + } + + // disabled + //濡傛灉褰撳墠瑙掕壊娌℃湁缂栬緫鎴栨柊寤哄姛鑳斤紝鏌ョ湅鏄庣粏鏃跺瓧娈佃缃叏閮ㄥ彧璇� + //鍙湁鏄庣粏琛紝灏嗘槑缁嗚〃涔熻缃负涓嶅彲鑳界紪杈戯紝骞朵笖涓嶆樉绀烘坊鍔犺銆佸垹闄よ + if (!saveBtn) { + this.editFormOptions.forEach((row) => { + row.forEach((x) => { + x.disabled = true; + }); + }); + //娌℃湁鏂板缂栬緫鏉冮檺鐨勶紝寮瑰嚭妗嗛兘璁剧疆涓哄彧璇� + this.detail.columns.forEach((column) => { + if (column.hasOwnProperty('edit')) { + column.readonly = true; + // row['edit'] = false; + } + }); + //寮瑰嚭妗嗘墿灞曟寜閽� + this.extendBtn(boxButtons, this.extend.buttons.box); + //寮瑰嚭寮规鎸夐挳(2020.04.21),娌℃湁缂栬緫鎴栨柊寤烘潈闄愭椂锛屼篃鍙互閫氳繃buttons灞炴�ф坊鍔犺嚜瀹氫箟寮瑰嚭妗嗘寜閽� + this.boxButtons.push(...boxButtons); + this.detailOptions.buttons.push(detailGridButtons); + this.detailOptions.buttons.forEach((button) => { + if (!button.hasOwnProperty('hidden')) { + button.hidden = false; + } + }); + //寮瑰嚭妗嗘墿灞曟槑缁嗚〃鎸夐挳 + this.extendBtn(this.detailOptions.buttons, this.extend.buttons.detail); + + return boxButtons; + } + + this.detailOptions.edit = true; + boxButtons.push( + ...[ + { + name: '淇� 瀛�', + icon: 'el-icon-check', + type: 'danger', + disabled: false, + value: 'save', + onClick() { + this.save(); + } + } + // { + // name: '閲� 缃�', + // icon: 'el-icon-refresh-right', + // type: 'primary', + // disabled: false, + // onClick() { + // this.resetEdit(); + // } + // } + ] + ); + //浠庤〃琛ㄦ牸鎿嶄綔鎸夐挳 + this.detailOptions.buttons.push( + ...[ + { + name: '娣诲姞琛�', + icon: 'el-icon-plus', + type: 'primary', + hidden: false, + plain: true, + onClick() { + this.addRow(); + } + }, + { + type: 'danger', + plain: true, + name: '鍒犻櫎琛�', + hidden: false, + icon: 'el-icon-delete', + onClick() { + this.delRow(); + } + }, + //2022.01.08澧炲姞鏄庣粏琛ㄥ鍏ュ鍑哄姛鑳� + //娉ㄦ剰闇�瑕侀噸鍐欏悗鍙版槑缁嗚〃鎺ュ彛鐨勫鍏ヤ笌涓嬭浇妯℃澘銆佸鍑虹殑鏉冮檺,Sys_DictionaryListController.cs/SellOrderListController.cs + { + type: 'danger', + plain: true, + name: '瀵煎叆', + value: 'import', + hidden: false, + icon: 'el-icon-upload2', + onClick() { + this.upload.url = `${this.http.ipAddress}api/${this.detail.table}/${this.const.IMPORT}?table=1`; + this.upload.template.url = `${this.http.ipAddress}api/${this.detail.table}/${this.const.DOWNLOADTEMPLATE}`; + //瀹氫箟涓嬭浇妯℃澘鐨勬枃浠跺悕 + this.upload.template.fileName = this.detail.cnName; + this.upload.excel = true; + } + }, + { + type: 'danger', + plain: true, + name: '瀵煎嚭', + value: 'export', + icon: 'el-icon-download', + hidden: false, + onClick() { + this.export(true); + } + } + ] + ); + this.detailOptions.buttons.forEach((button) => { + if (button.hasOwnProperty('hidden')) { + button.hidden = false; + } + }); + //寮瑰嚭妗嗘墿灞曟寜閽� + this.extendBtn(boxButtons, this.extend.buttons.box); + + //寮瑰嚭妗嗘墿灞曟槑缁嗚〃鎸夐挳 + this.detailOptions.buttons.push(detailGridButtons); + this.extendBtn(this.detailOptions.buttons, this.extend.buttons.detail); + + //寮瑰嚭寮规鎸夐挳 + this.boxButtons.push(...boxButtons); + }, + onClick(click) { + click.apply(this); + }, + changeDropdown(btnName, v1) { + let button = this.buttons.filter((x) => { + return x.name == btnName; + }); + if (button && button.length > 0) { + button[0].onClick.apply(this); + } + }, + emptyValue(value) { + if (typeof value == 'string' && value.trim() === '') { + return true; + } + if (value instanceof Array && !value.length) { + return true; + } + return value === null || value === undefined || value === ''; + }, + getSearchParameters() { + //鑾峰彇鏌ヨ鍙傛暟 + // 2020.09.11澧炲姞鍥哄畾鏌ヨ琛ㄥ崟,濡傛灉璁剧疆鍥哄畾浜嗘煡璇㈣〃鍗曪紝鐐瑰嚮鏌ヨ鏃讹紝涓嶅啀鍏抽棴 + if (!this.fiexdSearchForm) { + this.searchBoxShow = false; + } + + let query = { wheres: [] }; + for (const key in this.searchFormFields) { + let value = this.searchFormFields[key]; + if (this.emptyValue(value)) continue; + + if (typeof value == 'number') { + value = value + ''; + } + let displayType = this.getSearchItem(key); + + //鑱旂骇鍙繚鐣欓�変腑鑺傜偣鐨勬渶鍚庝竴涓�� + if (displayType == 'cascader') { + //鏌ヨ涓嬮潰鎵�鏈夌殑瀛愯妭鐐癸紝濡傦細閫変腑鐨勬槸鐖惰妭鐐癸紝搴旇鏌ヨ涓嬮潰鎵�鏈夌殑鑺傜偣鏁版嵁--寰呭畬 + value = value.length ? value[value.length - 1] + '' : ''; + } + //2021.05.02澧炲姞鍖洪棿鏌ヨ + if ( + typeof value == 'string' || + ['date', 'datetime', 'range'].indexOf(displayType) == -1 + ) { + query.wheres.push({ + name: key, + value: + typeof value == 'string' ? (value + '').trim() : value.join(','), + displayType: displayType + }); + continue; + } + for (let index = 0; index < value.length; index++) { + if (!this.emptyValue(value[index])) { + query.wheres.push({ + name: key, + value: (value[index] + '').trim(), + displayType: (() => { + if (['date', 'datetime', 'range'].indexOf(displayType) != -1) { + return index ? 'lessorequal' : 'thanorequal'; + } + return displayType; + })() + }); + } + } + } + return query; + }, + search() { + //鏌ヨ + // let query = this.getSearchParameters(); + // this.$refs.table.load(query, true); + this.$refs.table.load(null, true); + }, + loadTableBefore(param, callBack) { + //鏌ヨ鍓嶈缃煡璇㈡潯浠跺強鍒嗛〉淇℃伅 + let query = this.getSearchParameters(); + if (query) { + param = Object.assign(param, query); + } + + if (this.$route.query.viewflow && this.$route.query.id) { + param.wheres.push({ + name: this.table.key, + value: this.$route.query.id + }); + } + // if (this.isViewFlow() && data && data.length) { + // let query = JSON.parse(JSON.stringify(this.$route.query)); + // query.viewflow = 0; + // this.$router.replace({ path: this.$route.path, query: query }); + // this.$nextTick(() => { + // this.getWorkFlowSteps(data[0]); + // }); + // } + let status = this.searchBefore(param); + callBack(status); + }, + + loadTableAfter(data, callBack, result) { + //鏌ヨ鍚� + //2020.10.30澧炲姞鏌ヨ鍚庤繑鍥炴墍鏈夌殑鏌ヨ淇℃伅 + let status = this.searchAfter(data, result); + callBack(status); + //鑷姩寮瑰嚭妗嗗鎵硅鎯� + }, + loadDetailTableBefore(param, callBack) { + //鏄庣粏鏌ヨ鍓� + //鏂板缓鏃剁姝㈠姞杞芥槑缁� + if (this.currentAction == this.const.ADD) { + callBack(false); + return false; + } + let status = this.searchDetailBefore(param); + callBack(status); + }, + loadDetailTableAfter(data, callBack) { + //鏄庣粏鏌ヨ鍚� + let status = this.searchDetailAfter(data); + callBack(status); + }, + getSearchItem(field) { + //鑾峰彇鏌ヨ鐨勫弬鏁� + let data; + for (let index = 0; index < this.searchFormOptions.length; index++) { + if (data) return data.type; + const item = this.searchFormOptions[index]; + data = item.find((x) => { + return x.field == field; + }); + } + + return (data || {}).type; + }, + resetSearch() { + //閲嶇疆鏌ヨ瀵硅薄 + this.resetSearchForm(); + //2020.10.17澧炲姞閲嶇疆鍚庢柟娉� + this.resetSearchFormAfter && this.resetSearchFormAfter(); + }, + resetEdit() { + //閲嶇疆缂栬緫鐨勬暟鎹� + let isEdit = this.currentAction != this.const.ADD; + //閲嶇疆涔嬪墠 + if (!this[isEdit ? 'resetUpdateFormBefore' : 'resetAddFormBefore']()) { + return; + } + let objKey = {}; + //缂栬緫鐘舵�佷笅,涓嶉渶瑕侀噸缃富閿�,鍒涘缓鏃堕棿鍒涘缓浜� + if (isEdit) { + objKey[this.table.key] = this.editFormFields[this.table.key]; + } + this.resetEditForm(objKey); + //閲嶇疆涔嬪悗 + + if (!this[isEdit ? 'resetUpdateFormAfter' : 'resetAddFormAfter']()) { + return; + } + }, + resetSearchForm(sourceObj) { + //閲嶇疆鏌ヨ琛� + this.resetForm('searchForm', sourceObj); + }, + resetEditForm(sourceObj) { + if (this.hasDetail && this.$refs.detail) { + // this.$refs.detail.rowData.splice(0); + this.$refs.detail.reset(); + } + this.resetForm('form', sourceObj); + if (this.$refs.form && this.$refs.form.$refs.volform) { + setTimeout(() => { + this.$refs.form.$refs.volform.clearValidate(); + }, 100); + } + }, + getKeyValueType(formData, isEditForm) { + try { + let keyLeft = (isEditForm ? 'e' : 's') + '_b_'; + formData.forEach((item) => { + item.forEach((x) => { + if (this.keyValueType.hasOwnProperty(keyLeft + x.field)) { + return true; + } + let data; + if (x.type == 'switch') { + this.keyValueType[x.field] = 1; + } else if (x.bind && x.bind.data) { + data = x.bind.data; + } else if (x.data) { + if (x.data instanceof Array) { + data = x.data; + } else if (x.data.data && x.data.data instanceof Array) { + data = x.data.data; + } + } + if ( + data && + data.length > 0 && + !this.keyValueType.hasOwnProperty(x.field) + ) { + this.keyValueType[x.field] = data[0].key; + this.keyValueType[keyLeft + x.field] = x.type; + } + }); + }); + } catch (error) { + console.log(error.message); + } + }, + resetForm(formName, sourceObj) { + // return; + //閲嶇疆琛ㄥ崟鏁版嵁 + if (this.$refs[formName]) { + this.$refs[formName].reset(); + } + + if (!sourceObj) return; + let form, keyLeft; + if (formName == 'searchForm') { + form = this.searchFormFields; + keyLeft = 's' + '_b_'; + } else { + form = this.editFormFields; + keyLeft = 'e' + '_b_'; + } + //鑾峰彇鏁版嵁婧愮殑data绫诲瀷锛屽惁鍒欏鏋滄暟鎹簮data鐨刱ey鏄暟瀛楋紝閲嶇疆鐨勫�兼槸瀛楃涓插氨鏃犳硶缁戝畾鍊� + if (!this.keyValueType._dinit) { + this.getKeyValueType(this.editFormOptions, true); + this.getKeyValueType(this.searchFormOptions, false); + this.keyValueType._dinit = true; + } + var _cascaderParentTree; + for (const key in form) { + if (sourceObj.hasOwnProperty(key)) { + let newVal = sourceObj[key]; + let kv_type = this.keyValueType[keyLeft + key]; + + if ( + kv_type == 'selectList' || + kv_type == 'checkbox' || + kv_type == 'cascader' || + kv_type == 'treeSelect' + ) { + // 2020.05.31澧炲姞iview缁勪欢Cascader + // 2020.11.01澧炲姞iview缁勪欢Cascader琛ㄥ崟閲嶇疆鏃舵煡璇㈡墍鏈夌殑鐖惰妭鐐� + if (kv_type == 'cascader' || kv_type == 'treeSelect') { + var treeDic = this.dicKeys.find((dic) => { + return dic.fileds && dic.fileds.indexOf(key) != -1; + }); + + if (treeDic && treeDic.orginData && treeDic.orginData.length) { + let keyIsNum = typeof treeDic.orginData[0].id == 'number'; + + if (kv_type == 'cascader') { + newVal = keyIsNum ? newVal * 1 || 0 : newVal + ''; + if (kv_type == 'cascader') { + _cascaderParentTree = this.base.getTreeAllParent( + newVal, + treeDic.orginData + ); + if (_cascaderParentTree) { + newVal = _cascaderParentTree.map((x) => { + return x.id; + }); + } + } + } else { + if (newVal === null || newVal === undefined) { + newVal = []; + } else if (typeof newVal == 'string') { + newVal = newVal.split(','); + } + if (keyIsNum) { + if (Array.isArray(newVal)) { + newVal = newVal.map((x) => { + return x * 1 || 0; + }); + } + } else if (typeof newVal == 'number') { + newVal = [newVal + '']; + } + } + } else { + newVal = [newVal]; + } + } else if ( + newVal != '' && + newVal != undefined && + typeof newVal == 'string' + ) { + newVal = newVal.split(','); + } else if (kv_type == 'checkbox') { + newVal = []; + } + } else if ( + this.keyValueType.hasOwnProperty(key) && + typeof this.keyValueType[key] == 'number' && + newVal * 1 == newVal + ) { + newVal = newVal * 1; + } else { + if (newVal == null || newVal == undefined) { + newVal = ''; + } else if (this.numberFields.indexOf(key) != -1) { + newVal = newVal * 1 || 0; + } else { + newVal += ''; + } + } + if (newVal instanceof Array) { + if (form[key]) { + form[key] = []; + } + form[key] = newVal; + } else { + form[key] = newVal; + } + } else { + form[key] = form[key] instanceof Array ? [] : ''; + } + } + }, + onBtnClick(param) { + this[param.method](param.data); + }, + refresh() { + //鍒锋柊 + this.search(); + // this.$refs.table.load(); + }, + saveBefore(formData) { + return true; + }, + saveAfter(formData, result) { + return true; + }, + save() { + //鏂板鎴栫紪杈戞椂淇濆瓨 + // if (!this.$refs.form.validate()) return; + this.$refs.form.validate((result) => { + if (result) { + this.saveExecute(); + } + }); + }, + async saveExecute() { + let editFormFields = {}; + //涓婁紶鏂囦欢浠ラ�楀彿闅斿紑 + for (const key in this.editFormFields) { + if ( + this.uploadfiled && + this.uploadfiled.length > 0 && + this.uploadfiled.indexOf(key) != -1 && + this.editFormFields[key] instanceof Array + ) { + let allPath = this.editFormFields[key].map((x) => { + return x.path; + }); + editFormFields[key] = allPath.join(','); + } else if (typeof this.editFormFields[key] == 'function') { + try { + editFormFields[key] = this.editFormFields[key](); + } catch (error) { } + } else { + //2021.05.30淇涓嬫媺妗嗘竻闄ゆ暟鎹悗鍚庡彴涓嶈兘淇濆瓨鐨勯棶棰� + if ( + this.editFormFields[key] === undefined && + this.dicKeys.some((x) => { + return x.fileds && x.fileds.indexOf(key) != -1; + }) + ) { + editFormFields[key] = null; + } else { + editFormFields[key] = this.editFormFields[key]; + } + } + } + //灏嗘暟缁勮浆鎹㈡垚string + //2020.11.01澧炲姞绾ц仈澶勭悊 + for (const key in editFormFields) { + if (editFormFields[key] instanceof Array) { + var iscascader = this.dicKeys.some((x) => { + return ( + x.type == 'cascader' && x.fileds && x.fileds.indexOf(key) != -1 + ); + }); + if (iscascader && editFormFields[key].length) { + editFormFields[key] = + editFormFields[key][editFormFields[key].length - 1]; + } else { + editFormFields[key] = editFormFields[key].join(','); + } + } + } + + let formData = { + mainData: editFormFields, + detailData: null, + delKeys: null + }; + + //鑾峰彇鏄庣粏鏁版嵁(鍓嶅彴鏁版嵁鏄庣粏鏈仛鏍¢獙锛屽緟瀹�.鍚庡彴宸茬粡鏍¢獙) + if (this.hasDetail) { + formData.detailData = this.$refs.detail.rowData; + let _fields = this.detail.columns + .filter((c) => { + return ( + c.type == 'selectList' || (c.edit && c.edit.type == 'selectList') + ); + }) + .map((c) => { + return c.field; + }); + //2022.06.20澧炲姞淇濆瓨鏃跺鏄庣粏琛ㄤ笅鎷夋澶氶�夌殑鍒ゆ柇 + if (_fields.length) { + formData.detailData = JSON.parse(JSON.stringify(formData.detailData)); + formData.detailData.forEach((row) => { + for (let index = 0; index < _fields.length; index++) { + const _field = _fields[index]; + if (Array.isArray(row[_field])) { + row[_field] = row[_field].join(','); + } + } + }); + } + } + if (this.detailOptions.delKeys.length > 0) { + formData.delKeys = this.detailOptions.delKeys; + } + //淇濆瓨鍓嶆嫤鎴� + let _currentIsAdd = this.currentAction == this.const.ADD; + if (_currentIsAdd) { + //2020.12.06澧炲姞鏂板缓鍓嶅紓姝ュ鐞嗘柟娉� + //2021.08.16淇寮傛璇硶鍐欓敊鐨勯棶棰� + if (!this.addBefore(formData) || !(await this.addBeforeAsync(formData))) + return; + } else { + //2020.12.06澧炲姞淇敼鍓嶅紓姝ュ鐞嗘柟娉� + if ( + !this.updateBefore(formData) || + !(await this.updateBeforeAsync(formData)) + ) + return; + } + let url = this.getUrl(this.currentAction); + this.http.post(url, formData, true).then((x) => { + //淇濆瓨鍚� + if (_currentIsAdd) { + if (!this.addAfter(x)) return; + //杩炵画娣诲姞 + if (this.continueAdd && x.status) { + this.$success(x.message); + //鏂板缓 + this.currentAction = this.const.ADD; + this.currentRow = {}; + this.resetAdd(); + this.refresh(); + return; + } + } else { + if (!this.updateAfter(x)) return; + } + if (!x.status) return this.$error(x.message); + this.$success(x.message || '鎿嶄綔鎴愬姛'); + //濡傛灉淇濆瓨鎴愬姛鍚庨渶瑕佸叧闂紪杈戞锛岀洿鎺ヨ繑鍥炰笉澶勭悊鍚庨潰 + if (this.boxOptions.saveClose) { + this.boxModel = false; + //2020.12.27濡傛灉鏄紪杈戜繚瀛樺悗涓嶉噸缃垎椤甸〉鏁帮紝鍒锋柊椤甸潰鏃惰繕鏄樉绀哄綋鍓嶉〉鐨勬暟鎹� + this.$refs.table.load(null, _currentIsAdd); + //this.refresh(); + return; + } + let resultRow; + if (typeof x.data == 'string' && x.data != '') { + resultRow = JSON.parse(x.data); + } else { + resultRow = x.data; + } + + if (this.currentAction == this.const.ADD) { + // this.currentRow=x.data; + this.editFormFields[this.table.key] = ''; + this.currentAction = this.const.EDIT; + this.currentRow = resultRow.data; + } + this.resetEditForm(resultRow.data); + // console.log(resultRow); + if (this.hasDetail) { + this.detailOptions.delKeys = []; + if (resultRow.list) { + this.$refs.detail.rowData.push(...resultRow.list); + } + } + this.$refs.table.load(null, _currentIsAdd); + // this.refresh(); + }); + }, + del(rows) { + if (rows) { + if (!(rows instanceof Array)) { + rows = [rows]; + } + } else { + rows = this.$refs.table.getSelected(); + } + //鍒犻櫎鏁版嵁 + + if (!rows || rows.length == 0) return this.$error('璇烽�夋嫨瑕佸垹闄ょ殑琛�!'); + let delKeys = rows.map((x) => { + return x[this.table.key]; + }); + if (!delKeys || delKeys.length == 0) + return this.$error('娌℃湁鑾峰彇瑕佸垹闄ょ殑琛屾暟鎹�!'); + //鍒犻櫎鍓� + if (!this.delBefore(delKeys, rows)) { + return; + } + let tigger = false; + this.$confirm('纭瑕佸垹闄ら�夋嫨鐨勬暟鎹悧?', '璀﹀憡', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning', + center: true + }).then(() => { + if (tigger) return; + tigger = true; + let url = this.getUrl(this.const.DEL); + this.http.post(url, delKeys, '姝e湪鍒犻櫎鏁版嵁....').then((x) => { + if (!x.status) return this.$error(x.message); + this.$success("鍒犻櫎鎴愬姛"); + //鍒犻櫎鍚� + if (!this.delAfter(x)) { + return; + } + this.refresh(); + }); + }); + }, + async modelOpenBeforeAsync(row) { + return true; + }, + async initBox() { + //2022.01.08澧炲姞鏂板缓鏃堕殣钘忔槑缁嗚〃瀵煎嚭鍔熻兘 + this.detailOptions.buttons.forEach((x) => { + if (x.value == 'export') { + x.hidden = this.currentAction == 'Add'; + } + }); + //鍒濆鍖栨柊寤恒�佺紪杈戠殑寮瑰嚭妗� + if (!(await this.modelOpenBeforeAsync(this.currentRow))) return false; + this.modelOpenBefore(this.currentRow); + if (!this.boxInit) { + this.boxInit = true; + this.boxModel = true; + // this.detailUrl = this.url; + } + return true; + }, + setEditForm(row) { + // if (this.remoteColumns.length == 0 || !rows || rows.length == 0) return; + let remoteColumns = this.$refs.table.remoteColumns; + remoteColumns.forEach((column) => { + this.editFormOptions.forEach((option) => { + option.forEach((x) => { + if (x.field == column.field) { + x.data.data = Object.assign([], x.data, column.bind.data); + } + }); + }); + }); + this.editFormFields; + //閲嶇疆缂栬緫琛ㄥ崟鏁版嵁 + this.editFormFields[this.table.key] = row[this.table.key]; + + this.resetEditForm(row); + this.currentAction = this.const.EDIT; + this.boxModel = true; + }, + async linkData(row, column) { + this.boxOptions.title = this.table.cnName + '(缂栬緫)'; + //鐐瑰嚮table鍗曞厓鏍煎揩鎹烽摼鎺ユ樉绀虹紪杈戞暟鎹� + this.currentAction = this.const.EDIT; + this.currentRow = row; + if (!(await this.initBox())) return; + this.resetDetailTable(row); + this.setEditForm(row); + this.setContinueAdd(false); + //璁剧疆杩滅▼鏌ヨ琛ㄥ崟鐨勯粯璁ey/value + this.getRemoteFormDefaultKeyValue(); + //鐐瑰嚮缂栬緫鎸夐挳寮瑰嚭妗嗗悗锛屽彲浠ュ湪姝ゅ鍐欓�昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + this.modelOpenProcess(row); + }, + setContinueAdd(isAdd) { + if (!this.continueAdd) return; + var _button = this.boxButtons.find((x) => { + return x.value == 'save'; + }); + if (_button) { + _button.name = isAdd ? this.continueAddName : '淇� 瀛�'; + } + }, + resetAdd() { + if (this.hasDetail) { + this.$refs.detail && + // this.$refs.detail.rowData && + this.$refs.detail.reset(); + } + let obj = {}; + //濡傛灉鏈塻witch鏍囩锛岄粯璁ら兘璁剧疆涓哄惁 + this.editFormOptions.forEach((x) => { + x.forEach((item) => { + if (item.type == 'switch') { + obj[item.field] = 0; + } + }); + }); + this.resetEditForm(obj); + }, + async add() { + this.boxOptions.title = this.table.cnName + '(鏂板缓)'; + //鏂板缓 + this.currentAction = this.const.ADD; + this.currentRow = {}; + if (!(await this.initBox())) return; + + this.resetAdd(); + this.setContinueAdd(true); + // this.resetEditForm(); + this.boxModel = true; + //鐐瑰嚮鏂板缓鎸夐挳寮瑰嚭妗嗗悗锛屽彲浠ュ湪姝ゅ鍐欓�昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + this.modelOpenProcess(); + // this.modelOpenAfter(); + }, + async edit(rows) { + this.boxOptions.title = '缂栬緫'; + //缂栬緫 + this.currentAction = this.const.EDIT; + if (rows) { + if (!(rows instanceof Array)) { + rows = [rows]; + } + } else { + rows = this.$refs.table.getSelected(); + } + if (rows.length == 0) { + return this.$error('璇烽�夋嫨瑕佺紪杈戠殑琛�!'); + } + if (rows.length != 1) { + return this.$error('鍙兘閫夋嫨涓�琛屾暟鎹繘琛岀紪杈�!'); + } + //璁板綍褰撳墠缂栬緫鐨勮 + this.currentRow = rows[0]; + //鍒濆鍖栧脊鍑烘 + if (!(await this.initBox())) return; + this.setContinueAdd(false); + //閲嶇疆琛ㄥ崟 + this.resetDetailTable(); + + //璁剧疆褰撳墠鐨勬暟鎹埌琛ㄥ崟涓� + this.setEditForm(rows[0]); + //璁剧疆杩滅▼鏌ヨ琛ㄥ崟鐨勯粯璁ey/value + this.getRemoteFormDefaultKeyValue(); + //鐐瑰嚮缂栬緫鎸夐挳寮瑰嚭妗嗗悗锛屽彲浠ュ湪姝ゅ鍐欓�昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + this.modelOpenProcess(rows[0]); + // this.modelOpenAfter(rows[0]); + }, + getRemoteFormDefaultKeyValue() { + //璁剧疆琛ㄥ崟杩滅▼鏁版嵁婧愮殑榛樿key.value + if (this.currentAction != this.const.EDIT || this.remoteKeys.length == 0) + return; + this.editFormOptions.forEach((x, xIndex) => { + x.forEach((item, yIndex) => { + if (item.remote) { + let column = this.columns.find((x) => { + return x.bind && x.bind.key == item.dataKey; + }); + if (!column) return; + let key = this.currentRow[item.field]; + let obj = column.bind.data.find((x) => { + return x.key == key; + }); + // obj ? obj.value : key濡傛灉娌℃湁鏌ュ埌鏁版嵁婧愶紝鐩存帴浣跨敤鍘熸暟鎹� + item.data = [{ key: key, value: obj ? obj.value : key }]; + this.editFormOptions[xIndex].splice(yIndex, 1, item); + // this.$set(item, 'data', [{ key: key + '', value: obj.value }]) + // item.data = [{ key: key + '', value: obj.value }]; + } + }); + }); + }, + modelOpenProcess(row) { + this.$nextTick(() => { + this.modelOpenAfter(row); + }); + return; + // if (!this.$refs.form) { + // let timeOut = setTimeout(x => { + // this.modelOpenAfter(row); + // }, 500) + // return; + // } + // this.modelOpenAfter(row); + }, + import() { + //瀵煎叆(涓婁紶excel),寮瑰嚭瀵煎叆缁勪欢UploadExcel.vue + this.upload.excel = true; + this.$refs.upload_excel && this.$refs.upload_excel.reset(); + }, + download(url, fileName) { + //涓嬭浇瀵煎嚭鐨勬枃浠� + let xmlResquest = new XMLHttpRequest(); + xmlResquest.open('GET', url, true); + xmlResquest.setRequestHeader('Content-type', 'application/json'); + xmlResquest.setRequestHeader( + 'Authorization', + this.$store.getters.getToken() + ); + let elink = this.$refs.export; + xmlResquest.responseType = 'blob'; + xmlResquest.onload = function (oEvent) { + if (xmlResquest.status != 200) { + this.$error('涓嬭浇鏂囦欢鍑洪敊浜�..'); + return; + } + let content = xmlResquest.response; + // let elink = this.$refs.export;//document.createElement("a"); + elink.download = fileName; //+".xlsx"; + // elink.style.display = "none"; + let blob = new Blob([content]); + elink.href = URL.createObjectURL(blob); + // document.body.appendChild(elink); + elink.click(); + // document.body.removeChild(elink); + }; + xmlResquest.send(); + }, + getFileName(isDetail) { + //2021.01.08澧炲姞瀵煎嚭excel鏃惰嚜瀹氫箟鏂囦欢鍚� + if (isDetail) { + return this.detail.cnName + '.xlsx'; + } + return this.table.cnName + '.xlsx'; + }, + export(isDetail) { + //瀵煎嚭 + let url, query, param; + if (isDetail) { + //鏄庣粏琛ㄥ鍑烘椂濡傛灉鏄柊寤虹姸鎬侊紝绂佹瀵煎嚭 + if (this.currentAction == 'Add') { + return; + } + url = `api/${this.detail.table}/${this.const.EXPORT}`; + param = { + wheres: [ + { name: this.table.key, value: this.editFormFields[this.table.key] } + ] + }; + } else { + //涓昏〃瀵煎嚭 + url = this.getUrl(this.const.EXPORT); + query = this.getSearchParameters(); + param = { order: this.pagination.order, wheres: query.wheres || [] }; + } + //2020.06.25澧炲姞瀵煎嚭鍓嶅鐞� + if (!isDetail && !this.exportBefore(param)) { + return; + } + + if (param.wheres && typeof param.wheres == 'object') { + param.wheres = JSON.stringify(param.wheres); + } + let $http = this.http; + //2022.09.26澧炲姞鑷畾涔夊鍑烘枃浠跺悕 + let fileName = this.downloadFileName || this.getFileName(isDetail); + //2021.01.08浼樺寲瀵煎嚭鍔熻兘 + $http + .post(url, param, '姝e湪瀵煎嚭鏁版嵁....', { responseType: 'blob' }) + .then((content) => { + const blob = new Blob([content]); + if ('download' in document.createElement('a')) { + // 闈濱E涓嬭浇 + 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); + document.body.removeChild(elink); + } else { + // IE10+涓嬭浇 + navigator.msSaveBlob(blob, fileName); + } + }); + //.then(result => { + // if (!result.status) { + // return this.$error(result.message); + // } + // let path = this.getUrl(this.const.DOWNLOAD); + // path = path[0] == "/" ? path.substring(1) : path; + // this.download( + // $http.ipAddress + path + "?path=" + result.data, + // this.table.cnName + ".xlsx" // filePath + // ); + /// window.open($http.ipAddress + path + "?fileName=" + filePath, "_self"); + // }); + }, + getSelectRows() { + //鑾峰彇閫変腑鐨勮 + return this.$refs.table.getSelected(); + }, + getDetailSelectRows() { + //鎴栬幏鍙栨槑缁嗛�変腑鐨勮 + if (!this.$refs.detail) { + return []; + } + return this.$refs.detail.getSelected(); + }, + audit() { + //瀹℃牳寮瑰嚭妗� + let rows = this.$refs.table.getSelected(); + if (rows.length == 0) return this.$error('璇烽�夋嫨瑕佸鏍哥殑琛�!'); + let auditStatus = Object.keys(rows[0]).find(x => { return x.toLowerCase() === 'auditstatus' }); + if (!auditStatus) { + return this.$message.error(`琛ㄥ繀椤诲寘鎷鏍稿瓧娈点�怉uditStatus銆�,骞朵笖鏄痠nt绫诲瀷`) + } + // let checkStatus = rows.every((x) => { + // return this.$global.audit.status.some(c => { return c === x[auditStatus] || !x[auditStatus] }) + // }); + // if (!checkStatus) return this.$error('鍙兘閫夋嫨寰呭鎵规垨瀹℃牳涓殑鏁版嵁!'); + this.$refs.audit.open(rows); + }, + saveAudit(params, rows, callback) { + + //淇濆瓨瀹℃牳 + let keys = rows.map(x => { return x[this.table.key] }); + if (!this.auditBefore(keys, rows)) { + return; + } + let url = `${this.getUrl(this.const.AUDIT)}?auditReason=${params.reason}&auditStatus=${params.value}` + this.http.post(url, keys, '瀹℃牳涓�....').then((x) => { + if (!this.auditAfter(x, keys)) { + return; + } + if (!x.status) return this.$error(x.message); + + callback && callback(x); + this.$success(x.message); + this.refresh(); + }); + }, + viewModelCancel() { + //鏌ョ湅琛ㄧ粨鏋� + this.viewModel = false; + }, + initFormOptions(formOptions, keys, formFields, isEdit) { + //鍒濆鍖栨煡璇€�佺紪杈戝璞$殑涓嬫媺妗嗘暟鎹簮銆佸浘鐗囦笂浼犻摼鎺ュ湴鍧� + //let defaultOption = { key: "", value: "璇烽�夋嫨" }; + //鏈変笂浼犵殑瀛楁 + //2020.05.03鏂板 + //缂栬緫鏁版嵁婧愮殑绫诲瀷 + formOptions.forEach((item) => { + item.forEach((d) => { + if (d.type == 'number') { + //2022.08.22浼樺寲琛ㄥ崟绫诲瀷涓簄umber鏃剁殑榛樿鍊� + if (formFields[d.field] === '') { + formFields[d.field] = undefined; + } + this.numberFields.push(d.field); + } + if ( + d.type == 'img' || + d.type == 'excel' || + d.type == 'file' || + d.columnType == 'img' + ) { + d.url = this.http.ipAddress + 'api' + this.table.url + 'Upload'; + this.uploadfiled.push(d.field); + } + if (!d.dataKey) return true; + //2022.02.20寮哄埗寮�鍚仈绾у彲浠ラ�夋嫨鏌愪釜鑺傜偣 + if (d.type == 'cascader' && !d.hasOwnProperty('changeOnSelect')) { + //寮哄埗寮�鍚仈绾у彲浠ラ�夋嫨鏌愪釜鑺傜偣 + d.changeOnSelect = true; + } + //寮�鍚繙绋嬫悳绱� + if (d.remote) { + this.remoteKeys.push(d.dataKey); + d.data = []; //{ dicNo: d.dataKey, data: [] }; + return true; + } + //2020.05.03澧炲姞缂栬緫琛ㄥ崟瀵筩heckbox鐨勬敮鎸� + if (d.type == 'checkbox' && !(formFields[d.field] instanceof Array)) { + formFields[d.field] = []; + } + if (keys.indexOf(d.dataKey) == -1) { + //2020.05.03澧炲姞璁板綍缂栬緫瀛楁鐨勬暟鎹簮绫诲瀷 + + keys.push(d.dataKey); + //2020.05.03淇鏌ヨ琛ㄥ崟涓庣紪杈戣〃鍗晅ype绫诲瀷鍙樻垚寮轰竴鑷存�х殑闂 + //this.dicKeys.push({ dicNo: d.dataKey, data: [], type: d.type }); + // 2020.11.01澧炲姞iview缁勪欢Cascader鏁版嵁婧愬瓨鍌� + let _dic = { + dicNo: d.dataKey, + data: [], + fileds: [d.field], + orginData: [] + }; + if (d.type == 'cascader') { + _dic.type = 'cascader'; + } + if (isEdit) { + _dic['e_type'] = d.type; + } + this.dicKeys.push(_dic); + } else if (d.type == 'cascader') { + this.dicKeys.forEach((x) => { + if (x.dicNo == d.dataKey) { + x.type = 'cascader'; + x.fileds.push(d.field); + } + }); + } + if (d.type != 'cascader') { + //2020.01.30绉婚櫎鍐呴儴琛ㄥ崟formOptions鏁版嵁婧愰厤缃牸寮廳ata.data锛屾墍鏈夊弬鏁版敼涓轰笌缁勪欢api鏍煎紡鐩稿悓 + Object.assign( + d, + this.dicKeys.filter((f) => { + return f.dicNo == d.dataKey; + })[0], + { type: d.type } + ); + } + }); + }); + }, + //鍒濆table涓庢槑缁嗚〃鐨勬暟鎹簮鎸囧悜dicKeys瀵硅薄锛屽啀鍘诲悗鍙板姞杞芥暟鎹簮 + initColumns(scoure, dicKeys, keys) { + if (!scoure || !(scoure instanceof Array)) return; + scoure.forEach((item) => { + if (!item.bind || (item.bind.data && item.bind.data.length > 0)) + return true; + let key = item.bind.key || item.bind.dicNo; + if (this.remoteKeys.indexOf(key) != -1) { + item.bind.remote = true; + return true; + } + if (this.hasKeyField.indexOf(item.field) == -1) { + this.hasKeyField.push(item.field); + } + var dic = dicKeys.filter((x) => { + return x.dicNo == key; + }); + if (!dic || dic.length == 0) { + dicKeys.push({ dicNo: key, data: [] }); + dic = [dicKeys[dicKeys.length - 1]]; + keys.push(key); + } + //2020.11.01澧炲姞绾ц仈澶勭悊 + if (dic[0].type == 'cascader' || dic[0].type == 'treeSelect') { + item.bind = { data: dic[0].orginData, type: 'select', key: key }; + } else { + item.bind = dic[0]; + } + //2020.05.03浼樺寲table鏁版嵁婧恈heckbox涓巗elect绫诲瀷浠庣紪杈戝垪涓�夊彇 + item.bind.type = item.bind.e_type || 'string'; + }); + }, + bindOptions(dic) { + //缁戝畾涓嬫媺妗嗙殑鏁版嵁婧� + //缁戝畾鍚庡彴鐨勫瓧鍏告暟鎹� + dic.forEach((d) => { + if (d.data.length >= (this.select2Count || 500)) { + if ( + !this.dicKeys.some((x) => { + return ( + x.dicNo == d.dicNo && + (x.type == 'cascader' || x.type == 'treeSelect') + ); + }) + ) { + d.data.forEach((item) => { + item.label = item.value; + item.value = item.key; + }); + } + } + this.dicKeys.forEach((x) => { + if (x.dicNo != d.dicNo) return true; + //2020.10.26澧炲姞绾ц仈鏁版嵁婧愮粦瀹氬鐞� + if (x.type == 'cascader' || x.type == 'treeSelect') { + // x.data=d.data; + //鐢熸垚tree缁撴瀯 + let _data = JSON.parse(JSON.stringify(d.data)); + //2022.04.04澧炲姞绾ц仈瀛楀吀鏁版嵁婧愬埛鏂板悗table娌℃湁鍙樺寲鐨勯棶棰� + this.columns.forEach((column) => { + if (column.bind && column.bind.key == d.dicNo) { + column.bind.data = d.data; + } + }); + let arr = this.base.convertTree(_data, (node, data, isRoot) => { + if (!node.inited) { + node.inited = true; + node.label = node.value; + node.value = node.key; + } + }); + x.data.push(...arr); + x.orginData.push(...d.data); + //2021.10.17淇鏌ヨ绾ц仈涓嶈兘缁戝畾鏁版嵁婧愮殑闂 + this.searchFormOptions.forEach((searhcOption) => { + searhcOption.forEach((_option) => { + if (_option.type == 'cascader' && _option.dataKey == x.dicNo) { + _option.data = arr; + _option.orginData = d.data; + } + }); + }); + //2021.10.17淇绾ц仈涓嶈兘浜岀骇鍒锋柊鐨勯棶棰� + this.editFormOptions.forEach((editOption) => { + editOption.forEach((_option) => { + if ( + (_option.type == 'cascader' || _option.type == 'treeSelect') && + _option.dataKey == x.dicNo + ) { + _option.data = arr; + _option.orginData = d.data; + } + }); + }); + } else if (d.data.length > 0 && !d.data[0].hasOwnProperty('key')) { + let source = d.data, + newSource = new Array(source.length); + for (let index = 0; index < source.length; index++) { + newSource[index] = { + //榛樿浠庡瓧鍏告暟鎹鍑烘潵鐨刱ey閮芥槸string绫诲瀷,浣嗗鏋滄暟鎹粠sql涓煡璇㈢殑鍙兘涓洪潪string,鍚︽槸async-validator闇�瑕侀噸缃缃牸寮� + key: source['key'] + '', //source[index][x.config.valueField] + "", + value: source['value'] //source[index][x.config.textField] + }; + } + + x.data.push(...newSource); + } else { + //2020.06.06锛屽鏋滄槸selectList鏁版嵁婧愪娇鐢ㄧ殑鑷畾涔塻ql骞朵笖key鏄暟瀛楋紝寮哄埗杞崲鎴愬瓧绗︿覆 + if ( + x.e_type == 'selectList' && + d.data.length > 0 && + typeof d.data[0].key == 'number' + ) { + d.data.forEach((c) => { + c.key = c.key + ''; + }); + } + x.data.push(...d.data); + } + if ( + this.singleSearch && + this.singleSearch.dataKey && + this.singleSearch.dataKey == x.dicNo + ) { + this.singleSearch.data.splice(0, 1, ...x.data); + } + }); + }); + }, + getUrl(action, ingorPrefix) { + //鏄惁蹇界暐鍓嶇紑/ 鑾峰彇鎿嶄綔鐨剈rl + return (!ingorPrefix ? '/' : '') + 'api' + this.table.url + action; + }, + + initDicKeys() { + //鍒濆鍖栧瓧鍏告暟鎹� + let keys = []; + //2022.04.17浼樺寲閲嶆柊鍔犺浇鏁版嵁婧� + this.dicKeys.forEach((item) => { + item.data.splice(0); + item.orginData && item.orginData.splice(0); + }); + //this.dicKeys.splice(0); + //鍒濆鍖栫紪杈戞暟鎹簮,榛樿涓轰竴涓┖鏁扮粍锛屽鏋滆姹傚繀濉缃畉ype=number/decimal鐨勬渶灏忓�� + this.initFormOptions(this.editFormOptions, keys, this.editFormFields, true); + //鍒濆鍖栨煡璇㈡暟鎹簮,榛樿涓轰竴涓┖鏁扮粍 + this.initFormOptions( + this.searchFormOptions, + keys, + this.searchFormFields, + false + ); + //鏌ヨ鏃ユ湡璁剧疆涓哄彲閫夊紑濮嬩笌缁撴灉鏃ユ湡 + this.searchFormOptions.forEach((item) => { + item.forEach((x) => { + if (x.type == 'date' || x.type == 'datetime') x.range = true; + }); + }); + //鍒濆鍖杁atatable琛ㄦ暟鎹簮,榛樿涓轰竴涓┖鏁扮粍,dicKeys涓虹晫闈㈡墍鏈夌殑鏁版嵁瀛楀吀缂栧彿 + this.initColumns(this.columns, this.dicKeys, keys); + //2021.05.23榛樿寮�鍚煡璇㈤〉闈㈡墍鏈夊瓧娈垫帓搴�,濡傛灉涓嶉渶瑕佹帓搴忥紝鍦╫nInited閬嶅巻columns璁剧疆sort=false + //2021.09.25绉婚櫎寮哄埗鎺掑簭鍔熻兘 + // this.columns.forEach(x => { + // x.sort = x.render ? false : true; + // }) + if (this.detailOptions && this.detailOptions.columns) { + this.initColumns(this.detailOptions.columns, this.dicKeys, keys); + } + //鍒濆鍖栧揩閫熸煡璇㈠瓧娈�,榛樿浣跨敤浠g爜鐢熸垚鍣ㄩ厤缃殑绗竴涓煡璇㈠瓧娈� + if (this.searchFormOptions.length > 0) { + this.singleSearch = { + dataKey: this.searchFormOptions[0][0].dataKey, + dicNo: this.searchFormOptions[0][0].dicNo, + field: this.searchFormOptions[0][0].field, + title: this.searchFormOptions[0][0].title, + type: this.searchFormOptions[0][0].type, + data: [] + }; + // this.singleSearch = this.searchFormOptions[0][0]; + } + if (keys.length == 0) return; + let $this = this; + this.http.post('/api/Sys_Dictionary/GetVueDictionary', keys).then((dic) => { + + $this.bindOptions(dic); + //2022.04.04澧炲姞瀛楀吀鍔犺浇瀹屾垚鏂规硶 + $this.dicInited && $this.dicInited(dic); + }); + }, + setFiexdColumn(columns, containerWidth) { + //璁$畻鏁翠釜table鐨勫搴︼紝鏍规嵁瀹藉害鍐冲畾鏄惁鍚敤绗竴琛屾樉绀虹殑鍒椾负鍥哄畾鍒� + //2021.09.21绉婚櫎寮哄埗鍥哄畾绗竴鍒� + // let columnsWidth = 0; + // columns.forEach(x => { + // if (!x.hidden && x.width) { + // columnsWidth += x.width; + // } + // }); + // //鍚敤绗竴鍒椾负鍥哄畾鍒� + // if (columnsWidth > containerWidth) { + // let firstColumn = columns.find(x => !x.hidden); + // if (firstColumn) { + // firstColumn.fixed = true; + // } + // } + }, + initBoxHeightWidth() { + //鍒濆鍖栧脊鍑烘鐨勯珮搴︿笌瀹藉害 + let clientHeight = document.documentElement.clientHeight; + //寮瑰嚭妗嗛珮搴﹁嚦灏�250px + clientHeight = clientHeight < 250 ? 250 : clientHeight; + let clientWidth = document.documentElement.clientWidth; + if ( + this.editFormOptions.some((x) => { + return x.some((item) => { + return item.type == 'editor'; + }); + }) + ) { + this.editor.uploadImgUrl = this.getUrl('upload'); + this.boxOptions.height = clientHeight * 0.8; + this.boxOptions.width = clientWidth * 0.8; + } else { + if (this.boxOptions.height) { + //濡傛灉楂樺害涓庡搴﹁秴杩囦簡鑾峰彇鍒扮殑鍙楂樺搴︼紝鍒欒涓洪粯璁ょ殑90%楂樺 + if (this.boxOptions.height > clientHeight * 0.8) { + this.boxOptions.height = clientHeight * 0.8; + } + } + if (this.boxOptions.width) { + //濡傛灉楂樺害涓庡搴﹁秴杩囦簡鑾峰彇鍒扮殑鍙楂樺搴︼紝鍒欒涓洪粯璁ょ殑90%楂樺 + if (this.boxOptions.width > clientWidth * 0.8) { + this.boxOptions.width = clientWidth * 0.8; + } + } + } + //璁$畻鏁翠釜table鐨勫搴︼紝鏍规嵁瀹藉害鍐冲畾鏄惁鍚敤绗竴琛屾樉绀虹殑鍒椾负鍥哄畾鍒� + let maxTableWidth = clientWidth - 270; + this.setFiexdColumn(this.columns, maxTableWidth); + + this.height = this.tableHeight || clientHeight - 206; + this.url = this.getUrl(this.const.PAGE); + //璁$畻寮瑰嚭妗嗙殑楂樹笌瀹藉害 + //濡傛灉鏈夋槑缁嗚〃锛岄珮搴︿笌瀹藉甫璁剧疆涓�0.9/0.82 + if (this.detail.columns && this.detail.columns.length > 0) { + this.hasDetail = true; + clientWidth = clientWidth * 0.8; + clientHeight = clientHeight * 0.85; + if (!this.detailOptions.height) { + this.detailOptions.height = + clientHeight - this.editFormOptions.length * 36 - 234; + this.detailOptions.height = + this.detailOptions.height < 240 ? 240 : this.detailOptions.height; + } + + this.detailOptions.columns = this.detail.columns; + this.detailOptions.pagination.sortName = this.detail.sortName; + this.detailOptions.cnName = this.detail.cnName; + this.detailOptions.key = this.detail.key; + this.detailOptions.url = this.getUrl('getDetailPage'); + //璁$畻寮瑰嚭妗嗘暣涓猼able鐨勫搴︼紝鏍规嵁瀹藉害鍐冲畾鏄惁鍚敤绗竴琛屾樉绀虹殑鍒椾负鍥哄畾鍒� + this.setFiexdColumn(this.detail.columns, clientWidth); + } else { + let maxColumns = 1; //鏈�澶у垪鏁帮紝鏍规嵁鍒楄绠楀脊妗嗙殑瀹藉害 + this.editFormOptions.forEach((x) => { + if (x.length > maxColumns) maxColumns = x.length; + }); + let maxHeightRate = 0.7, + maxWidthRate = 0.5; + maxWidthRate = maxColumns / 10 + 0.3; + maxHeightRate = (this.editFormOptions.length || 1) * 0.1 + 0.03; + maxHeightRate = maxHeightRate > 0.9 ? 0.9 : maxHeightRate; + clientWidth = clientWidth * maxWidthRate; + clientHeight = clientHeight * maxHeightRate; + // this.boxOptions.width = clientWidth * maxWidthRate; + // this.boxOptions.height = clientHeight * maxHeightRate; + } + if (!this.boxOptions.height) { + this.boxOptions.height = clientHeight + 10; + } + if (!this.boxOptions.width) { + this.boxOptions.width = clientWidth + 30; + } + }, + rowOnChange(row) { + this.rowChange(row); + }, + rowChange(row) { + //閫変腑琛宑heckbox琛屼簨浠� + }, + rowOnClick({ row, column, event }) { + this.rowClick({ row, column, event }); + }, + rowClick({ row, column, event }) { + // 鐐瑰嚮琛屼簨浠�(2020.11.07) + }, + rowOnDbClick({ row, column, event }) { + this.rowDbClick({ row, column, event }); + }, + rowDbClick({ row, column, event }) { + // 鍙屽嚮鍑昏浜嬩欢(2021.05.23) + }, + $error(message) { + this.$message.error(message); + // this.$message({ + // type: 'error', + // content: message, + // duration: 5 + // }); + }, + $success(message) { + this.$message.success(message); + }, + setFiexdSearchForm(visiable) { + //2020.09.011澧炲姞鍥哄畾鏌ヨ琛ㄥ崟鍔熻兘,visiable=true榛樿灏嗘煡璇㈣〃鍗曞睍寮� + this.fiexdSearchForm = true; + let refreshBtn = this.buttons.find((x) => x.name == '鍒� 鏂�'); + if (visiable) { + this.searchBoxShow = true; + } + if (refreshBtn) { + refreshBtn.name = '閲� 缃�'; + refreshBtn.onClick = function () { + this.resetSearch(); + }; + } + }, + tableBeginEdit(row, column, index) { + //2021.03.19鏄惁寮�鍚煡璇㈢晫闈㈣〃鏍煎弻鍑荤紪杈戠粨鏉熸柟娉�,杩斿洖false涓嶄細缁撴潫缂栬緫 + return this.beginEdit(row, column, index); + }, + beginEdit(row, column, index) { + //2021.03.19鏄惁寮�鍚煡璇㈢晫闈㈣〃鏍煎弻鍑荤紪杈戠粨鏉熸柟娉�,杩斿洖false涓嶄細缁撴潫缂栬緫 + return true; + }, + tableEndEditBefore(row, column, index) { + return this.endEditBefore(row, column, index); + }, + endEditBefore(row, column, index) { + //2021.03.19鏄惁寮�鍚煡璇㈢晫闈㈣〃鏍煎弻鍑荤紪杈戠粨鏉熸柟娉�,杩斿洖false涓嶄細缁撴潫缂栬緫 + return true; + }, + filterPermission(tableName, permission) { + //2021.03.19鍒ゆ柇鏄惁鏈夋煇涓〃鐨勬寜閽潈闄� + //:["Search","Add","Delete","Update","Import","Export","Upload","Audit"] + const _result = (this.$store.state.permission || []).find((x) => { + return x.url == '/' + tableName; + }); + return _result && _result.permission.some((x) => x == permission); + }, + destroyed() { + //2021.04.11澧炲姞vue椤甸潰閿�姣佹柟娉�,璺敱蹇呴』璁剧疆keepLive:false锛岃缃柟娉曡锛氬墠绔紑鍙戞枃妗�-銆媅绂佺敤椤甸潰缂撳瓨keepAlive] + }, + loadTreeTableChildren(tree, treeNode, resolve) { + this.loadTreeChildren.call(this, tree, treeNode, resolve); + }, + loadTreeChildren(tree, treeNode, resolve) { + //鏍戝舰缁撴瀯鍔犺浇瀛愯妭鐐�(2021.05.02),鍦╫nInit涓缃簡rowKey涓婚敭瀛楁鍚庢墠浼氱敓鏁� + return resolve([]); + }, + importDetailAfter(data) { + //2022.01.08澧炲姞鏄庣粏琛ㄥ鍏ュ悗澶勭悊 + }, + importExcelAfter(data) { + //2022.01.08澧炲姞鏄庣粏琛ㄥ鍏ュ悗鏂规硶鍒ゆ柇 + + if (!data.status) { + return; // this.$message.error(data.message); + } + //鏄庣粏琛ㄥ鍏� + if (this.boxModel) { + if (data.data) { + data.data = JSON.parse(data.data); + } else { + data.data = []; + } + data.data.forEach((x) => { + x[this.detail.key] = undefined; + x[this.table.key] = undefined; + }); + this.importDetailAfter(data); //澧炲姞鏄庣粏琛ㄥ鍏ュ悗澶勭悊 + this.$refs.detail.rowData.unshift(...data.data); + this.upload.excel = false; + return; + } + this.importAfter(data); + }, + onGridModelClose(iconClick) { + if (this.isBoxAudit) { + this.initFormOptionType(false); + } + this.isBoxAudit = false; + this.onModelClose(iconClick); + }, + initAuditColumn() { + + }, + getWorkFlowSteps(row) { + let table = this.table.url.replaceAll('/', ''); + let url = `api/Sys_WorkFlow/getSteps?tableName=${table}&id=${row[this.table.key] + }`; + this.http.get(url, {}, true).then((result) => { + this.workFlowSteps.splice(0); + //鏈夊彲鑳芥病鏈夐厤缃鎵规祦绋� + if (!result.list || !result.list.length) { + result.list = []; + this.auditParam.showAction = true; + this.auditParam.height = 240; + this.auditParam.showViewButton = row.AuditStatus == 0; + } else { + this.auditParam.showAction = result.list.some((c) => { + return c.isCurrentUser; + }); + this.auditParam.height = 511; + this.auditParam.showViewButton = true; + } + this.auditParam.reason = ''; + this.auditParam.status = -1; + this.auditParam.value = -1; + if (result.his) { + result.his.forEach((item) => { + item.auditStatus = this.getAuditStatus(item.auditStatus); + }); + } + + this.auditParam.auditHis = result.his; + this.workFlowSteps.push(...result.list); + this.isBoxAudit = true; + this.initFormOptionType(true); + this.edit(row); + this.boxOptions.title = '瀹℃牳'; + }); + }, + initFormOptionType(isReadonly) { + this.editFormOptions.forEach((options) => { + options.forEach((option) => { + if (isReadonly) { + if (!option.readonly) { + this.formFieldsType.push(option.field); + option.readonly = true; + } + } else { + if (this.formFieldsType.indexOf(option.field) != -1) { + option.readonly = false; + } + } + }); + }); + }, + getAuditStatus(status) { + let data = this.auditParam.data.find((x) => { + return x.value == status; + }); + if (!data) { + return '-'; + // return `瀹℃牳鍊间笉姝g‘:${status}` + } + return data.text; + }, + initFlowQuery() { + if (this.$route.query.viewflow) { + this.$refs.table && this.search(); + } + }, + fullscreen(full) { //寮瑰嚭妗嗗叏灞忔柟娉� + + } +}; +import customColumns from './ViewGridCustomColumn.js'; +//鍚堝苟鎵╁睍鏂规硶 +methods = Object.assign(methods, detailMethods, serviceFilter, customColumns); +export default methods; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/props.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/props.js" new file mode 100644 index 0000000..5d0ebc6 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/props.js" @@ -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; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js" new file mode 100644 index 0000000..5d3b21f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js" @@ -0,0 +1,109 @@ + + +let serviceFilter = { + onInit () { //瀵瑰簲created + console.log('Create鎵ц鍓�') + }, + onInited () { //瀵瑰簲created锛屽湪onInit涓巓nInited涓棿浼氬垵濮嬪寲鐣岄潰鏁版嵁瀵硅薄 + 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) {//鏌ヨ浠庤〃鍚巔aram鏌ヨ鍙傛暟,result鍥炶繑鏌ヨ鐨勭粨鏋� + // console.log(this.detailOptions.cnName + '瑙﹀彂loadDetailTableAfter'); + return true; + }, + delBefore (ids, rows) { //鏌ヨ鐣岄潰鐨勮〃鍒犻櫎鍓� ids涓哄垹闄ょ殑id鏁扮粍,,rows鍒犻櫎鐨勮 + return true; + }, + delAfter (result) {//鏌ヨ鐣岄潰鐨勮〃鍒犻櫎鍚� + return true; + }, + delDetailRow (rows) { //寮瑰嚭妗嗗垹闄ゆ槑缁嗚〃鐨勮鏁版嵁(鍙槸瀵箃able鎿嶄綔锛屽苟娌℃湁鎿嶄綔鍚庡彴) + return true; + }, + addBefore (formData) { //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛� + return true; + }, + async addBeforeAsync (formData) { //寮傛澶勭悊,鍔熻兘鍚屼笂(2020.12.06) + return true; + }, + addAfter (result) {//鏂板缓淇濆瓨鍚巖esult杩斿洖鐨勭姸鎬佸強琛ㄥ崟瀵硅薄 + return true; + }, + updateBefore (formData) { //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + async updateBeforeAsync (formData) { //寮傛澶勭悊,鍔熻兘鍚屼笂(2020.12.06) + return true; + }, + updateAfter (result) {//缂栬緫淇濆瓨鍚巖esult杩斿洖鐨勭姸鎬佸強琛ㄥ崟瀵硅薄 + 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鍚庡埛鏂皌able琛ㄦ牸鏁版嵁 + 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涓虹偣鍑诲乏涓笂瑙扻瑙﹀彂鐨勫叧闂簨浠� + //濡傛灉杩斿洖 false涓嶄細鍏抽棴寮瑰嚭妗� + //return false; + this.boxModel=false; + }, + selectable(row, index){ + //琛–heckBox 鏄惁鍙互鍕鹃�� + return true; + } +} +export default serviceFilter; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolBox.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolBox.vue" new file mode 100644 index 0000000..8893643 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolBox.vue" @@ -0,0 +1,200 @@ +<template> + <div class="vol-dialog"> + + <el-dialog v-model="vmodel" :close-on-click-modal="false" :close-on-press-escape="false" :width="width" + :fullscreen="fullscreen" :draggable="draggable" :modal="modal" :before-close="handleClose"> + <template #header> + <i :class="icon"></i> {{ title }} + <button class="el-dialog__headerbtn" type="button" style="right: 35px; color: var(--el-color-info)" @click="handleFullScreen"> + <i class="el-icon el-icon-full-screen"></i> + </button> + </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(); + } + ); + const fullscreen=ref(false); + const handleFullScreen=()=> { + fullscreen.value = !fullscreen.value; + context.emit("fullscreen", fullscreen.value); + } + return { + handleClose, + inited, + vmodel, + footer, + top, + calcHeight, + contentHeight, + fullscreen, + handleFullScreen + }; + } +}); +</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; + width: 0; + padding-right: 30px; + padding-left: 5px; +} +// .vol-dialog ::v-deep(.el-dialog__headerbtn .el-dialog__close) { +// color: #fff; +// } +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenu.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenu.vue" new file mode 100644 index 0000000..66a9779 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenu.vue" @@ -0,0 +1,198 @@ +<template> + <div class="vol-el-menu"> + <el-menu + close="vol-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> {{ item.name }}</span> + </template> + <vol-element-menu-child + :enable="enable" + :list="item.children" + ></vol-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> {{ item.name }}</span> + </el-menu-item> + </template> + </template> + </el-menu> + </div> +</template> + +<script> +import VolElementMenuChild from './VolElementMenuChild'; +import { useRouter } from 'vue-router'; + +import { + defineComponent, + reactive, + watch, + ref, + toRef, + toRefs, + getCurrentInstance + // onMounted, +} from 'vue'; +export default defineComponent({ + components: { + 'vol-element-menu-child': VolElementMenuChild + }, + 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> +.vol-el-menu { + box-sizing: content-box; + width: 100%; + .menu-icon { + font-size: 18px; + margin-right: 6px; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenuChild.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenuChild.vue" new file mode 100644 index 0000000..ccea321 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenuChild.vue" @@ -0,0 +1,56 @@ +<template> + <div class="vol-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> {{ 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> {{ item.name }}</span> + </template> + <vol-element-menu-child :enable="enable" :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> {{item.name }}</span> + </el-menu-item> + </template> + </template> + </div> +</template> + +<script> +export default { + name: "vol-element-menu-child", + props: { + list: { + type: Array, + default: [], + }, + enable: { + type: Boolean, + default: false, //鏄惁鍒ゆ柇enable=1 + }, + }, +}; +</script> + +<style scoped lang="less"> +.vol-el-menu-item ::v-deep(.el-menu-item) { + height: 42px !important; + line-height: 42px !important; +} +</style> + + + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm.vue" new file mode 100644 index 0000000..3e6b9b1 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm.vue" @@ -0,0 +1,1487 @@ +<template> + <el-form + style="display: inline-block; width: 100%" + :inline="true" + ref="volform" + @submit.prevent + :model="formFields" + :label-width="labelWidth" + :rules="rules" + > + <template v-for="(row, findex) in formRules" :key="findex"> + <div class="vol-form-item"> + <el-form-item + :label="item.title ? item.title + '锛�' : ''" + v-show="!item.hidden" + v-for="(item, index) in row" + :prop="item.field" + :key="item.field + index" + :style="{ width: getColWidth(item) + '%' }" + > + <!-- render --> + <form-expand + v-if="item.render && typeof item.render == 'function'" + :render="item.render" + :par="12" + ></form-expand> + <!-- 2021.10.17澧炲姞琛ㄥ崟瀹炴椂鏂规硶璁$畻 --> + <span + v-else-if=" + item.readonly && typeof formFields[item.field] == 'function' + " + >{{ formFields[item.field]() }}</span + > + <!-- 鍙鍥剧墖鎴栨枃浠� --> + <div v-else-if="isReadonlyImgFile(item, formFields)"> + <div v-if="item.type == 'img'" class="form-imgs"> + <div + class="img-item" + v-for="(img, imgIndex) in formFields[item.field]" + :key="imgIndex" + > + <img + :src="getSrc(img.path)" + :onerror="errorImg" + @click="previewImg(img.path)" + /> + </div> + </div> + <div + v-else + class="form-file-list" + v-for="(file, fileIndex) in formFields[item.field]" + :key="fileIndex" + > + <a @click="dowloadFile(formFields[item.field][fileIndex])">{{ + file.name + }}</a> + </div> + </div> + + <div v-else :class="{ 'form-item-extra': item.extra }"> + <!-- 鍙灞炴�� --> + <label + :style="item.inputStyle" + v-if="item.type == 'label'" + class="readonly-input" + >{{ getText(formFields, item) }}</label + > + <!-- 20223.05.13闆嗘垚el-tree-select --> + <!-- :filter-method="(value)=>{filterMethod(value,item.data)}" --> + <!-- :filterable="true" --> + <el-tree-select + style="width: 100%" + v-else-if="item.type == 'treeSelect'" + v-model="formFields[item.field]" + :data="item.data" + :multiple="item.multiple" + :render-after-expand="false" + :show-checkbox="false" + :check-strictly="true" + check-on-click-node + node-key="key" + :props="{ label: 'label' }" + > + <template #default="{data,node }"> + <!-- <el-checkbox v-model="node.checked"></el-checkbox> --> + <!-- {{getNode(node, data)}} --> + <!-- {{node.checked}} --> + <!-- 杩欓噷杩樻湁鐐归棶棰橈紝鍚庨潰澶勭悊 --> + {{ data.label}}</template + > + </el-tree-select> + <template + v-else-if="['select', 'selectList'].indexOf(item.type) != -1" + > + <el-select-v2 + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + style="width: 100%" + :size="size" + v-if="item.data.length > select2Count" + v-model="formFields[item.field]" + filterable + :multiple="item.type == 'select' ? false : true" + :placeholder="item.placeholder ? item.placeholder : item.title" + :allow-create="item.autocomplete" + :options="item.data" + @change=" + (val) => { + item.onChange(val, item.data); + } + " + clearable + > + <template #default="{ item }"> + {{ item.label }} + </template> + </el-select-v2> + <el-select + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + style="width: 100%" + :size="size" + v-else-if="item.remote || item.url" + v-model="formFields[item.field]" + filterable + :multiple="item.type == 'select' ? false : true" + :placeholder="item.placeholder ? item.placeholder : item.title" + clearable + :remote-method=" + (val) => { + remoteSearch(item, formFields, val); + } + " + > + <el-option + v-for="item in item.data" + :key="item.key" + :label="item.value" + :value="item.key" + > + </el-option> + </el-select> + <el-select + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + style="width: 100%" + :size="size" + v-else + v-model="formFields[item.field]" + filterable + :multiple="item.type == 'select' ? false : true" + :placeholder="item.placeholder ? item.placeholder : item.title" + :allow-create="item.autocomplete" + @change=" + (val) => { + item.onChange(val, item.data); + } + " + clearable + > + <el-option + v-show="!item.hidden" + :disabled="item.disabled" + v-for="item in item.data" + :key="item.key" + :label="item.value" + :value="item.key" + > + </el-option> + </el-select> + </template> + + <el-switch + v-show="!item.hidden" + v-else-if="item.type == 'switch'" + v-model="formFields[item.field]" + :disabled="item.readonly || item.disabled" + active-color="#0f84ff" + @change="item.onChange" + inactive-color="rgb(194 194 194)" + :active-value=" + typeof formFields[item.field] == 'boolean' + ? true + : typeof formFields[item.field] == 'string' + ? '1' + : 1 + " + :inactive-value=" + typeof formFields[item.field] == 'boolean' + ? false + : typeof formFields[item.field] == 'string' + ? '0' + : 0 + " + > + </el-switch> + + <el-radio-group + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + v-model="formFields[item.field]" + v-else-if="item.type == 'radio'" + @change="item.onChange" + > + <el-radio + v-for="kv in item.data" + :disabled="item.readonly || item.disabled" + :key="kv.key" + :label="kv.key" + >{{ kv.value }}</el-radio + > + </el-radio-group> + + <el-checkbox-group + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + v-model="formFields[item.field]" + v-else-if="item.type == 'checkbox'" + @change="item.onChange" + > + <el-checkbox + v-for="kv in item.data" + :key="kv.key" + :disabled="item.readonly || item.disabled" + :label="kv.key" + >{{ kv.value }}</el-checkbox + > + </el-checkbox-group> + <div + class="v-date-range" + style="display: flex" + v-else-if=" + ['date', 'datetime'].indexOf(item.type) != -1 && item.range + " + > + <el-date-picker + :size="size" + :disabled="item.readonly || item.disabled" + style="flex: 1; width: auto" + v-model="formFields[item.field][0]" + :type="item.type == 'date' ? 'date' : 'datetime'" + :disabledDate="(val) => getDateOptions(val, item)" + placeholder="寮�濮嬫椂闂�" + @change=" + (val) => { + dateRangeChange(val, item); + } + " + :value-format="getDateFormat(item)" + > + </el-date-picker> + <span style="margin: 0px 5px; font-size: 13px; color: #6f6b6b" + >鑷�</span + > + <el-date-picker + :size="size" + :disabled="item.readonly || item.disabled" + style="flex: 1; width: auto" + v-model="formFields[item.field][1]" + placeholder="缁撴潫鏃堕棿" + :type="item.type == 'date' ? 'date' : 'datetime'" + :disabledDate="(val) => getDateOptions(val, item)" + @change=" + (val) => { + dateRangeChange(val, item); + } + " + :value-format="getDateFormat(item)" + > + </el-date-picker> + </div> + <!-- v-show涓嶆坊鍔犳牴鑺傜偣灏变細鎶ラ敊娌℃湁鏍圭偣鑺� --> + <div + v-show="!item.hidden" + style="width: 100%" + v-else-if="['date', 'datetime', 'month'].indexOf(item.type) != -1" + > + <el-date-picker + :size="size" + clearable + :disabled="item.readonly || item.disabled" + style="width: 100%" + v-model="formFields[item.field]" + @change="item.onChange" + :type="item.type" + :placeholder=" + item.placeholder ? item.placeholder : '璇烽�夋嫨' + item.title + " + :disabledDate="(val) => getDateOptions(val, item)" + :value-format="getDateFormat(item)" + > + </el-date-picker> + </div> + + <el-time-picker + :size="size" + v-else-if="item.type == 'time'" + v-model="formFields[item.field]" + :disabled="item.readonly || item.disabled" + placeholder="璇烽�夋嫨鏃堕棿" + :value-format="item.format || 'HH:mm:ss'" + :format="item.format" + style="width: 100%" + > + </el-time-picker> + + <el-scrollbar + style="border: 1px solid #c7d8db; border-radius: 5px" + :height="item.height || 150" + v-else-if=" + item.type == 'editor' && (item.readonly || item.disabled) + " + > + <div ref="editor" v-html="formFields[item.field]"></div> + </el-scrollbar> + + <vol-wang-editor + ref="editor" + v-else-if="item.type == 'editor'" + :url="item.url || editor.uploadImgUrl" + :upload="item.upload || editor.upload" + v-model="formFields[item.field]" + :height="item.height || 350" + ></vol-wang-editor> + + <vol-upload + v-show="!item.hidden" + v-else-if="isFile(item, formFields)" + :desc="item.desc" + :multiple="item.multiple" + :max-file="item.maxFile" + :max-size="item.maxSize" + :autoUpload="item.autoUpload" + :fileInfo="formFields[item.field]" + :url="item.url" + :img="item.type == 'img' || item.columnType == 'img'" + :excel="item.type == 'excel'" + :fileTypes="item.fileTypes ? item.fileTypes : []" + :upload-before="item.uploadBefore" + :upload-after="item.uploadAfter" + :append="item.multiple" + :on-change=" + (files) => { + return fileOnChange(files, item); + } + " + :file-click="item.fileClick" + :remove-before="item.removeBefore" + :downLoad="item.downLoad ? true : false" + ></vol-upload> + <el-cascader + :size="size" + clearable + style="width: 100%; margin-top: -3px" + v-model="formFields[item.field]" + :disabled="item.readonly || item.disabled" + v-else-if="item.type == 'cascader'" + :options="item.data" + :props="{ + checkStrictly: item.changeOnSelect || item.checkStrictly + }" + @change="item.onChange" + > + </el-cascader> + <el-rate + v-else-if="item.type == 'rate'" + @change=" + (val) => { + item.onChange && item.onChange(val); + } + " + :max="item.max" + v-model="formFields[item.field]" + /> + <div + style="display: flex" + v-else-if="item.type == 'range' || item.range" + > + <el-input + :size="size" + :disabled="item.readonly || item.disabled" + style="flex: 1" + v-model="formFields[item.field][0]" + clearable + /> + <span style="margin: 0 5px">-</span> + <el-input + :size="size" + :disabled="item.readonly || item.disabled" + style="flex: 1" + v-model="formFields[item.field][1]" + clearable + /> + </div> + <el-input + :size="size" + clearable + :ref="item.field" + :input-style="item.inputStyle" + :disabled="item.readonly || item.disabled" + v-else-if="item.type == 'textarea'" + v-model="formFields[item.field]" + type="textarea" + :autosize="{ + minRows: item.minRows || 2, + maxRows: item.maxRows || 10 + }" + :placeholder="item.placeholder ? item.placeholder : item.title" + /> + <el-input-number + :size="size" + style="width: 100%" + :ref="item.field" + :input-style="item.inputStyle" + v-else-if="item.type == 'number'" + v-model="formFields[item.field]" + :min="item.min" + :disabled="item.readonly || item.disabled" + :max="item.max" + controls-position="right" + /> + <el-input + :size="size" + clearable + :input-style="item.inputStyle" + v-else-if="item.type == 'password'" + type="password" + v-model="formFields[item.field]" + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + :placeholder="item.placeholder ? item.placeholder : item.title" + /> + <!-- 2021.11.18淇el-input娌℃湁榛樿enter浜嬩欢鏃跺洖杞﹀紓甯� --> + <el-input + :size="size" + clearable + :ref="item.field" + :input-style="item.inputStyle" + v-else-if="item.onKeyPress" + :placeholder="item.placeholder ? item.placeholder : item.title" + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + v-model="formFields[item.field]" + @keypress=" + ($event) => { + onKeyPress($event, item); + } + " + @change="item.onKeyPress" + @keyup.enter="item.onKeyPress" + ></el-input> + <el-input + :size="size" + clearable + v-else + :ref="item.field" + :input-style="item.inputStyle" + :placeholder="item.placeholder ? item.placeholder : item.title" + :disabled="item.readonly || item.disabled" + v-show="!item.hidden" + v-model="formFields[item.field]" + ></el-input> + + <div class="form-extra" v-if="item.extra"> + <form-expand + v-if="item.extra.render" + :render="item.extra.render" + ></form-expand> + <a + v-else-if="item.extra.click" + :style="item.extra.style" + @click="item.extra.click(item, formFields[item.field])" + > + <i v-if="item.extra.icon" :class="item.extra.icon" /> + {{ item.extra.text }} + </a> + <a v-else :style="item.extra.style"> + <i v-if="item.extra.icon" :class="item.extra.icon" /> + {{ item.extra.text }} + </a> + </div> + </div> + </el-form-item> + </div> + </template> + <slot></slot> + <div style="width: 100%"> + <slot name="footer"></slot> + </div> + </el-form> +</template> +<script> +const rule = { + change: [ + 'checkbox', + 'select', + 'date', + 'datetime', + 'drop', + 'radio', + 'cascader' + ], // 2020.05.31澧炲姞绾ц仈绫诲瀷 + phone: /^[1][3,4,5,6,7,8,9][0-9]{9}$/, + decimal: /(^[\-0-9][0-9]*(.[0-9]+)?)$/, + number: /(^[\-0-9][0-9]*([0-9]+)?)$/ +}; +const inputTypeArr = ['text', 'string', 'mail', 'textarea', 'password']; +const types = { + int: 'number', + byte: 'number', + decimal: 'number', // "float", + string: 'string', + bool: 'boolean', + date: 'datetime', + date: 'date', + mail: 'email' +}; +//琛ㄥ崟楠岃瘉娉ㄦ剰锛氭瘡娆¢獙璇侀兘蹇呴』鎵цcallback,鍚﹀垯楠岃瘉涓嶆墽琛屽洖璋冩柟娉� +const colPow = Math.pow(10, 3); +import FormExpand from './VolForm/VolFormRender'; +import { + defineAsyncComponent, + defineComponent, + ref, + reactive, + toRefs, + getCurrentInstance, + onMounted, + watch +} from 'vue'; +export default defineComponent({ + components: { + FormExpand, + 'vol-upload': defineAsyncComponent(() => + import('@/components/basic/VolUpload.vue') + ), + 'vol-wang-editor': defineAsyncComponent(() => + import('@/components/editor/VolWangEditor.vue') + ) + }, + props: { + loadKey: { + // 鏄惁鍔犺浇formRules瀛楁閰嶇疆鐨勬暟鎹簮 + type: Boolean, + default: true + }, + width: { + // 琛ㄥ崟瀹藉害 + type: Number, + default: 0 + }, + labelWidth: { + // 琛ㄥ崟宸﹁竟label鏂囧瓧鏍囩鐨勫搴� + type: Number, + default: 100 + }, + formRules: { + // 琛ㄥ崟閰嶇疆瑙勫垯锛屽瀛楁绫诲瀷锛屾槸鍚﹀繀濉� + type: Array, + default: [] + }, + formFields: { + type: Object, + default: () => { + return {}; + } + }, + editor: { + // 2021.01.16缂栬緫鍣ㄤ俊鎭� {uploadImgUrl:"",upload:null//涓婁紶鏂规硶} + type: Object, + default: () => { + return {}; + } + }, + size: { + type: String, //large / default / small + default: 'large' + }, + select2Count: { + //瓒呭嚭鏁伴噺鏄剧ずselect2缁勪欢 + type: Number, + default: 500 + } + }, + computed: { + rules() { + let ruleResult = {}; + this.formRules.forEach((option, xIndex) => { + option.forEach((item) => { + ruleResult[item.field] = [this.getRule(item, this.formFields)]; + }); + }); + if (this.$refs.volform) { + setTimeout(() => { + this.$refs.volform.clearValidate(); + }, 100); + } + return ruleResult; + } + }, + setup(props, context) { + const { appContext, proxy } = getCurrentInstance(); + const remoteCall = ref(true); + const span = ref(1); + const rangeFields = toRefs([]); + const volform = ref(null); + const numberFields = toRefs([]); + onMounted(() => {}); + const initFormRules = (init) => { + if (props.loadKey) { + initSource(); + } + props.formRules.forEach((row, xIndex) => { + if (row.length > span.value) span.value = row.length; + let _count = 0, + _size = 0; + row.forEach((x) => { + if (x.colSize > 0) { + _size = _size + x.colSize; + _count++; + } + }); + if (_count > 0 && row.length - _count > 0) { + let _cellSize = (12 - _size) / (row.length - _count); + row.forEach((x) => { + if (!x.colSize) { + x.colSize = _cellSize; + } + }); + } + row.forEach((item, yIndex) => { + if (item.type == 'number') { + numberFields.push(item.field); + } + // 鐩墠鍙敮鎸乻elect鍗曢�夎繙绋嬫悳绱紝remote杩滅▼浠庡悗鍙板瓧鍏告暟鎹簮杩涜鎼滅储锛寀rl浠庢寚瀹氱殑url鎼滅储 + if (item.remote || item.url) { + // item.remoteData = []; + item.loading = false; + item.point = { x: xIndex, y: yIndex }; + } + // 鍒濆鍖栦笂浼犳枃浠朵俊鎭� + initUpload(item, init); + // 鍒濆鍖栨暟鎹簮绌哄璞� + if (item.dataKey) { + // 涓嬫媺妗嗛兘寮哄埗璁剧疆涓哄瓧绗︿覆绫诲瀷 + item.columnType = 'string'; + if (!item.data) { + item.data = []; + } + } + + if (item.range || item.type == 'range') { + if ( + !(props.formFields[item.field] instanceof Array) || + props.formFields[item.field].length != 2 + ) { + props.formFields[item.field] = ['', '']; + } + rangeFields.push(item.field); + } + }); + }); + }; + + const initSource = () => { + let keys = [], + binds = []; + // 鍒濆鍖栧瓧鍏告暟鎹簮 + props.formRules.forEach((item) => { + item.forEach((x) => { + if (x.dataKey && (!x.data || x.data.length == 0) && !x.remote) { + x.data = []; + binds.push({ key: x.dataKey, data: x.data, type: x.type }); + if (keys.indexOf(x.dataKey) == -1) { + keys.push(x.dataKey); + } + } + }); + }); + + if (keys.length == 0) return; + appContext.config.globalProperties.http + .post('/api/Sys_Dictionary/GetVueDictionary', keys) + .then((dic) => { + bindOptions(dic, binds); + proxy.$emit('dicInited', dic); + }); + }; + const bindOptions = (dic, binds) => { + dic.forEach((d) => { + if (d.data.length > props.select2Count) { + if ( + !binds.some((x) => { + return x.key == d.dicNo && x.type == 'cascader'; + }) + ) { + d.data.forEach((item) => { + item.label = item.value; + item.value = item.key; + }); + } + } + binds.forEach((x) => { + if (x.key != d.dicNo) return true; + // 濡傛灉鏈夋暟鎹殑鍒欎笉鏌ヨ + if (x.data.length > 0) return true; + //2022.03.13澧炲姞绾ц仈鏁版嵁婧愯嚜鍔ㄨ浆鎹� + if (x.type == 'cascader' || x.type == 'treeSelect') { + let _data = JSON.parse(JSON.stringify(d.data)); + let cascaderArr = appContext.config.globalProperties.base.convertTree( + _data, + (node, data, isRoot) => { + if (!node.inited) { + node.inited = true; + node.label = node.value; + node.value = node.key; + } + } + ); + props.formRules.forEach((option) => { + option.forEach((item) => { + if (item.dataKey == x.key) { + item.orginData = x.data; + item.data = cascaderArr; + } + }); + }); + } else if (d.data.length > 0 && !d.data[0].hasOwnProperty('key')) { + let source = d.data, + newSource = new Array(source.length); + for (let index = 0; index < source.length; index++) { + newSource[index] = { + key: source['key'] + '', + value: source['value'] + }; + } + x.data.push(...newSource); + } else { + x.data.push(...d.data); + } + }); + }); + }; + + const initUpload = (item, init) => { + if (!init) return; + if ( + ['img', 'excel', 'file'].indexOf(item.type != -1) || + item.columnType == 'img' + ) { + // 鍙槸娌¤缃槸鍚﹁嚜鍔ㄤ笂浼犵殑锛岄粯璁ら兘鏄�夋嫨鏂囦欢鍚庤嚜鍔ㄤ笂浼� + if (!item.hasOwnProperty('autoUpload')) { + item.autoUpload = true; + } + if (!item.hasOwnProperty('fileList')) { + item.fileList = true; + } + if (!item.hasOwnProperty('downLoad')) { + item.downLoad = true; + } + if (!item.removeBefore) { + item.removeBefore = (index, file, files) => { + return true; + }; + } + if (!item.fileClick) { + item.fileClick = (index, file, files) => { + return true; + }; + } + if (!item.onChange) { + item.onChange = (files) => { + return true; + }; + } + if (!item.uploadAfter) { + item.uploadAfter = (result, files) => { + return true; + }; + } + if (!item.uploadBefore) { + item.uploadBefore = (files) => { + return true; + }; + } + } + }; + const validate = (callback) => { + let result = true; + volform.value.validate((valid) => { + if (!valid) { + appContext.config.globalProperties.$message.error('鏁版嵁楠岃瘉鏈�氳繃!'); + result = false; + } else if (typeof callback === 'function') { + try { + callback(valid); + } catch (error) { + let msg = `琛ㄥ崟楠岃瘉鍥炶皟鏂规硶寮傚父锛�${error.message}`; + appContext.config.globalProperties.$message.error(msg); + console.log(msg); + } + } + }); + return result; + }; + + initFormRules(true); + return { + remoteCall, + span, + rangeFields, + numberFields, + validate, + volform + // initFormRules, + // initSource + }; + }, + created() { + this.formRules.forEach((rules) => { + rules.forEach((option) => { + if (option.type == 'treeSelect' && option.multiple === undefined) { + option.multiple = true; + } + }); + }); + }, + + data() { + return { + // remoteCall: true, + errorImg: 'this.src="' + require('@/assets/imgs/error-img.png') + '"' + // span: 1, + // rangeFields: [], + }; + }, + methods: { + getColWidth(item) { + //2021.08.30 澧炲姞鍔ㄦ�佽绠楄〃鍗曞搴� + let _span = 0; + this.formRules.forEach((row, xIndex) => { + //2022.05.06 杩藉姞琛ㄥ崟涓殣钘忕殑鍏冪礌涓嶅弬涓庡姩鎬佽绠楄〃鍗曞搴� + let rowLength = row.filter((item) => { + return !item.hidden; + }).length; + if (rowLength > _span) _span = rowLength; + }); + let rete = + Math.round(((item.colSize || 12 / _span) / 0.12) * colPow, 10.0) / + colPow; + if (item.colSize) return rete.toFixed(3); + return rete.toFixed(3); + // return (100 - rete).toFixed(3); + }, + previewImg(url) { + this.base.previewImg(url, this.http.ipAddress); + }, + getSrc(path) { + if (!path) return; + if (!this.base.isUrl(path) && path.indexOf('.') != -1) { + return this.http.ipAddress + path; + } + return path; + }, + // 鏄惁涓哄浘鐗囨枃浠剁瓑鏍煎紡骞跺瀛楁鐨勮浆鎹㈡垚鏁扮粍锛歔{name:'1.jpg',path:'127.0.0.1/ff/1.jpg'}] + isFile(item, formFields) { + if ( + item.type == 'img' || + item.columnType == 'img' || + item.type == 'excel' || + item.type == 'file' + ) { + this.convertFileToArray(item, formFields); + return true; + } + return false; + }, + isReadonlyImgFile(item, formFields) { + if ((item.disabled || item.readonly) && this.isFile(item, formFields)) { + return true; + } + return false; + }, + convertFileToArray(item, formFields) { + if (!item.maxFile) { + item.maxFile = 1; // 榛樿鍙兘涓婁紶涓�涓枃浠讹紝鍙互鍦╫nInit涓缃� + } + + let fileInfo = formFields[item.field]; + if (fileInfo instanceof Array) { + return; + } + if (fileInfo === null || fileInfo === undefined) { + formFields[item.field] = []; + return; + } + // 灏嗕互閫楀彿闅斿紑鐨勬枃浠跺垎鍓叉垚鏁扮粍127.0.0.1/aa/1.jpg,灏�127.0.0.1/aa/2.jpg + if (typeof fileInfo === 'string') { + if (fileInfo.trim() === '') { + formFields[item.field] = []; + return; + } + // 濡傛灉鏂囦欢璺緞鏄瓧绗︿覆锛屽垯浣跨敤锛屾媶鍒� + fileInfo = fileInfo.replace(/\\/g, '/'); + let files = fileInfo.split(','); + formFields[item.field] = []; + for (let index = 0; index < files.length; index++) { + let file = files[index]; + let splitFile = file.split('/'); + formFields[item.field].push({ + name: splitFile.length > 0 ? splitFile[splitFile.length - 1] : file, + path: file // this.base.isUrl(file) ? file : this.http.ipAddress + file, + }); + } + } + }, + dowloadFile(file) { + this.base.dowloadFile( + file.path, + file.name, + { + Authorization: this.$store.getters.getToken() + }, + this.http.ipAddress + ); + }, + validatorPhone(ruleOption, value, callback) { + if (!ruleOption.required && !value && value != '0') { + return callback(); + } + if (!rule.phone.test((value || '').trim())) { + return callback(new Error('璇疯緭鍏ユ纭殑鎵嬫満鍙�')); + } + callback(); + }, + validatorPwd(ruleOption, value, callback) { + if (!ruleOption.required && !value && value != '0') { + return callback(); + } + if ((value + '').trim().length < 6) { + return callback(new Error('瀵嗙爜闀垮害涓嶈兘灏忎簬6浣�')); + } + callback(); + }, + convertArrayValue(data, val) { + // 2020.12.13澧炲姞琛ㄥ崟澶氶�夊彧杞崲瀛楀吀 + // 缂栬緫澶氶�塼able鏄剧ず + //2023.04.20淇鍙涓簂abel鏃跺師鏁版嵁琚瓧鍏告浛鎹簡鐨勯棶棰� + let valArr = Array.isArray(val) + ? val.map((x) => { + return x; + }) + : val.split(','); + for (let index = 0; index < valArr.length; index++) { + var _item = data.find((x) => { + return x.key && x.key != '0' && x.key + '' == valArr[index] + ''; + }); + if (_item) { + valArr[index] = _item.value; + } + } + return valArr.join(','); + }, + getText(formFields, item) { + // 2019.10.24淇琛ㄥ崟select缁勪欢涓哄彧璇荤殑灞炴�ф椂娌℃湁缁戝畾鏁版嵁婧� + let text = formFields[item.field]; + if (typeof text === 'function') return text(formFields); + if (text === 'null' || text === '' || text === null || text === undefined) + return '--'; + //2021.03.02澧炲姞鍙鏃舵棩鏈熷鐞� + if (item.type == 'date') { + return text.replace('T', ' ').split(' ')[0]; + } + //2021.03.31淇琛ㄥ崟switch鍙鏃舵病鏈夎浆鎹㈠�肩殑闂 + if (item.type == 'switch') { + return text ? '鏄�' : '鍚�'; + } + if (!item.data) return text; + if (item.type == 'selectList' || item.type == 'checkbox') { + return this.convertArrayValue(item.data, text); + } + var _item = item.data.find((x) => { + return x.key == text; + }); + return _item ? _item.value : text; + }, + onClear(item, formFields) { + // 杩滅▼select鏍囩娓呯┖閫夐」 + item.data.splice(0); + // console.log(2); + }, + onChange(item, value) { + if (item.onChange && typeof item.onChange === 'function') { + item.onChange(value, item); + } + }, + onRemoteChange(item, value) { + // 绗簩娆℃墦寮�鏃讹紝榛樿鍊兼垚浜唘ndefined锛屽緟鏌iewgrid涓噸缃唬鐮� + if (value == undefined && item.data.length > 0) { + this.formFields[item.field] = item.data[0].key; + // console.log('undefined'); + } + this.remoteCall = false; + if (item.onChange && typeof item.onChange === 'function') { + item.onChange(value, item); + } + }, + getData(item) { + return item.data; + }, + + // 杩滅▼鎼滅储(鎵撳紑寮瑰嚭妗嗘椂搴旇绂佹鎼滅储) + remoteSearch(item, formFields, val) { + if (!item.remote && !item.url) { + return; + } + if ( + val == '' || + (item.data.length == 1 && + (val == item.data[0].key || val == item.data[0].value)) + ) { + return; + } + // 寮瑰嚭妗嗘垨鍒濆鍖栬〃鍗曟椂缁檇ata璁剧疆鏁扮粍榛樿鍊�2 + // 2020.09.26淇杩滅▼鎼滅储鑷畾涔塽rl涓嶈捣浣滅敤鐨勯棶棰� + let url; + if (typeof item.url === 'function') { + url = item.url(val, item.dataKey, item); + } else { + url = + (item.url || '/api/Sys_Dictionary/GetSearchDictionary') + + '?dicNo=' + + item.dataKey + + '&value=' + + val; + } + this.http.post(url).then((dicData) => { + //this.$set(item, "loading", false); + item.loading = false; + item.data = dicData; + this.formRules[item.point.x].splice(item.point.y, 1, item); + }); + }, + getObject(date) { + if (typeof date === 'object') { + return date; + } + return new Date(date); + }, + reset(sourceObj) { + // 閲嶇疆琛ㄥ崟鏃讹紝绂佺敤杩滅▼鏌ヨ + this.$refs['volform'].resetFields(); + if (this.rangeFields.length) { + this.rangeFields.forEach((key) => { + this.formFields[key].splice(0); + this.formFields[key] = [null, null]; + }); + } + if (!sourceObj) return; + for (const key in this.formFields) { + if (sourceObj.hasOwnProperty(key)) { + this.formFields[key] = sourceObj[key]; + if (this.numberFields.indexOf(key) != -1) { + this.formFields[key] = sourceObj[key] * 1 || 0; + } + } + } + // this.remoteCall = true; + }, + + fileOnChange(files, item) { + this.$refs.volform.clearValidate(item.field); + if (item.onChange) { + return item.onChange(files); + } + return true; + }, + isReadonly(item) { + return item.readonly || item.disabled; + }, + getRule(item, formFields) { + //2021.07.17澧炲姞鍙琛ㄥ崟涓嶉獙璇� + //range涓巗wtich鏆傛椂涓嶅仛鏍¢獙 + if ( + // item.readonly || + // item.disabled || + item.type == 'switch' || + item.type == 'range' + ) + return { required: false }; + // 鐢ㄦ埛璁剧疆鐨勮嚜瀹氫箟鏂规硶 + if (item.validator && typeof item.validator === 'function') { + return { + validator: (rule, val, callback) => { + // 鐢ㄦ埛鑷畾涔夌殑鏂规硶锛屽鏋滆繑鍥炰簡鍊硷紝鐩存帴鏄剧ず杩斿洖鐨勫�硷紝楠岃瘉涓嶉�氳繃 + let message = item.validator(rule, val); + if (message) return callback(new Error(message + '')); + return callback(); + }, + required: item.required, + trigger: rule.change.indexOf(item.type) != -1 ? 'change' : 'blur' + }; + } + if (['img', 'excel', 'file'].indexOf(item.type) != -1) { + return { + validator: (rule, val, callback) => { + //2021.09.05绉婚櫎鏂囦欢涓婁紶榛樿蹇呭~ + if ( + item.required && + !this.isReadonly(item) && + (!val || !val.length) + ) { + return callback( + new Error(item.type == 'img' ? '璇蜂笂浼犵収鐗�' : '璇蜂笂浼犳枃浠�') + ); + } + return callback(); + }, + required: item.required, + trigger: 'change' + }; + } + // 璁剧疆鏁板瓧鐨勬渶澶у�兼皯鏈�灏忓�� + if ( + item.type == 'number' || + item.columnType == 'number' || + item.columnType == 'int' || + item.type == 'decimal' + ) { + // 濡傛灉鏄繀濉」鐨勬暟瀛楋紝璁剧疆涓�涓粯璁ゆ渶澶т笌鏈�鍊煎皬 + if (item.required && typeof item.min !== 'number') { + item.min = 0; //item.type == "decimal" ? 0.1 : 1; + } + + return { + required: item.required, + message: item.title + '鍙兘鏄暟瀛�', + title: item.title, + trigger: 'blur', + min: item.min, + max: item.max, + type: item.columnType || item.type, + validator: (ruleObj, value, callback) => { + if (!ruleObj.min && !ruleObj.max) { + if (ruleObj.required) { + if ((!value && value != '0') || !rule.decimal.test(value)) { + return callback(new Error('鍙兘鏄暟瀛�')); + } + } + return callback(); + } + if (this.isReadonly(item)) return callback(); + if (ruleObj.type == 'number') { + if (!rule.number.test(value)) { + ruleObj.message = ruleObj.title + '鍙兘鏄暣鏁�'; + return callback(new Error(ruleObj.message)); + } + } else { + if (!rule.decimal.test(value)) { + ruleObj.message = ruleObj.title + '鍙兘鏄暟瀛�'; + return callback(new Error(ruleObj.message)); + } + } + if ( + ruleObj.min !== undefined && + typeof ruleObj.min === 'number' && + value < ruleObj.min + ) { + ruleObj.message = ruleObj.title + '涓嶈兘灏忎簬' + ruleObj.min; + return callback(new Error(ruleObj.message)); + } + if ( + ruleObj.max !== undefined && + typeof ruleObj.max === 'number' && + value > ruleObj.max + ) { + ruleObj.message = ruleObj.title + '涓嶈兘澶т簬' + ruleObj.max; + return callback(new Error(ruleObj.message)); + } + return callback(); + } + }; + } + + // 鎵嬫満銆佸瘑鐮侀獙璇� + if (item.type == 'password' || item.type == 'phone') { + return { + validator: + item.type == 'phone' ? this.validatorPhone : this.validatorPwd, + required: item.required, + trigger: 'blur' + }; + } + + if (!item.required && item.type != 'mail') return { required: false }; + + if (!item.hasOwnProperty('type')) item.type = 'text'; + + if (inputTypeArr.indexOf(item.type) != -1) { + let message = + item.title + + (item.type == 'mail' ? '蹇呴』鏄竴涓偖绠卞湴鍧�' : '涓嶈兘涓虹┖'); + let type = item.type == 'mail' ? 'email' : types[item.columnType]; + let _rule = { + required: true, + message: message, + trigger: 'blur', + type: type, + validator: (ruleObj, value, callback) => { + if ( + !this.isReadonly(item) && + (value === '' || value === undefined || value === null) + ) { + return callback(new Error(ruleObj.message)); + } + return callback(); + } + }; + if (item.type == 'mail') { + _rule.validator = undefined; + return _rule; + } + if (item.min) { + _rule.min = item.min; + _rule.message = item.title + '鑷冲皯' + item.min + '涓瓧绗�!'; + } + if (item.max) { + return [ + _rule, + { + max: item.max, + required: true, + message: item.title + '鏈�澶�' + item.max + '涓瓧绗�!', + trigger: 'blur' + } + ]; + } + return _rule; + } + + if (item.type == 'radio') { + return { + required: item.required, + message: '璇烽�夋嫨' + item.title, + trigger: 'change', + type: 'string' + }; + } + if ( + item.type == 'date' || + item.type == 'datetime' || + item.type == 'month' || + item.type == 'time' + ) { + return { + required: true, + message: '璇烽�夋嫨' + item.title, + trigger: 'change', + type: item.range ? 'array' : 'string', + validator: (rule, val, callback) => { + if (this.isReadonly(item)) return callback(); + // 鐢ㄦ埛鑷畾涔夌殑鏂规硶锛屽鏋滆繑鍥炰簡鍊硷紝鐩存帴鏄剧ず杩斿洖鐨勫�硷紝楠岃瘉涓嶉�氳繃 + if (!val || (item.range && !val.length)) { + return callback(new Error('璇烽�夋嫨鏃ユ湡')); + } + return callback(); + } + }; + } + + if (item.type == 'cascader') { + return { + type: 'array', + required: true, + min: item.min || 1, + // message: "璇烽�夋嫨" + item.title, + trigger: 'change', + validator: (rule, val, callback) => { + if (this.isReadonly(item)) return callback(); + // 鐢ㄦ埛鑷畾涔夌殑鏂规硶锛屽鏋滆繑鍥炰簡鍊硷紝鐩存帴鏄剧ず杩斿洖鐨勫�硷紝楠岃瘉涓嶉�氳繃 + let _arr = this.formFields[item.field]; + if (!_arr || !_arr.length) { + return callback(new Error('璇烽�夋嫨' + item.title)); + } + return callback(); + } + }; + } + + if ( + ['select', 'selectList', 'checkbox', 'cascader', 'treeSelect'].indexOf( + item.type + ) != -1 + ) { + let _rule = { + type: item.type == 'select' ? 'string' : 'array', + required: true, + min: item.min || 1, + message: '璇烽�夋嫨' + item.title, + trigger: 'change', + validator: (rule, value, callback) => { + if (this.isReadonly(item)) return callback(); + //2021.11.27淇澶氶�夋病鏈夋彁绀虹殑闂 + if (value == undefined || value === '') { + return callback(new Error(rule.message)); + } else if ( + (item.type == 'checkbox' || + item.type == 'selectList' || + item.type == 'treeSelect') && + (!(value instanceof Array) || !value.length) + ) { + return callback(new Error(rule.message)); + } + return callback(); + } + }; + + if (_rule.max) { + _rule.nax = item.max; + _rule.message = '鏈�澶氬彧鑳介�夋嫨' + item.max + '椤�'; + } + return _rule; + } + return {}; + }, + compareDate(date1, date2) { + if (!date2) { + return true; + } + return ( + date1.valueOf() < + (typeof date2 == 'number' ? date2 : new Date(date2).valueOf()) + ); + }, + getDateOptions(date, item) { + //2021.07.17璁剧疆鏃堕棿鍙�夎寖鍥� + 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) + ); + }, + getDateFormat(item) { + if (item.type == 'month') { + return 'YYYY-MM'; + } + // if (item.type=='time') { + // return 'HH:mm:ss' + // } + //瑙乭ttps://day.js.org/docs/zh-CN/display/format + return item.type == 'date' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'; + }, + dateRangeChange(val, item) { + if (!val) { + this.$emit('update:formFields'); + return; + } + item.onChange && item.onChange(val); + }, + onKeyPress($event, item) { + if ($event.keyCode == 13) { + return; + } + item.onKeyPress($event); + }, + filterMethod(value, data) { + return data.label.includes(value); + }, + getNode( label,node, data){ + console.log(label) + } + } +}); +</script> +<style lang="less" scoped> +.el-form-item { + margin-right: 0; +} +.el-form-item { + .form-imgs { + img { + float: left; + cursor: pointer; + object-fit: cover; + margin: 0 10px 10px 0; + width: 65px; + height: 65px; + border: 1px solid #c7c7c7; + overflow: hidden; + border-radius: 5px; + box-sizing: content-box; + } + } +} +.el-form-item ::v-deep(.el-form-item__label) { + padding: 0 0px 0 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.el-form-item ::v-deep(.el-range-separator) { + text-align: center; + width: 13px; + padding: 0px 1px; + font-size: 12px; +} +.el-form-item ::v-deep(.el-range__close-icon) { + margin-right: -10px; +} +.form-item-extra { + > *:first-child { + flex: 1; + } + display: flex; + .form-extra { + padding-left: 7px; + line-height: 36px; + } +} +.vol-form-item { + width: 100%; +} +.vol-form-item ::v-deep(.el-form-item__content) { + display: unset !important; +} +.vol-form-item ::v-deep(.el-input--large .el-input__inner) { + height: 34px !important; +} +.vol-form-item ::v-deep(.el-input-number--large .el-input-number__increase) { + border-top: 1px solid #d4d4d4; +} +.vol-form-item ::v-deep(.el-input-number--large .el-input-number__decrease) { + border-bottom: 1px solid #d4d4d4; +} +.vol-form-item ::v-deep(.el-input--large.el-date-editor) { + height: 36px; +} +.v-date-range ::v-deep(.el-input__prefix) { + display: none; +} +.v-date-range ::v-deep(.el-input__inner) { + padding: 0; +} + +.el-form-item ::v-deep(.el-checkbox) { + margin-right: 8px; +} +.el-form-item ::v-deep(.el-checkbox .el-checkbox__label) { + padding-left: 5px; +} +.el-form-item ::v-deep(textarea) { + font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft YaHei', '寰蒋闆呴粦', Arial, sans-serif !important; +} +.el-form-item ::v-deep(.el-select .el-select__tags > span) { + display: flex; +} +.el-form-item ::v-deep(.el-select-v2__combobox-input) { + height: 30px; +} +.el-form-item ::v-deep(.el-select__tags) { + overflow: hidden; + height: 30px; +} +.el-form-item ::v-deep(.el-select-tags-wrapper) { + position: absolute; +} + +.el-form-item { + vertical-align: top !important; +} +.form-file-list { + a { + color: #3ea9ff; + } + a:hover { + cursor: pointer; + color: #0281e7; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm/VolFormRender.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm/VolFormRender.js" new file mode 100644 index 0000000..ef7459e --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolForm/VolFormRender.js" @@ -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(); + } +}; + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/DownloadForm.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/DownloadForm.js" new file mode 100644 index 0000000..69bf098 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/DownloadForm.js" @@ -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> + <vol-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"></vol-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_VolTable}", "import VolTable from '@/components/basic/VolTable'") + code = code.replace(",{component_table}", ",'vol-table': VolTable") + } else { + code = code.replace("{import_VolTable}", '') + 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> + <vol-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"></vol-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')) { // 闈濱E涓嬭浇 + 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) + } +} \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormDraggable.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormDraggable.vue" new file mode 100644 index 0000000..de8b85f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormDraggable.vue" @@ -0,0 +1,1159 @@ +<template> + <div class="drag-container"> + <!-- @start="onStart" --> + <div class="drag-left"> + <div class="left-title">缁勪欢鍒楄〃</div> + <draggable + v-model="components" + @end="end1" + class="left-draggable-item" + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" + animation="300" + @start="onStart" + :sort="false" + :move="onMove1" + > + <transition-group> + <div + :class="item.id == 1 ? 'item forbid' : 'item'" + v-for="item in components" + :key="item.id" + > + <i :class="item.icon"></i> {{ item.name }} + </div> + </transition-group> + </draggable> + <div class="example"> + <div @click="example1">绀轰緥涓�<i class="el-icon-arrow-right"></i></div> + <div @click="example2">绀轰緥浜�<i class="el-icon-arrow-right"></i></div> + <div @click="example3">绀轰緥涓�<i class="el-icon-arrow-right"></i></div> + </div> + </div> + + <div class="drag-center"> + <div class="center-top"> + <span> + <!-- <i class="el-icon-warning-outline"></i>楂樻晥鐨勮〃鍗曢厤缃� --> + </span> + <el-button type="primary" size="mini" plain @click="save" + ><i class="el-icon-check"> </i>淇濆瓨</el-button + > + <el-button type="primary" size="mini" plain @click="preview(true)" + ><i class="el-icon-view"> </i>棰勮</el-button + > + <el-button type="primary" size="mini" plain @click="download" + ><i class="el-icon-view"> </i>涓嬭浇</el-button + > + <el-button type="primary" @click="clearItems" size="mini" plain + ><i class="el-icon-delete"> </i>娓呯┖</el-button + > + <a + style="margin-left: 15px" + href="http://v2.volcore.xyz/document/api" + target="_blank" + >璁捐鍣ㄥ熀浜庢鏋秜olform銆乿oltable銆乿olupload銆乿olbox瀹氬埗寮�鍙�</a + > + </div> + <div> + <el-alert + title="鍏充簬琛ㄥ崟璁捐鍣�" + type="success" + :show-icon="true" + :closable="false" + > + <div> + 1銆佽〃鍗曡璁″櫒鍩轰簬draggable寮�鍙�,涓烘湰妗嗘灦鑷畾涔夐〉闈㈠姛鑳界殑琛ュ厖,妗嗘灦浠嶄互鍙鍖栦唬鐮佺敓鎴愬櫒涓烘牳蹇� + </div> + <div> + 2銆佹敮鎸佸彲瑙嗗寲璁捐1瀵�1銆�1瀵瑰鍙婅〃鍗曚笅鎷夋鑷姩缁戝畾銆乼able鑷姩鍔犺浇鏁版嵁(鍒嗛〉銆佺紪杈�)銆佽嚜鍔ㄤ笂浼犳枃浠躲�佸瘜鏂囨湰缂栬緫 + </div> + </el-alert> + </div> + <el-scrollbar style="flex: 1"> + <div class="tips" key="empty" v-show="!currentComponents.length"> + 璇峰皢宸﹁竟缁勪欢鎷栧叆姝ゅ鍣ㄤ腑 + </div> + <el-form label-position="top"> + <draggable + class="draggable-container" + v-model="currentComponents" + @end="end2" + animation="300" + :move="onMove" + group="componentsGroup" + > + <transition-group class="drag-center-item"> + <div + class="item2" + :class="{ actived: index === currentIndex }" + @click="itemClick(item, index)" + :style="{ width: item.width + '%' }" + v-for="(item, index) in currentComponents" + :key="index" + > + <i + class="el-icon-document-copy" + @click.stop="copyItem(item)" + ></i> + <i class="el-icon-delete" @click.stop="removeItem(index)"> </i> + <el-form-item + :required="item.required" + label-position="top" + style="width: 100%" + :label="item.type == 'line' ? '' : item.name" + > + <el-col> + <!-- <div></div> --> + <!-- {{ item.name }} --> + <el-input + v-if="item.type == 'text'" + placeholder="璇疯緭鍏ュ唴瀹�" + v-model="item.value" + :disabled="item.readonly" + size="medium" + ></el-input> + <el-input + v-else-if="item.type == 'textarea'" + type="textarea" + v-model="item.value" + :disabled="item.readonly" + placeholder="璇疯緭鍏ュ唴瀹�" + ></el-input> + <el-date-picker + v-else-if="item.type == 'date'" + align="right" + v-model="item.value" + type="date" + :disabled="item.readonly" + size="medium" + placeholder="閫夋嫨鏃ユ湡" + > + </el-date-picker> + <el-radio-group + :disabled="item.readonly" + v-else-if="item.type == 'radio'" + v-model="item.value" + > + <el-radio + v-for="item in item.data" + :key="item.key" + :label="item.value" + :value="item.key" + > + </el-radio> + <!-- <el-radio :label="1">鏄�</el-radio> + <el-radio :label="0">鍚�</el-radio> --> + </el-radio-group> + + <el-checkbox-group + v-model="item.values" + :disabled="item.readonly" + style="width: 100%; display: inline-block" + v-else-if="item.type == 'checkbox'" + > + <el-checkbox + v-for="item in item.data" + :key="item.key" + :label="item.value" + :value="item.key" + > + </el-checkbox> + <!-- <el-checkbox label="澶嶉�夋 A"></el-checkbox> + <el-checkbox label="澶嶉�夋 B"></el-checkbox> + <el-checkbox label="澶嶉�夋 C"></el-checkbox> --> + </el-checkbox-group> + + <el-select + style="width: 100%" + :disabled="item.readonly" + v-model="item.value" + size="medium" + v-else-if="item.type == 'select'" + placeholder="璇烽�夋嫨" + > + <el-option + v-for="item in item.data" + :key="item.value" + :label="item.label" + :value="item.value" + > + </el-option> + </el-select> + <el-select + style="width: 100%" + :disabled="item.readonly" + v-model="item.values" + size="medium" + :multiple="true" + v-else-if="item.type == 'selectList'" + placeholder="璇烽�夋嫨" + > + <el-option + v-for="item in item.data" + :key="item.value" + :label="item.label" + :value="item.value" + > + </el-option> + </el-select> + <el-cascader + :disabled="item.readonly" + style="width: 100%" + v-else-if="item.type == 'cascader'" + v-model="item.values" + :options="item.data" + @change="() => {}" + ></el-cascader> + + <el-switch + :disabled="item.readonly" + v-model="item.value" + style="width: 100%" + v-else-if="item.type == 'switch'" + active-color="#13ce66" + inactive-color="#0e7ef3" + :active-value="1" + :inactive-value="0" + > + </el-switch> + <div class="col-line" v-else-if="item.type == 'line'"> + {{ item.name }} + </div> + + <vol-upload + v-else-if=" + item.type == 'img' || + item.type == 'excel' || + item.type == 'file' + " + :fileInfo="item.fileInfo" + :url="item.url" + :img="item.type == 'img'" + :excel="item.type == 'excel'" + :multiple="item.multiple" + :max-size="item.maxSize" + :max-file="item.maxFile" + :autoUpload="item.autoUpload" + > + </vol-upload> + <vol-wang-editor + v-else-if="item.type == 'editor'" + :url="item.url" + v-model="item.value" + :height="item.height" + ></vol-wang-editor> + + <vol-table + v-else-if="item.type == 'table'" + :url="item.url" + :load-key="true" + :index="item.edit" + :tableData="item.tableData" + :columns="item.columns" + :height="item.height" + :pagination-hide="true" + :column-index="item.columnIndex" + :ck="item.ck" + ></vol-table> + <el-button + @click="model = true" + v-else-if="item.type == 'box'" + type="primary" + size="small" + >{{ item.name }}</el-button + > + </el-col> + </el-form-item> + </div> + </transition-group> + </draggable> + </el-form> + </el-scrollbar> + </div> + <div class="drag-right"> + <div class="left-title">缁勪欢灞炴��</div> + <div class="attr" v-show="currentIndex != -1"> + <div class="attr-item"> + <div class="text">瀛楁鍚嶇О</div> + <el-input size="medium" v-model="currentItem.name" /> + </div> + <div class="attr-item"> + <div class="text">瀛楁(鍞竴瀛楁)</div> + <el-input size="medium" v-model="currentItem.field" /> + </div> + <div class="attr-item" v-show="currentItem.type == 'table'"> + <div class="text">鍚庡彴杩斿洖鏁版嵁鎺ュ彛鍦板潃</div> + <el-input + size="medium" + placeholder="渚�:api/琛ㄥ悕/getPageData" + v-model="currentItem.url" + /> + </div> + <div + class="attr-item" + v-show=" + currentItem.type != 'table' && currentItem.hasOwnProperty('url') + " + > + <div class="text">涓婁紶鎺ュ彛鍦板潃(鍚庡彴鎺ュ彛)</div> + <el-input + size="medium" + placeholder="鍙敤妗嗘灦鍦板潃:api/琛ㄥ悕/upload" + v-model="currentItem.url" + /> + <div class="text" style="margin-top: 10px">鏂囦欢澶у皬闄愬埗(M)</div> + <el-input size="medium" v-model="currentItem.maxSize" /> + <div class="text" style="margin-top: 10px">鏄惁澶氭枃浠朵笂浼�</div> + <el-switch + v-model="currentItem.multiple" + style="width: 100%" + active-color="#13ce66" + inactive-color="rgb(165 165 165)" + :active-value="true" + :inactive-value="false" + > + </el-switch> + <div class="text" style="margin-top: 10px">鏄惁鑷姩涓婁紶</div> + <el-switch + v-model="currentItem.autoUpload" + style="width: 100%" + active-color="#13ce66" + inactive-color="rgb(165 165 165)" + :active-value="true" + :inactive-value="false" + > + </el-switch> + </div> + <div class="attr-item" v-show="currentItem.data"> + <div class="text">鏁版嵁婧�(涓嬫媺妗嗙粦瀹氳缃腑缁存姢)</div> + + <!-- dicList --> + <el-select + style="width: 100%" + v-model="currentItem.key" + size="medium" + @change="dicChange" + placeholder="璇烽�夋嫨鏁版嵁婧愬瓧鍏�" + > + <el-option + v-for="item in dicList" + :key="item.key" + :label="item.value" + :value="item.key" + > + </el-option> + </el-select> + </div> + <div class="attr-item" v-show="currentItem.type != 'table'"> + <div class="text">鏍囩瀹藉害</div> + <el-slider + style="width: 90%" + :min="20" + v-model="colWidth" + :step="10" + show-stops + > + </el-slider> + </div> + <div class="attr-item attr2" v-show="currentItem.type != 'table'"> + <div> + <div class="text">蹇呭~</div> + <el-switch + v-model="currentItem.required" + style="width: 100%" + active-color="#13ce66" + inactive-color="rgb(165 165 165)" + :active-value="true" + :inactive-value="false" + > + </el-switch> + </div> + + <div> + <!-- active-text="鏄�" + inactive-text="鍚�" --> + <div class="text">鍙</div> + <el-switch + v-model="currentItem.readonly" + style="width: 100%" + active-color="#13ce66" + inactive-color="rgb(165 165 165)" + :active-value="true" + :inactive-value="false" + > + </el-switch> + </div> + </div> + <div v-show="currentItem.type == 'table'"> + <div class="attr-item"> + <div class="text">鏄惁浣跨敤閫夐」鍗�(tabs)</div> + <div> + <el-switch + v-model="currentItem.tabs" + style="width: 100%" + active-text="鏄�" + inactive-text="鍚�" + active-color="#13ce66" + inactive-color="rgb(165 165 165)" + :active-value="true" + :inactive-value="false" + > + </el-switch> + </div> + </div> + <div class="attr-item attr2"> + <el-button + style="width: 100%" + @click="openTableModel" + type="primary" + size="medium" + >table閰嶇疆</el-button + > + </div> + </div> + </div> + </div> + </div> + <vol-box + v-model="model" + :height="300" + :width="550" + :lazy="true" + title="寮瑰嚭妗�" + > + <template #content> + <div>寮瑰嚭妗嗗唴瀹�</div> + </template> + <template #footer> + <div> + <el-button type="primary" size="mini" @click="model = false" + ><i class="el-icon-close"></i>鐐瑰嚮鍏抽棴</el-button + > + <el-button size="mini" @click="model = false" + ><i class="el-icon-close"></i>鍏抽棴</el-button + > + </div> + </template> + </vol-box> + + <vol-box + v-model="previewModel" + :height="600" + :width="1300" + :lazy="true" + :padding="1" + :close="false" + title="棰勮" + > + <preview style="height:600px" :options="viewFormData"></preview> + </vol-box> + + <vol-box + v-model="tableModel" + :height="600" + :width="1300" + :lazy="true" + :padding="0" + :title="currentItem.name" + > + <template #content> + <div style="height:600px" > + <el-alert title="鍏充簬table閰嶇疆" type="info" :closable="false" show-icon> + 姝ゅtable鏄妗嗘灦voltable鍩烘湰鎿嶄綔鐨勯厤缃�,濡傛灉闇�瑕佷簨浠惰Е鍙戙�佹暟鎹姞杞界瓑鏇村鍔熻兘锛岃鍦ㄧ敓鎴愬悗鐨勪唬鐮佹坊鍔犻渶瑕佺殑鍔熻兘锛屽畬鏁撮厤缃鏂囨。<a + href="http://v2.volcore.xyz/document/api" + style="color: #1e88e5; margin-left: 9px" + target="_blank" + >voltable</a + > + </el-alert> + <div class="btns"> + <div class="btns-left"> + 琛ㄦ牸榛樿鍔熻兘鎸夐挳锛� + <el-checkbox + v-for="item in currentItem.buttons" + :label="item.name" + :key="item.name" + >{{ item.name }}</el-checkbox + > + </div> + <div class="btns-right"> + <el-button type="primary" size="mini" @click="addRow" + ><i class="el-icon-plus"></i>娣诲姞瀛楁</el-button + > + <el-button type="primary" size="mini" @click="delRow" + ><i class="el-icon-delete"></i>鍒犻櫎瀛楁</el-button + > + <el-button type="primary" size="mini" @click="sortRow" + ><i class="el-icon-sort"></i>閲嶆柊鎺掑垪</el-button + > + </div> + </div> + <vol-table + :load-key="true" + :tableData="currnetTableData" + :columns="currentTableOption" + :height="448" + ref="table" + :index="true" + :pagination-hide="true" + :column-index="true" + :ck="true" + ></vol-table> + </div> + </template> + <template #footer> + <div style="text-align: center"> + <el-button size="mini" @click="tableModel = false" + ><i class="el-icon-close"></i>鍏抽棴</el-button + > + <el-button type="primary" size="mini" @click="saveConfigOptions" + ><i class="el-icon-check"></i>淇濆瓨</el-button + > + </div> + </template> + </vol-box> +</template> + +<script> +import { h, resolveComponent } from "vue"; +// import draggable from "vuedraggable"; +import { VueDraggableNext } from "vue-draggable-next"; +import VolWangEditor from "@/components/editor/VolWangEditor.vue"; +import VolUpload from "./../VolUpload"; +import VolTable from "./../VolTable"; +import VolBox from "./../VolBox"; +import VolFormPreview from "./VolFormPreview"; +import { components, tableOption } from "./options"; +import { options1, options2, options3 } from "./formTemplate"; +import downloadForm from "./DownloadForm"; +export default { + props: { + userComponents: { + type: Array, + default: () => { + return []; + }, + }, + }, + components: { + draggable: VueDraggableNext, + "vol-upload": VolUpload, + "vol-wang-editor": VolWangEditor, + "vol-table": VolTable, + "vol-box": VolBox, + preview: VolFormPreview, + }, + data() { + return { + options:{}, + options1: options1, + options2: options2, + options3: options3, + colWidth: 100, + currentIndex: -1, + currentItem: {}, + currnetTableData: [], + currentTableOption: tableOption, + //瀹氫箟瑕佽鎷栨嫿瀵硅薄鐨勬暟缁� + components: components, + currentComponents: [], + dicList: [], + model: false, + tableModel: false, + previewModel: false, + viewFormData: { fields: {}, formOptions: [], tables: [] }, + }; + }, + watch: { + colWidth(newVal) { + if (this.currentIndex != -1) { + this.currentComponents[this.currentIndex].width = newVal; + } + }, + userComponents: { + handler(newVal) { + this.currentComponents = newVal; + }, + immediate: true, + deep: true, + }, + }, + created() { + this.currentComponents = this.userComponents; + this.http + .post("api/Sys_Dictionary/GetBuilderDictionary", {}, false) + .then((x) => { + this.dicList = x.map((c) => { + return { key: c, value: c }; + }); + }); + }, + methods: { + getFormOptions(item) { + let _option = {}; + _option.field = item.field; + _option.title = item.name; + _option.type = item.type; + _option.required = item.required; + _option.readonly = item.readonly; + if (item.type == "line") { + _option.title = ""; + let title = item.name; + _option.render = (h) => { + return h( + "div", + { + style: { + "line-height": "25px", + "font-weight": "bold", + "margin-left": "15px", + "border-bottom": "1px solid #dadada", + }, + }, + title + //涔熷彲浠ュ湪杩欓噷鏀句竴浜涚粍浠讹紝渚嬪锛� + // resolveComponent("el-tooltip"), + // { + // content: "杩欓噷鏄彁绀虹殑鍐呭", + // props: { effect: "dark", placement: "top-start" }, + // style: {}, + // }, + // [h("a", { style: { color: "#2a92ff" } }, "鎻愮ず淇℃伅")] + ); + }; + } + if (item.type == "editor") { + _option.height = item.height; + } + if (item.width == 100) { + _option.colSize = 12; + } else { + _option.colSize = (_option.width * 12) / 100; + } + if (["img", "excel", "file"].indexOf(item.type) != -1) { + _option.maxSize = item.maxSize; + _option.fileInfo = item.fileInfo; + _option.multiple = item.multiple; + _option.autoUpload = item.autoUpload; + _option.maxFile = item.maxFile; + } + if ( + ["img", "excel", "file", "editor", "table"].indexOf(item.type) != -1 + ) { + _option.url = item.url; + } + if (item.data) { + _option.data = item.data; + _option.dataKey = item.key; + } + return _option; + }, + getLineFormOptions(index) { + let _index = index; + let endIndex = index; + let width = 0; + let _options = []; + for (index; index < this.filterCurrentComponents().length; index++) { + const item = this.currentComponents[index]; + if (item.width + width <= 100) { + width = item.width + width; + endIndex = index; + _options.push(this.getFormOptions(item)); + } + } + return { options: _options, index: _index, endIndex: endIndex }; + }, + filterCurrentComponents() { + return this.currentComponents.filter((x) => { + return x.type != "table"; + }); + }, + setSpan() {}, + preview(isPre) { + let _fields = {}; + let _formOptions = []; + let endIndex = -1; + this.filterCurrentComponents().forEach((item, index) => { + if (item.hasOwnProperty("values")) { + _fields[item.field] = []; + } else { + _fields[item.field] = null; + } + + if (item.width == 100) { + _formOptions.push([this.getFormOptions(item)]); + } else { + if (endIndex == -1) { + let lineOptions = this.getLineFormOptions(index); + endIndex = lineOptions.endIndex; + _formOptions.push(lineOptions.options); + endIndex--; + } else { + endIndex--; + } + } + // _fields[item.field] = null;xc + // let _option = {}; + // _option.field = item.field; + // _option.title = item.name; + // _option.type = item.type; + // _option.required = item.required; + // _option.readonly = item.readonly; + // if (["img", "excel", "file"].indexOf(item.type) != -1) { + // _option.maxSize = item.maxSize; + // _option.fileInfo = item.fileInfo; + // _option.multiple = item.multiple; + // _option.autoUpload = item.autoUpload; + // _option.maxFile = item.maxFile; + // } + // if ( + // ["img", "excel", "file", "editor", "table"].indexOf(item.type) != -1 + // ) { + // _option.url = item.url; + // } + // if (item.data) { + // _option.data = item.data; + // _option.dataKey = item.key; + // } + // _formOptions.push(_option); + }); + this.viewFormData.fields = _fields; + // console.log(JSON.stringify(_formOptions)) + this.viewFormData.formOptions = _formOptions; + if (isPre) { + this.previewModel = true; + } + let tableIndex = 0; + let keys = []; + let tables = this.currentComponents + .filter((x) => { + return x.type == "table"; + }) + .map((m) => { + m.pagination = !m.url; + if (m.name == "琛ㄦ牸") { + tableIndex++; + } + return { + name: m.name + (m.name == "琛ㄦ牸" ? tableIndex : ""), + url: m.url, + tabs: m.tabs, + pagination: m.pagination, + buttons: m.buttons, + columns: m.columns.map((c) => { + let obj = { + title: c.title, + field: c.field, + hidden: !c.show, + width: c.width, + required: c.required, + }; + + if (c.dataSource) { + obj.bind = { key: c.dataSource, data: [] }; + } + if (c.dataSource) { + keys.push(c.dataSource); + } + if (c.edit) { + if (!obj.bind) { + obj.bind = { key: "", data: [] }; + } + obj.edit = { type: c.editType }; + } + return obj; + }), + tableData: [{}, {}, {}], + }; + }); + this.viewFormData.tables = tables.filter((x) => { + return !x.tabs; + }); + this.viewFormData.tabs = tables.filter((x) => { + return x.tabs; + }); + this.getDicKeys(keys); + this.options = this.viewFormData; + }, + getDicKeys(keys) { + if (!keys.length) { + return; + } + debugger + this.http + .post("api/Sys_Dictionary/GetVueDictionary", keys, true) + .then((result) => { + result.forEach((c) => { + this.viewFormData.tables.forEach((t) => { + let _option = t.columns.find((x) => { + return x.bind && x.bind.key == c.dicNo; + }); + if (_option) { + _option.bind.data = c.data; + } + }); + this.viewFormData.tabs.forEach((t) => { + let _option = t.columns.find((x) => { + return x.bind && x.bind.key == c.dicNo; + }); + if (_option) { + _option.bind.data = c.data; + } + }); + }); + }); + }, + save() { + this.preview(false); + this.$emit("save", { + daraggeOptions: this.currentComponents, + formOptions: this.viewFormData, + }); + }, + download() { + this.preview(false); + downloadForm.call(this); + }, + openTableModel() { + let dataSource = this.currentTableOption.find((x) => { + return x.field == "dataSource"; + }); + if (!dataSource.bind.data.length) { + dataSource.bind.data = this.dicList; + } + this.currnetTableData = JSON.parse( + JSON.stringify(this.currentItem.columns) + ); + this.tableModel = true; + }, + addRow() { + this.currnetTableData.push({ field: this.getField() }); + }, + delRow() { + this.$confirm("纭瑕佸垹闄ら�夋嫨鐨勬暟鎹悧?", "璀﹀憡", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + center: true, + }).then(() => { + this.$refs.table.delRow(); + }); + }, + sortRow() { + this.currnetTableData = this.currnetTableData.sort((a, b) => { + return a.orderNo - b.orderNo; + }); //.reverse(); + this.$Message.success("鍒楁樉绀洪『搴忓凡閲嶆柊鎺掑垪,鐐瑰嚮棰勮鍙煡鐪�"); + }, + saveConfigOptions() { + this.currentItem.columns = JSON.parse( + JSON.stringify(this.currnetTableData) + ); + this.tableModel = false; + }, + copyItem(item) { + let itemClone = JSON.parse(JSON.stringify(item)); + itemClone.field = "field" + new Date().valueOf(); + this.currentComponents.push(itemClone); + }, + removeItem(index) { + this.currentComponents.splice(index, 1); + this.colWidth = 100; + this.currentIndex = -1; + this.currentItem = {}; + }, + clearItems() { + this.currentComponents.length = 0; + this.colWidth = 100; + this.currentIndex = -1; + this.currentItem = {}; + }, + itemClick(item, index) { + this.currentIndex = index; + this.colWidth = this.currentComponents[this.currentIndex].width; + this.currentItem = this.currentComponents[this.currentIndex]; + }, + //寮�濮嬫嫋鎷戒簨浠� + onStart(e, e1) { + this.drag = true; + }, + getField() { + return "field" + new Date().valueOf(); + }, + //宸﹁竟寰�鍙宠竟鎷栧姩鏃剁殑浜嬩欢 + end1(e) { + if (1 == 1 && e.from !== e.to) { + let obj = JSON.parse(JSON.stringify(this.components[e.oldIndex])); + obj.field = this.getField(); + obj.width = 100; + obj.readonly = false; + obj.required = false; + this.currentComponents.splice(e.newIndex, 1, obj); + this.userComponents.splice(0); + this.userComponents.push(...this.currentComponents); + // this.currentComponents = this.currentComponents.filter((x) => { + // return x.hasOwnProperty("field"); + // }); + this.colWidth = 100; + this.currentIndex = e.newIndex; //this.currentComponents.length - 1; + this.currentItem = this.currentComponents[this.currentIndex]; + } + }, + //鍙宠竟寰�宸﹁竟鎷栧姩鏃剁殑浜嬩欢 + end2(e) {}, + onMove1(e, originalEvent) { + // this.moveId = e.relatedContext.element.id; + return true; + }, + //move鍥炶皟鏂规硶 + onMove(e, originalEvent) { + console.log(JSON.stringify(this.currentComponents)); + return true; + }, + dicChange(key) { + debugger + this.http + .post("api/Sys_Dictionary/GetVueDictionary", [key], true) + .then((result) => { + this.currentItem.data = result[0].data; + if (result[0].data.length) { + if (this.currentItem.type == "select") { + this.currentItem.value = result[0].data[0].value; + } else { + this.currentItem.values = [result[0].data[0].value]; + } + } + }); + }, + example1() { + this.currentComponents = this.options1; + }, + example2() { + this.currentComponents = this.options2; + }, + example3() { + this.currentComponents = this.options3; + }, + }, + computed: { + tabsTable() { + return this.currentComponents.filter((x) => { + return x.type == "table" && x.tabs == true; + }); + }, + }, +}; +</script> +<style lang="less" scoped> +* { + box-sizing: border-box; +} +.drag-container { + /* padding: 20px; */ + display: flex; + height: 100%; + position: absolute; + width: 100%; + box-sizing: border-box; +} +.drag-left { + width: 250px; + display: flex; + border-right: 1px solid #eee; + flex-direction: column; +} +.left-title { + height: 42px; + text-align: left; + border-right: 1px solid #eee; + padding: 10px 0 10px 11px; + border-bottom: 1px solid #eee; +} +.drag-center { + display: flex; + flex-direction: column; + flex: 1; +} +.left-draggable-item { + // flex: 1; + display: inline-block; + padding: 5px; + // border-right: 1px solid #eee; +} + +.left-draggable-item .item { + cursor: move; + float: left; + width: 111px; + /* height: 20px; */ + text-align: center; + border: 1px solid #eeeeee; + padding: 2px 13px; + text-align: left; + line-height: 28px; + margin: 4px; + border-radius: 3px; + background: #f0f9eb; + font-size: 13px; +} +// .drag-center-item { +// display: inline-block; +// width: 100%; +// height: calc(100vh - 122px); +// padding: 10px; +// } +.draggable-container { + display: inline-block; + width: 100%; + height: calc(100vh - 215px); + padding: 10px 0; +} + +.item2 { + position: relative; + cursor: move; + padding: 18px 10px 10px 10px; + text-align: left; + float: left; + margin-bottom: 10px; +} +.item2 .el-icon-delete, +.item2 .el-icon-document-copy { + position: absolute; + right: 10px; + top: 2px; + padding: 5px; + display: none; + color: red; + cursor: pointer; +} +.item2 .el-icon-document-copy { + right: 35px; +} +.item2:hover, +.actived { + background: #f0f9eb; +} +.item:hover { + border: 1px dashed #787be8; + color: #787be8; +} +.item2:hover .el-icon-delete, +.item2:hover .el-icon-document-copy { + display: block; +} + +.drag-right { + background: #f7fbff3d; + width: 250px; + border-left: 1px solid #eee; +} + +.center-top { + height: 42px; + line-height: 41px; + background: #f2f5fb; + border-bottom: 1px solid #eee; + text-align: left; + padding: 0 10px; + font-size: 12px; + color: #3391f3; +} +.center-top span { + margin-right: 10px; +} +.attr { + padding: 0px 15px 15px 15px; +} +.attr-item { + text-align: left; + margin-top: 12px; + font-size: 14px; +} +.attr-item .text { + padding: 0 0 5px 5px; +} +.attr2 { + display: flex; +} +.attr2 > div { + flex: 1; +} +.tips { + position: absolute; + font-size: 26px; + letter-spacing: 6px; + left: 0px; + right: 0px; + top: 150px; + width: 500px; + margin: auto; + color: #c5c5c5; +} +.col-line { + line-height: 25px; + font-weight: bold; + border-bottom: 1px solid rgb(218 218 218); +} +.drag-container ::v-deep(.el-col) { + width: 100%; +} +.drag-center ::v-deep(.el-form-item__label) { + line-height: 0 !important; +} +.drag-center ::v-deep(.el-scrollbar__wrap) { + overflow-x: hidden; +} +.drag-center ::v-deep(.el-form-item) { + margin-bottom: 10px; +} +.drag-center ::v-deep(.el-date-editor) { + width: 100%; +} +.drag-center ::v-deep(.el-checkbox) { + margin-right: 15px; +} +.drag-center ::v-deep(.el-checkbox__label) { + padding-left: 5px; +} +.drag-center ::v-deep(.hello > div) { + z-index: 500 !important; +} +.drag-center ::v-deep(th), +.drag-center ::v-deep(td) { + padding: 6px 0; +} +.example { + margin-top: 8px; + > div { + cursor: pointer; + padding: 14px 20px; + border-top: 1px solid #eee; + font-size: 13px; + color: #646465; + position: relative; + } + > div:hover { + background: rgb(231, 231, 231); + } + i { + position: absolute; + right: 20px; + } +} +.btns { + padding: 8px 0; + display: flex; + > div { + flex: 1; + } + .btns-left { + padding-top: 8px; + color: black; + font-weight: bold; + } + .btns-right { + text-align: right; + } +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormPreview.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormPreview.vue" new file mode 100644 index 0000000..7d8aa93 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormPreview.vue" @@ -0,0 +1,206 @@ +<template> + <div style="padding: 15px 20px 15px 5px"> + <div class="pre-text">{{ text }}</div> + <vol-form + ref="form" + :labelWidth="80" + :load-key="false" + :formFields="options.fields" + :formRules="options.formOptions" + > + </vol-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> + <vol-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" + ></vol-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> + <vol-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" + ></vol-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" + >涓嬭浇浠g爜</el-button + > + </div> + </div> +</template> + +<script> +import VolTable from "./../VolTable"; +import VolBox from "./../VolBox"; +import VolForm from "./../VolForm"; +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: { + "vol-table": VolTable, + "vol-box": VolBox, + "vol-form": VolForm, + }, +}; + +VolForm; +</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> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/formTemplate.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/formTemplate.js" new file mode 100644 index 0000000..e8dca27 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/formTemplate.js" @@ -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 } diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js" new file mode 100644 index 0000000..9872583 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js" @@ -0,0 +1,3 @@ +import VolFormDraggable from './VolFormDraggable' + + export default VolFormDraggable; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/options.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/options.js" new file mode 100644 index 0000000..c60047c --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/options.js" @@ -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 } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/templateCode.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/templateCode.js" new file mode 100644 index 0000000..41b8744 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/templateCode.js" @@ -0,0 +1,95 @@ +var code = `<template> +<div style="padding: 15px 20px 15px 5px"> + <div class="pre-text">{{ text }}</div> + <vol-form ref="form" + :labelWidth="80" + :load-key="false" + :formFields="fields" + :formRules="formOptions"> + </vol-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銆佹柊寤轰竴涓獀ue椤甸潰锛屾妸姝ら〉闈㈠唴瀹瑰鍒惰繘鍘� +// 2銆乺outer->index.js閰嶇疆璺敱锛岄〉闈笂杈撳叆鍦板潃鍗冲彲鐪嬪埌鏁版嵁(涔熷彲浠ユ妸鑿滃崟閰嶇疆涓�) +// 3銆佹垨鑰呭弬鐓ц〃鍗曡璁¢〉闈㈠仛鍔ㄦ�侀〉闈� +//**琛ㄥ崟璁捐鍣ㄧ殑table涓嬭浇杩樺湪寮�鍙戜腑 +{import_VolTable} +import VolForm from '@/components/basic/VolForm' +export default { + components: {"vol-form": VolForm,{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") + } + } +}; + +VolForm; +</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 \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolHeader.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolHeader.vue" new file mode 100644 index 0000000..cbe64e7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolHeader.vue" @@ -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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable.vue" new file mode 100644 index 0000000..af51360 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable.vue" @@ -0,0 +1,1873 @@ +<template> + <!-- 2021.11.18绉婚櫎voltable鏂规硶@cell-mouse-leave="rowEndEdit" --> + <div + class="vol-table" + :class="[ + textInline ? 'text-inline' : '', + fxRight ? 'fx-right' : '', + isChrome ? 'chrome' : '', + ]" + > + <div class="mask" v-show="loading"></div> + <div class="message" v-show="loading">鍔犺浇涓�.....</div> + <el-table + :show-summary="summary" + :summary-method="getSummaryData" + :row-key="rowKey" + :key="randomTableKey" + lazy + stripe + :load="loadTreeChildren" + @select="userSelect" + @select-all="userSelect" + @selection-change="selectionChange" + @row-dblclick="rowDbClick" + @row-click="rowClick" + @header-click="headerClick" + :highlight-current-row="highlightCurrentRow" + ref="table" + class="v-table" + @sort-change="sortChange" + tooltip-effect="dark" + :height="realHeight" + :max-height="realMaxHeight" + :data="url ? rowData : tableData" + border + :row-class-name="initIndex" + :cell-style="getCellStyle" + style="width: 100%" + :scrollbar-always-on="true" + > + <el-table-column + v-if="ck" + type="selection" + :fixed="fixed" + :selectable="selectable" + width="55" + ></el-table-column> + <el-table-column + v-if="columnIndex" + type="index" + :fixed="fixed" + width="55" + ></el-table-column> + + <!-- 2020.10.10绉婚櫎table绗竴琛屽己鍒舵帓搴� --> + <el-table-column + v-for="(column, cindex) in filterColumns" + :prop="column.field" + :label="column.title" + :min-width="column.width" + :formatter="formatter" + :fixed="column.fixed" + :key="column.field + cindex" + :align="column.align" + :sortable="column.sort ? 'custom' : false" + :show-overflow-tooltip="true" + > + <template #header> + <span + v-if="(column.require || column.required) && column.edit" + class="column-required" + >*</span + >{{ column.title }} + </template> + + <template #default="scope"> + <!-- 2022.01.08澧炲姞澶氳〃澶达紝鐜板湪鍙敮鎸佸父鐢ㄥ姛鑳芥覆鏌擄紝涓嶆敮鎸佺紪杈戝姛鑳�(娑夊強鍒扮粍浠堕噸鍐�) --> + <el-table-column + style="border: none" + v-for="columnChildren in filterChildrenColumn(column.children)" + :key="columnChildren.field" + :min-width="columnChildren.width" + :class-name="columnChildren.class" + :prop="columnChildren.field" + :align="columnChildren.align" + :label="columnChildren.title" + > + <template #default="scopeChildren"> + <a + href="javascript:void(0)" + style="text-decoration: none" + @click="link(scopeChildren.row, columnChildren, $event)" + v-if="column.link" + v-text="scopeChildren.row[columnChildren.field]" + ></a> + <div + v-else-if="columnChildren.formatter" + @click=" + columnChildren.click && + columnChildren.click( + scopeChildren.row, + columnChildren, + scopeChildren.$index + ) + " + v-html=" + columnChildren.formatter( + scopeChildren.row, + columnChildren, + scopeChildren.$index + ) + " + ></div> + <div v-else-if="column.bind"> + {{ formatter(scopeChildren.row, columnChildren, true) }} + </div> + <span v-else-if="column.type == 'date'">{{ + formatterDate(scopeChildren.row, columnChildren) + }}</span> + <template v-else> + {{ scopeChildren.row[columnChildren.field] }} + </template> + </template> + </el-table-column> + <!-- 2020.06.18澧炲姞render娓叉煋鑷畾涔夊唴瀹� --> + <table-render + v-if="column.render && typeof column.render == 'function'" + :row="scope.row" + key="rd-01" + :index="scope.$index" + :column="column" + :render="column.render" + ></table-render> + <!-- 鍚敤鍙屽嚮缂栬緫鍔熻兘锛屽甫缂栬緫鍔熻兘鐨勪笉浼氭覆鏌撲笅鎷夋鏂囨湰鑳屾櫙棰滆壊 --> + <!-- @click="rowBeginEdit(scope.$index,cindex)" --> + <!-- 2021.09.21澧炲姞缂栬緫鏃跺readonly灞炴�у垽鏂� --> + <template + v-else-if=" + column.edit && + !column.readonly && + ['file', 'img', 'excel'].indexOf(column.edit.type) != -1 + " + > + <div style="display: flex; align-items: center" @click.stop> + <i + style=" + padding: 3px; + margin-right: 10px; + color: #8f9293; + cursor: pointer; + " + @click="showUpload(scope.row, column)" + class="el-icon-upload" + ></i> + <img + v-show="column.edit.type == 'img'" + v-for="(file, imgIndex) in getFilePath( + scope.row[column.field], + column + )" + :key="imgIndex" + :onerror="defaultImg" + @click="viewImg(scope.row, column, file.path, $event)" + class="table-img" + :src="file.path" + /> + <a + style="margin-right: 8px" + v-show="column.edit.type != 'img'" + class="t-file" + v-for="(file, fIndex) in getFilePath( + scope.row[column.field], + column + )" + :key="fIndex" + @click="dowloadFile(file)" + >{{ file.name }}</a + > + </div> + </template> + <div + v-else-if=" + column.edit && + !column.readonly && + (column.edit.keep || edit.rowIndex == scope.$index) + " + class="edit-el" + > + <div @click.stop class="e-item"> + <div> + <!-- 2020.07.24澧炲姞鏃ユ湡onChange浜嬩欢 --> + <el-date-picker + clearable + size="default" + style="width: 100%" + v-if="['date', 'datetime'].indexOf(column.edit.type) != -1" + v-model="scope.row[column.field]" + @change=" + (val) => { + column.onChange && + column.onChange(scope.row, column, val); + } + " + :type="column.edit.type" + :placeholder="column.placeholder || column.title" + :disabledDate="(val) => getDateOptions(val, column)" + :value-format="getDateFormat(column)" + :disabled="initColumnDisabled(scope.row, column)" + > + </el-date-picker> + <el-time-picker + clearable + size="default" + style="width: 100%" + v-else-if="column.edit.type == 'time'" + v-model="scope.row[column.field]" + @change=" + (val) => { + column.onChange && + column.onChange(scope.row, column, val); + } + " + :placeholder="column.placeholder || column.title" + :value-format="column.format || 'HH:mm:ss'" + :disabled="initColumnDisabled(scope.row, column)" + > + </el-time-picker> + <el-switch + v-else-if="column.edit.type == 'switch'" + v-model="scope.row[column.field]" + active-color="#0f84ff" + inactive-color="rgb(194 194 194)" + @change=" + (val) => { + switchChange(val, scope.row, column); + } + " + :active-value=" + typeof scope.row[column.field] == 'boolean' + ? true + : typeof scope.row[column.field] == 'string' + ? '1' + : 1 + " + :inactive-value=" + typeof scope.row[column.field] == 'boolean' + ? false + : typeof scope.row[column.field] == 'string' + ? '0' + : 0 + " + :disabled="initColumnDisabled(scope.row, column)" + > + </el-switch> + <template + v-else-if=" + ['select', 'selectList'].indexOf(column.edit.type) != -1 + " + > + <el-select-v2 + style="width: 100%" + :size="size" + v-if="column.bind.data.length >= select2Count" + v-model="scope.row[column.field]" + filterable + :multiple="column.edit.type == 'select' ? false : true" + :placeholder="column.placeholder || column.title" + :autocomplete="column.autocomplete" + :options="column.bind.data" + @change=" + column.onChange && column.onChange(scope.row, column) + " + clearable + :disabled="initColumnDisabled(scope.row, column)" + > + <template #default="{ item }"> + {{ item.label }} + </template> + </el-select-v2> + + <el-select + size="default" + style="width: 100%" + v-else + v-model="scope.row[column.field]" + :filterable=" + column.filter || column.bind.data.length > 10 + ? true + : false + " + :multiple="column.edit.type == 'select'" + :placeholder="column.placeholder || column.title" + :autocomplete="column.autocomplete" + @change=" + column.onChange && column.onChange(scope.row, column) + " + clearable + :disabled="initColumnDisabled(scope.row, column)" + > + <el-option + v-for="item in column.bind.data" + :key="item.key" + v-show="!item.hidden" + :disabled="item.disabled" + :label="item.value" + :value="item.key" + >{{ item.value }} + </el-option> + </el-select> + </template> + <el-input + v-else-if="column.edit.type == 'textarea'" + type="textarea" + :placeholder="column.placeholder || column.title" + v-model="scope.row[column.field]" + :disabled="initColumnDisabled(scope.row, column)" + > + </el-input> + <input + class="table-input" + v-else-if="!column.summary && !column.onKeyPress" + v-model.lazy="scope.row[column.field]" + :disabled="initColumnDisabled(scope.row, column)" + /> + <el-input + v-else + @change="inputKeyPress(scope.row, column, $event)" + @input="inputKeyPress(scope.row, column, $event)" + @keyup.enter="inputKeyPress(scope.row, column, $event)" + size="default" + v-model="scope.row[column.field]" + :placeholder="column.placeholder || column.title" + :disabled="initColumnDisabled(scope.row, column)" + ></el-input> + </div> + <div + class="extra" + v-if="column.extra && edit.rowIndex == scope.$index" + > + <a + :style="column.extra.style" + style="text-decoration: none" + @click="extraClick(scope.row, column)" + > + <i v-if="column.extra.icon" :class="[column.extra.icon]" /> + {{ column.extra.text }} + </a> + </div> + </div> + </div> + <!--娌℃湁缂栬緫鍔熻兘鐨勭洿鎺ユ覆鏌撴爣绛�--> + <template v-else> + <a + href="javascript:void(0)" + style="text-decoration: none" + @click="link(scope.row, column, $event)" + v-if="column.link" + v-text="scope.row[column.field]" + ></a> + <img + v-else-if="column.type == 'img'" + v-for="(file, imgIndex) in getFilePath( + scope.row[column.field], + column + )" + :key="imgIndex" + :onerror="defaultImg" + @click="viewImg(scope.row, column, file.path, $event)" + class="table-img" + :src="file.path" + /> + <a + style="margin-right: 8px" + v-else-if="column.type == 'file' || column.type == 'excel'" + class="t-file" + v-for="(file, fIndex) in getFilePath( + scope.row[column.field], + column + )" + :key="fIndex" + @click="dowloadFile(file)" + >{{ file.name }}</a + > + <span v-else-if="column.type == 'date'">{{ + formatterDate(scope.row, column) + }}</span> + <div + v-else-if="column.formatter" + @click="formatterClick(scope.row, column, $event)" + v-html="column.formatter(scope.row, column)" + ></div> + <!-- 2021.11.18淇table鏁版嵁婧愯缃负normal鍚庣偣鍑昏$event缂哄け鐨勯棶棰� --> + <div + v-else-if="column.bind && (column.normal || column.edit)" + @click="formatterClick(scope.row, column, $event)" + :style="column.getStyle && column.getStyle(scope.row, column)" + > + {{ formatter(scope.row, column, true) }} + </div> + <div + v-else-if="column.click && !column.bind" + @click="formatterClick(scope.row, column)" + > + {{ scope.row[column.field] }} + </div> + <div + @click=" + () => { + column.click && formatterClick(scope.row, column); + } + " + v-else-if="column.bind" + > + <el-tag + v-if="useTag" + :class="[isEmptyTag(scope.row, column)]" + :type="getColor(scope.row, column)" + :effect="column.effect" + >{{ formatter(scope.row, column, true) }}</el-tag + > + <template v-else>{{ + formatter(scope.row, column, true) + }}</template> + </div> + + <span v-else>{{ formatter(scope.row, column, true) }}</span> + </template> + </template> + </el-table-column> + </el-table> + <template v-if="!paginationHide"> + <div class="block pagination" key="pagination-01" style="display: flex"> + <div style="flex: 1"></div> + <el-pagination + key="pagination-02" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + :current-page="paginations.page" + :page-sizes="paginations.sizes" + :page-size="paginations.size" + layout="total, sizes, prev, pager, next, jumper" + :total="paginations.total" + ></el-pagination> + </div> + </template> + </div> + + <VolBox + v-model="uploadModel" + title="涓婁紶" + :height="228" + :width="500" + :padding="15" + lazy + > + <!-- 涓婁紶鍥剧墖銆乪xcel鎴栧叾浠栨枃浠躲�佹枃浠舵暟閲忋�佸ぇ灏忛檺鍒堕兘鍙互锛屽弬鐓olupload缁勪欢api --> + <div style="height: 200px; display: flex; align-items: center"> + <VolUpload + style="text-align: center" + :autoUpload="currentColumn.edit.autoUpload" + :multiple="currentColumn.edit.multiple" + :url="uploadUrl" + :max-file="currentColumn.edit.maxFile" + :img="currentColumn.edit.type == 'img'" + :excel="currentColumn.edit.type == 'excel'" + :fileTypes=" + currentColumn.edit.fileTypes ? currentColumn.edit.fileTypes : [] + " + :fileInfo="fileInfo" + :upload-after="uploadAfter" + > + <div>{{ currentColumn.message }}</div> + </VolUpload> + </div> + <template #footer> + <div style="text-align: center"> + <el-button type="default" size="small" @click="uploadModel = false" + >鍏抽棴</el-button + > + <el-button type="primary" size="small" @click="saveUpload" + >淇濆瓨</el-button + > + </div> + </template> + </VolBox> +</template> +<script> +import VolTableRender from "./VolTable/VolTableRender"; +let _errMsg; +import { defineComponent, defineAsyncComponent } from "vue"; +export default defineComponent({ + //https://github.com/element-plus/element-plus/issues/1483 + //娌℃湁鍘熷厛鐨剆election灞炴�т簡锛岀湅issue涓婁娇鐢╯elect/selectall鑾峰彇 + //鐩戝惉鏁扮粍闀垮害锛屽鏋滃垹闄や簡鏁版嵁锛岀幇鍦ㄥ彧鑳借杩竻闄ゆ墍鏈夐�変腑鐨勮 + watch: { + "tableData.length": { + handler(newLen, oldLen) { + this.watchRowSelectChange(newLen, oldLen); + }, + }, + "rowData.length": { + handler(newLen, oldLen) { + this.watchRowSelectChange(newLen, oldLen); + }, + }, + }, + components: { + "table-render": VolTableRender, + VolUpload: defineAsyncComponent(() => + import("@/components/basic/VolUpload.vue") + ), + VolBox: defineAsyncComponent(() => import("@/components/basic/VolBox.vue")), + }, + props: { + rowKey: { + // 鏍戝舰缁撴瀯鐨勪富閿瓧娈碉紝濡傛灉璁剧疆鍊奸粯璁や細寮�鍚爲褰able锛涙敞鎰弐owKey瀛楁鐨勫�煎繀椤绘槸鍞竴锛�2021.05.02锛� + typeof: String, + default: undefined, + }, + loadTreeChildren: { + // 鏍戝舰缁撴瀯鍔犺浇瀛愯妭鐐� + type: Function, + default: (tree, treeNode, resolve) => { + return resolve([]); + }, + }, + textInline: { + // 琛ㄦ牸鍐呭瓒呭嚭鍚庢槸鍚︽崲琛屾樉绀猴紙2020.01.16锛� + type: Boolean, + default: true, + }, + tableData: { + // 琛ㄦ暟鎹簮,閰嶇疆浜唘rl灏变笉鐢ㄤ紶杩欎釜鍙傛暟浜� + type: Array, + default: () => { + return []; + }, + }, + columns: { + type: Array, + default: [], + }, + height: { + type: Number, + default: 0, + }, + maxHeight: { + type: Number, + default: 0, + }, + linkView: { + type: Function, + default: function () { + return 1; + }, + }, + pagination: { + type: Object, + default: function () { + return { total: 0, size: 30, sortName: "" }; + }, + }, + url: { + type: String, + default: "", + }, + paginationHide: { + type: Boolean, + default: true, + }, + color: { + type: Boolean, + default: true, + }, + index: { + // 鏄惁鍒涘缓绱㈠紩鍙�,濡傛灉闇�瑕佽〃鏍肩紪杈戝姛鑳斤紝杩欓噷闇�瑕佽缃负true + type: Boolean, + default: false, + }, + allowEmpty: { + // 琛ㄦ牸鏁版嵁涓虹┖鏃舵槸鍚﹂粯璁や负-- + type: Boolean, + default: true, + }, + defaultLoadPage: { + // 浼犲叆浜唘rl锛屾槸鍚﹂粯璁ゅ姞杞借〃鏍兼暟鎹� + type: Boolean, + default: true, + }, + loadKey: { + // 鏄惁鑷姩浠庡悗鍙板姞杞芥暟鎹簮 + type: Boolean, + default: true, + }, + single: { + type: Boolean, // 鏄惁鍗曢�� + default: false, + }, + doubleEdit: { + type: Boolean, // 鏄惁鍙屽嚮鍚敤缂栬緫鍔熻兘 + default: true, + }, + beginEdit: { + // 缂栬緫寮�濮� + type: Function, + default: function (row, column, index) { + return true; + }, + }, + endEditBefore: { + // 缁撴潫缂栬緫鍓� + type: Function, + default: function (row, column, index) { + return true; + }, + }, + endEditAfter: { + // 缁撴潫缂栬緫鍓� + type: Function, + default: function (row, column, index) { + return true; + }, + }, + ck: { + // 鏄惁鏄剧ずcheckbox + type: Boolean, + default: true, + }, + columnIndex: { + // 鏄惁鏄剧ず琛屽彿(2020..11.1) + type: Boolean, + default: true, + }, + highlightCurrentRow: { + //澧炲姞閫変腑琛岄珮浜樉绀�(2022.10.07) + type: Boolean, + default: true, + }, + select2Count: { + //瓒呭嚭鏁伴噺鏄剧ずselect2缁勪欢 + type: Number, + default: 500, + }, + selectable: { + type: Function, + default: (row, index) => { + return true; + }, + }, + }, + data() { + return { + fixed: false, //鏄浐瀹氳鍙蜂笌checkbox + clickEdit: true, //2021.07.17璁剧疆涓虹偣鍑昏缁撴潫缂栬緫 + randomTableKey: 1, + visiblyColumns: [], + key: "", + realHeight: 0, + realMaxHeight: 0, + enableEdit: false, // 鏄惁鍚〃鏍肩敤缂栬緫鍔熻兘 + empty: this.allowEmpty ? "" : "--", + defaultImg: 'this.src="' + require("@/assets/imgs/error.png") + '"', + loading: false, + footer: {}, + total: 0, + formatConfig: {}, + // defaultColor: "", + // 2020.09.06璋冩暣table鍒楁暟鎹簮鐨勮儗鏅鑹� + colors: ["", "warning", "success", "danger", "info"], + rule: { + phone: /^[1][3,4,5,6,7,8,9][0-9]{9}$/, + decimal: /(^[\-0-9][0-9]*(.[0-9]+)?)$/, + number: /(^[\-0-9][0-9]*([0-9]+)?)$/, + }, + columnNames: [], + rowData: [], + paginations: { + sort: "", + order: "desc", + Foots: "", + total: 0, + // 2020.08.29澧炲姞鑷畾涔夊垎椤垫潯澶у皬 + sizes: [30, 60, 100, 120], + size: 30, // 榛樿鍒嗛〉澶у皬 + Wheres: [], + page: 1, + rows: 30, + }, + errorFiled: "", + edit: { columnIndex: -1, rowIndex: -1 }, // 褰撳墠鍙屽嚮缂栬緫鐨勮涓庡垪鍧愭爣 + editStatus: {}, + summary: false, // 鏄惁鏄剧ず鍚堣 + // 鐩墠鍙敮鎸佷粠鍚庡彴杩斿洖鐨剆ummaryData鏁版嵁 + summaryData: [], + summaryIndex: {}, + remoteColumns: [], // 闇�瑕佹瘡娆″埛鏂版垨鍒嗛〉鍚庝粠鍚庡彴鍔犺浇瀛楀吀鏁版嵁婧愮殑鍒楅厤缃� + cellStyleColumns: {}, // 鏈夎儗鏅鑹茬殑閰嶇疆 + fxRight: false, //鏄惁鏈夊彸杈瑰浐瀹氳〃澶� + selectRows: [], //褰撳墠閫変腑鐨勮 + isChrome: false, + //vol-table甯︽暟鎹簮鐨勫崟鍏冩牸鏄惁鍚敤tag鏍囩(涓嬫媺妗嗙瓑鍗曞厓鏍间互tag鏍囩鏄剧ず) + //2023.04.02鏇存柊voltable涓巑ain.js + useTag: true, + currentRow: {}, + currentColumn: [], + fileInfo: [], + uploadUrl: "", + uploadModel: false, + }; + }, + created() { + try { + this.useTag = this.$global.table.useTag; + } catch (error) { + console.log(error.message); + } + //2021.06.19鍒ゆ柇璋锋瓕鍐呮牳娴忚閲嶆柊璁$畻table楂樺害 + // if ( + // navigator.userAgent.indexOf('Chrome') != -1 || + // navigator.userAgent.indexOf('Edge') != -1 + // ) { + // this.isChrome = true; + // } + this.realHeight = this.getHeight(); + this.realMaxHeight = this.getMaxHeight(); + this.fxRight = this.columns.some((x) => { + return x.fixed == "right"; + }); + //2021.09.21绉婚櫎寮哄埗鍥哄畾琛屽彿涓巆heckbox鍒� + if ( + this.columns.some((x) => { + return x.fixed && x.fixed != "right"; + }) + ) { + this.fixed = true; + } + //2022.04.06浼樺寲table鍚堣鍥哄畾鍒楁樉绀� + // if ( + // this.columns.some((x) => { + // return x.summary; + // }) + // ) { + // this.columns.forEach((x) => { + // if (x.fixed && x.fixed != 'right') { + // x.fixed = false; + // } + // }); + // this.fixed = false; + // } + + // 浠庡悗鍙板姞涓嬫媺妗嗙殑[鏄惁鍚敤鐨刔鏁版嵁婧� + let keys = []; + let columnBind = []; + this.summaryData.push("鍚堣"); + if (this.columnIndex) { + this.summaryData.push(" "); + } + this.columns.forEach((x, _index) => { + if (x.cellStyle) { + this.cellStyleColumns[x.field] = x.cellStyle; + } + if (!x.hidden) { + // this.summaryIndex[x.field] = _index; + // 2020.10.11淇姹傚拰鍒楅敊浣嶇殑闂 + this.summaryData.push(""); + this.summaryIndex[x.field] = this.summaryData.length - 1; + } + // 姹傚拰 + if (x.summary && !this.summary) { + this.summary = true; + } + if (x.bind && x.bind.key && (!x.bind.data || x.bind.data.length == 0)) { + // 鍐欏叆杩滅▼ + if (!x.bind.data) x.bind.data = []; + if (x.bind.remote) { + this.remoteColumns.push(x); + } else if (this.loadKey) { + keys.push(x.bind.key); + x.bind.valueTyoe = x.type; + columnBind.push(x.bind); + } + } + }); + if (keys.length > 0) { + this.http + .post("/api/Sys_Dictionary/GetVueDictionary", keys) + .then((dic) => { + dic.forEach((x) => { + if (x.data.length > this.select2Count) { + x.data.forEach((item) => { + item.label = item.value; + item.value = item.key; + }); + } + columnBind.forEach((c) => { + // 杞崲鏁版嵁婧愮殑绫诲瀷涓庡垪鐨勭被鍨嬩竴鑷�(2020.04.04) + if ( + c.key == x.dicNo && + (c.valueTyoe == "int" || c.valueTyoe == "sbyte") + ) { + x.data.forEach((d) => { + // 2020.09.01澧炲姞瀵规暟瀛楃被鍨嬬殑浜屾鍒ゆ柇 + if (!isNaN(d.key)) { + d.key = ~~d.key; + } + }); + } + if (c.key == x.dicNo) c.data.push(...x.data); + }); + }); + }); + } + + this.paginations.sort = this.pagination.sortName; + // 2020.08.29澧炲姞鑷畾涔夊垎椤垫潯澶у皬 + Object.assign(this.paginations, this.pagination); + if (this.pagination.size) { + this.paginations.rows = this.pagination.size; + } + this.enableEdit = this.columns.some((x) => { + return x.hasOwnProperty("edit"); + }); + let keyColumn = this.columns.find((x) => { + return x.isKey; + }); + if (keyColumn) { + this.key = keyColumn.field; + } + this.defaultLoadPage && this.load(); + }, + computed: { + filterColumns() { + return this.columns.filter((x, index) => { + if (!x.field) { + x.field = x.title + index; + } + return !x.hidden; + }); + }, + }, + methods: { + watchRowSelectChange(newLen, oldLen) { + if (newLen < oldLen && this.selectRows.length) { + this.selectRows = []; + this.$refs.table.clearSelection(); + } + }, + switchChange(val, row, column) { + //杩欓噷鍦ㄥ垵濮嬪寲鐨勬椂鍊欎篃浼氳Е鍙慶hange浜嬩欢 + if (Object.keys(row).length <= 1) { + return; + } + if (column.onChange) { + column.onChange(val, row, column); + } + }, + inputKeyPress(row, column, $event, $e) { + column.onKeyPress && column.onKeyPress(row, column, $event); + this.getInputSummaries(null, null, $event, column); + }, + extraClick(row, column) { + column.extra.click && + column.extra.click( + row, + column, + this.url ? this.rowData : this.tableData + ); + }, + headerClick(column, event) { + if (this.clickEdit && this.edit.rowIndex != -1) { + if ( + this.rowEndEdit( + this.url + ? this.rowData[this.edit.rowIndex] + : this.tableData[this.edit.rowIndex], + column + ) + ) { + this.edit.rowIndex = -1; + } + } + // this.edit.rowIndex = -1; + }, + rowDbClick(row, column, event) { + //2021.05.23澧炲姞鍙屽嚮琛屼簨浠� + this.$emit("rowDbClick", { row, column, event }); + }, + rowClick(row, column, event) { + //2022.02.20澧炲姞鐐瑰嚮鏃惰〃鏍煎弬鏁板垽鏂� + if (!column) { + return; + } + //姝e湪缂栬緫鏃讹紝绂佹鍑哄彂rowClick浜嬩欢 + if (this.edit.rowIndex == -1) { + this.$emit("rowClick", { row, column, event }); + } + // 鐐瑰嚮琛屼簨浠�(2020.11.07) + + if (!this.doubleEdit) { + return; + } + // 鐐瑰嚮鍏朵粬琛屾椂锛屽鏋滅偣鍑荤殑琛屼笌姝e湪缂栬緫鐨勮鐩稿悓锛屼繚鎸佺紪杈戠姸鎬� + if (this.clickEdit && this.edit.rowIndex != -1) { + if (row.elementIndex == this.edit.rowIndex) { + // 鐐瑰嚮鐨勫崟鍏冩牸濡傛灉涓嶅彲浠ョ紪杈戯紝鐩存帴缁撴潫缂栬緫 + // 2020.10.12淇缁撴潫缂栬緫鏃讹紝element table楂樼増鏈睘鎬ц幏鍙栦笉鍒扮殑闂 + let _col = this.columns.find((x) => { + return x.field == ((event && event.property) || column.property); + }); + if (_col && (!_col.edit || _col.readonly)) { + if (this.rowEndEdit(row, event)) { + this.edit.rowIndex = -1; + } + } + return; + } + if (this.rowEndEdit(row, event && event.property ? event : column)) { + this.edit.rowIndex = -1; + } + //褰撴鍦ㄧ紪杈戯紝涓旂偣鍑诲埌鍏朵粬琛屾椂锛屽湪鍘熺紪杈戠殑琛岀粨鏉熺紪杈戝悗锛岃Е鍙戞柊琛岀殑rowClick浜嬩欢 + //姝e湪缂栬緫鏃讹紝绂佹鍑哄彂rowClick浜嬩欢 + if (this.edit.rowIndex == -1) { + this.$emit("rowClick", { row, column, event }); + } + } + this.rowBeginEdit(row, column); + }, + dowloadFile(file) { + this.base.dowloadFile( + file.path, + file.name, + { + Authorization: this.$store.getters.getToken(), + }, + this.http.ipAddress + ); + }, + getFilePath(pathSring, column) { + // 鑾峰彇琛ㄧ殑鍥剧墖涓庢枃浠舵樉绀� + if (!pathSring) return []; + // 澧炲姞鍥剧墖鑷畾涔夋搷浣� + // 杩斿洖鏍煎紡蹇呴』鏄痆{name:"鏂囦欢鍚�",path:"鍥剧墖鍏ㄨ矾寰勬垨base64鏍煎紡"}] + if (column.formatter) { + return column.formatter(pathSring); + } + let filePath; + if (column.base64 && pathSring.indexOf("data") != -1) { + filePath = ("," + pathSring) + .split(",data") + .filter((x) => { + return x; + }) + .map((m) => { + return "data" + m; + }); + } else { + filePath = pathSring.replace(/\\/g, "/").split(","); + } + + let fileInfo = []; + for (let index = 0; index < filePath.length; index++) { + let file = filePath[index]; + // 2020.12.19澧炲姞base64鍥剧墖鏄剧ず + if (column.base64) { + fileInfo.push({ + name: "", + path: + (file.indexOf("data") == -1 ? "data:image/png;base64," : "") + + file, + }); + } else if (file.indexOf(".") != -1) { + let splitFile = file.split("/"); + if (splitFile.length > 0) { + fileInfo.push({ + name: splitFile[splitFile.length - 1], + path: this.base.isUrl(file) ? file : this.http.ipAddress + file, + }); + } + } + } + return fileInfo; + }, + // 閲嶇疆table + reset() { + if (this.tableData && this.tableData.length > 0) { + this.tableData.splice(0); + } + if (this.rowData && this.rowData.length > 0) { + this.rowData.splice(0); + } + if (!this.paginationHide) { + this.paginations.page = 1; + // this.paginations.rows = 30; + if (this.paginations.wheres && this.paginations.wheres.length > 0) { + this.paginations.wheres.splice(0); + } + } + this.errorFiled = ""; + this.edit.columnIndex = -1; + this.edit.rowIndex = -1; + }, + getHeight() { + // 娌℃湁瀹氫箟楂樺害涓庢渶澶ч珮搴︼紝浣跨敤table榛樿鍊� + if (!this.height && !this.maxHeight) { + return null; + } + // 瀹氫箟浜嗘渶澶ч珮搴﹀垯涓嶄娇鐢ㄩ珮搴� + if (this.maxHeight) { + return null; + } + // 浣跨敤褰撳墠瀹氫箟鐨勯珮搴� + return this.height; + }, + getMaxHeight() { + // 娌℃湁瀹氫箟楂樺害涓庢渶澶ч珮搴︼紝浣跨敤table榛樿鍊� + if (!this.height && !this.maxHeight) { + return null; + } + // 瀹氫箟浜嗘渶澶ч珮搴︿娇鐢ㄦ渶澶ч珮搴� + if (this.maxHeight) { + return this.maxHeight; + } + // 涓嶄娇鐢ㄦ渶澶ч珮搴� + return null; + }, + getSelectedOptions(column) { + if (column.bind && column.bind.data && column.bind.data.length > 0) { + return column.bind.data; + } + return []; + }, + formatterClick(row, column, event) { + if (column.click) { + column.click(row, column, event); + event.stopPropagation && event.stopPropagation(); + } else { + this.rowClick(row, column, event); + } + }, + initIndex({ row, rowIndex }) { + if (this.index) { + row.elementIndex = rowIndex; + } + // if (rowIndex%2!==0) { + // return "even-row"; + // } + return; + }, + toggleEdit(event) {}, + setEditStatus(status) { + // this.columns.forEach((x) => { + // if (x.hasOwnProperty("edit")) { + // this.$set(x.edit, "status", status); + // } + // }); + }, + // 閫氳繃button鎸夐挳鍚敤缂栬緫 + beginWithButtonEdit(scope) { + // url?rowData:tableData + this.rowBeginEdit(scope.row, this.columns[scope.$index]); + }, + rowBeginEdit(row, column) { + if (this.edit.rowIndex != -1) { + return; + } + let _row = this.columns.find((x) => x.field == column.property); + if (_row) { + if (_row.readonly) { + return; + } + if ( + //涓嶈兘缂栬緫鐨勫瓧娈点�乻witch锛岀偣鍑讳笉寮�鍚惎缂栬緫鍔熻兘 + !_row.edit || + (_row.edit.keep && _row.edit.type == "switch") + ) { + return; + } + } + if (!this.enableEdit) return; + _errMsg = ""; + // 缂栬緫鍓� + this.columns + .filter((x) => { + return x.bind && x.bind.data && x.bind.data.length; + }) + .forEach((column) => { + let val = row[column.field]; + if (typeof column.bind.data[0].key == "string") { + if (typeof val == "number") { + row[column.field] = row[column.field] + ""; + } + } else { + if (typeof val == "string" && val) { + let _val = val * 1; + if (_val + "" === val) { + row[column.field] = _val; + } + } + } + }); + if (!this.beginEdit(row, column, row.elementIndex)) return; + if (row.hasOwnProperty("elementIndex")) { + if (this.edit.rowIndex == row.elementIndex) { + return; + } + this.edit.rowIndex = row.elementIndex; + } + }, + rowEndEdit(row, column, event) { + if (this.clickEdit && event) { + return true; + } + if (!this.enableEdit) { + if (!this.errorFiled) { + if ( + this.edit.rowIndex != -1 && + !this.endEditAfter(row, column, this.edit.rowIndex) + ) { + return false; + } + this.edit.rowIndex = -1; + } + return true; + } + if (!this.doubleEdit && event) { + return true; + } + let _row = this.url + ? this.rowData[this.edit.rowIndex] + : this.tableData[this.edit.rowIndex]; + // 缁撴潫缂栬緫鍓� + if (!this.endEditBefore(_row, column, this.edit.rowIndex)) return false; + if (this.edit.rowIndex != -1) { + //2022.06.26淇琛ㄦ牸鍐呭鍒囨崲鍚庤鏁颁笉涓�鑷存椂涓嶈兘缂栬緫鐨勯棶棰� + if (this.edit.rowIndex - 1 > (this.rowData || this.tableData).length) { + this.edit.rowIndex = -1; + return; + } + let row = (this.url ? this.rowData : this.tableData)[ + this.edit.rowIndex + ]; + for (let index = 0; index < this.columns.length; index++) { + const _column = this.columns[index]; + if (_column.edit) { + if (!this.validateRow(row, _column)) { + return; + } + } + } + } + if (!this.endEditAfter(_row, column, this.edit.rowIndex)) return false; + this.edit.rowIndex = -1; + return true; + }, + validateRow(row, option1) { + if (!this.validateColum(option1, row)) { + this.errorFiled = option1.field; + // 2022.05.06 淇敼閿欒淇℃伅閲嶅鐨勯棶棰� + this.$message.error(option1.title + _errMsg); + return false; + } + this.errorFiled = ""; + return true; + }, + validateColum(option, data) { + if (option.hidden || option.bind) return true; + let val = data[option.field]; + if (option.require || option.required) { + if (val != "0" && (val === "" || val === undefined)) { + if (!this.errorFiled) { + _errMsg = "涓嶈兘涓虹┖"; + } + return false; + } + } + if (!option.edit) { + return true; + } + let editType = option.edit.type; + // 楠岃瘉鏁板瓧 + if (editType == "int" || editType == "decimal" || editType == "number") { + if (val == "" || val == undefined) return true; + if (editType == "decimal") { + if (!this.rule.decimal.test(val)) { + _errMsg = "鍙兘鏄暟瀛�"; + return false; + } + } else if (!this.rule.decimal.test(val)) { + _errMsg = "鍙兘鏄暟瀛�"; + return false; + } + if ( + option.edit.min != undefined && + typeof option.edit.min === "number" && + val < option.edit.min + ) { + _errMsg = "涓嶈兘灏忎簬" + option.edit.min; + return false; + } + if ( + option.edit.max != undefined && + typeof option.edit.max === "number" && + val > option.edit.max + ) { + _errMsg = "涓嶈兘澶т簬" + option.edit.max; + return false; + } + return true; + } + + // 楠岃瘉瀛楃涓� + if (val && (editType == "text" || editType == "string")) { + if ( + option.edit.min != undefined && + typeof option.edit.min === "number" && + val.length < option.edit.min + ) { + _errMsg = "鑷冲皯" + option.edit.min + "涓瓧绗�"; + return false; + } + if ( + option.edit.max != undefined && + typeof option.edit.max === "number" && + val.length > option.edit.max + ) { + _errMsg = "鏈�澶�" + option.edit.max + "涓瓧绗�"; + return false; + } + } + return true; + }, + delRow() { + let rows = this.getSelected(); + if (rows.length == 0) return this.$Message.error("璇烽�夋嫨瑕佸垹闄ょ殑琛�!"); + + let data = this.url ? this.rowData : this.tableData; + let indexArr = this.getSelectedIndex(); + if (indexArr.length == 0) { + return this.$Message.error( + "鍒犻櫎鎿嶄綔蹇呴』璁剧疆VolTable鐨勫睘鎬ndex='true'" + ); + } + // if (indexArr.length == 0 || !this.key) { + // return this.$message.error( + // "璇疯缃甶ndex=true灞炴�ф垨鎸嘽olumns鐨勫瓧娈典负key" + // ); + // } + if (indexArr.length == 0) { + // let keyValues=[] + // rows.forEach(x=>{ + // if (x[this.key]) { + // } + // keyValues.push(x[this.key]) + // }) + // data.find(x=>) + } else { + for (let i = data.length - 1; i >= 0; i--) { + if (indexArr.indexOf(i) != -1) { + data.splice(i, 1); + } + } + } + this.edit.rowIndex = -1; + return rows; + }, + addRow(row) { + if (!row) { + row = {}; + } + this.columns.forEach((x) => { + // 2022.05.06 娣诲姞琛屾椂锛屽鏋滃垪鏈夌紪杈戝睘鎬э紝璁剧疆寮�鍚紪杈�(閬垮厤鍏抽棴缂栬緫鍚庯紝鏃犳硶鍐嶆鍚敤缂栬緫)?? + //x.readonly = false; + if (!row.hasOwnProperty(x.field)) { + if (x.edit && x.edit.type == "switch") { + row[x.field] = x.type == "bool" ? false : 0; + } else if (!row.hidden) { + // 2020.09.06娣诲姞琛屾椂锛岃缃粯璁ゅ瓧娈� + row[x.field] = undefined; + } + } + }); + if (!this.url) { + this.tableData.push(row); + return; + } + this.rowData.push(row); + }, + viewImg(row, column, url, $event) { + $event.stopPropagation(); + this.base.previewImg(url); + // window.open(row[column.field]); + }, + link(row, column, $e) { + $e.stopPropagation(); + this.$props.linkView(row, column); + }, + getSelected() { + return this.selectRows; + }, + getSelectedIndex() { + if (!this.index) { + // 鍙湁璁剧疆浜嗗睘鎬ndex鎵嶆湁绱㈠紩琛� + return []; + } + let indexArr = this.selectRows.map((x) => { + return x.elementIndex; + }); + return indexArr || []; + }, + GetTableDictionary(rows) { + // 鍒嗛〉鎴栧埛鏂版垨閲嶆柊缁戝畾鏁版嵁婧� + if (this.remoteColumns.length == 0 || !rows || rows.length == 0) return; + let remoteInfo = {}; + for (let index = 0; index < this.remoteColumns.length; index++) { + const column = this.remoteColumns[index]; + // column.bind.data.splice(0); + let key = column.bind.key; + let data = []; + rows.forEach((row) => { + if (row[column.field] || row[column.field] == "0") { + if (data.indexOf(row[column.field]) == -1) { + data.push(row[column.field]); + } + } + }); + if (data.length > 0) { + remoteInfo[key] = data; + } + } + if (remoteInfo.length == 0) return; + // ha= Object.assign([], ha, hb) + this.http + .post("/api/Sys_Dictionary/GetTableDictionary", remoteInfo) + .then((dic) => { + dic.forEach((x) => { + this.remoteColumns.forEach((column) => { + if (column.bind.key == x.key) { + column.bind.data = Object.assign([], column.bind.data, x.data); + // column.bind.data.push(...x.data); + } + }); + }); + this.$emit("dicInited", dic); + }); + }, + load(query, isResetPage) { + // isResetPage閲嶇疆鍒嗛〉鏁版嵁 + if (!this.url) return; + if (isResetPage) { + this.resetPage(); + } + let param = { + page: this.paginations.page, + rows: this.paginations.rows, + sort: this.paginations.sort, + order: this.paginations.order, + wheres: [], // 鏌ヨ鏉′欢锛屾牸寮忎负[{ name: "瀛楁", value: "xx" }] + }; + let status = true; + // 鍚堝苟鏌ヨ淇℃伅(鍖呮煡璇㈠垎椤点�佹帓搴忋�佹煡璇㈡潯浠剁瓑) + if (query) { + param = Object.assign(param, query); + } + /* 鏌ヨ鍓嶅鐞�(濡傛灉闇�瑕佹煡璇㈡潯浠讹紝瀹炵幇缁勪欢鏂规硶loadBefore鏂规硶鍗冲彲: + loadBefore=(param, callBack)=>{ + param.wheres = [{ name: "PhoneNo", value: "13419098211" }]; + callBack(true); + }) + */ + this.$emit("loadBefore", param, (result) => { + status = result; + }); + if (!status) return; + + if (param.wheres && param.wheres instanceof Array) { + param.wheres = JSON.stringify(param.wheres); + } + this.loading = true; + this.http.post(this.url, param).then( + (data) => { + //2021.06.04淇tree涓嶅埛鏂扮殑闂 + if (this.rowKey) { + this.randomTableKey++; + this.rowData.splice(0); + } + this.loading = false; + // 鏌ヨ杩斿洖缁撴灉鍚庡鐞� + // 2020.10.30澧炲姞鏌ヨ鍚庤繑鍥炴墍鏈夌殑鏌ヨ淇℃伅 + this.$emit( + "loadAfter", + data.rows || [], + (result) => { + status = result; + }, + data + ); + if (!status) return; + this.GetTableDictionary(data.rows); + this.rowData = data.rows || []; + this.paginations.total = data.total; + // 鍚堣 + this.getSummaries(data); + // this.$nextTick(() => { + // this.$refs.table.doLayout(); + // }); + }, + (error) => { + this.loading = false; + // this.$Message.error(error || "缃戠粶寮傚父"); + } + ); + }, // 鑾峰彇缁熻 + getSummaries(data) { + if (!this.summary || !data.summary) return; + this.summaryData.splice(0); + // 寮�鍚簡琛屽彿鐨勶紝+1 + if (this.columnIndex) { + this.summaryData.push(""); + } + // 濡傛灉鏈塩heckbox锛屽簲璇ョ畻浣滄槸绗竴琛� + if (this.ck) { + this.summaryData.push(""); + } + + this.columns.forEach((col) => { + if (col.children && col.children.length) { + col.children.forEach((item) => { + this.getColumnSummaries(item, data); + }); + } else { + this.getColumnSummaries(col, data); + } + }); + if (this.summaryData.length > 0 && this.summaryData[0] == "") { + this.summaryData[0] = "鍚堣"; + } + }, + getColumnSummaries(col, data) { + if (!col.hidden) { + if (data.summary.hasOwnProperty(col.field)) { + let sum = data.summary[col.field]; + if (sum) { + sum = + (sum * 1.0).toFixed(col.numberLength || 2).replace(".00", "") * + 1.0; + } + this.summaryData.push(sum); + } else { + this.summaryData.push(""); + } + } + }, + getInputChangeSummaries() {}, + handleSizeChange(val) { + this.paginations.size = val; + this.paginations.rows = val; + this.load(); + }, + handleCurrentChange(val) { + this.paginations.page = val; + this.load(); + }, + sortChange(sort) { + this.paginations.sort = sort.prop; + this.paginations.order = sort.order == "ascending" ? "asc" : "desc"; + this.load(); + }, + resetPage() { + // 閲嶇疆鏌ヨ鍒嗛〉 + // this.paginations.rows = 30; + this.paginations.page = 1; + }, + selectionChange(selection) { + // console.log(selection); + // 閫夋嫨琛屼簨浠�,鍙湁鍗曢�夋墠瑙﹀彂 + this.selectRows = selection; + if (this.single) { + if (selection.length == 1) { + this.$emit("rowChange", selection[0]); + } + if (selection.length > 1) { + let _row = selection[selection.length - 1]; + this.$refs.table.toggleRowSelection(selection[0]); + this.selectRows = [_row]; + } + } + // 灏唖electionchange鏆撮湶鍑哄幓 + this.$emit("selectionChange", selection); + }, + getColor(row, column) { + let val = row[column.field]; + if (column.getColor && typeof column.getColor === "function") { + let _color = column.getColor(row, column); + if (_color) { + return _color; + } + } + if (!val && val != "0") { + return ""; + } + if (!this.formatConfig[column.field]) { + this.formatConfig[column.field] = [val]; + return this.colors[0]; + } + let index = this.formatConfig[column.field].indexOf(val); + if (index != -1) { + return this.colors[index]; + } + if (this.formatConfig[column.field].length > 5) { + return ""; + } + + if (index == -1) { + this.formatConfig[column.field].push(val); + index = this.formatConfig[column.field].length - 1; + } + return this.colors[index]; + }, + formatterDate(row, column) { + return (row[column.field] || "").substr(0, 10); + }, + formatter(row, column, template) { + if (!template) return row[column.property]; + let val = row[column.field]; + if (!val && val != 0) return val; + // 鏄惁鍊� + if (column.edit && column.edit.type == "switch") { + return val ? "鏄�" : "鍚�"; + } + if (!column.bind || !column.bind.data) { + return row[column.field]; + } + + if ( + column.edit && + (column.edit.type == "selectList" || column.edit.type == "treeSelect") + ) { + if (!Array.isArray(val)) { + row[column.field] = val.split(","); + } else { + val = val.join(","); + } + return this.getSelectFormatter(column, val); + } + // 缂栬緫澶氶�塼able鏄剧ず + if ( + column.bind.type == "selectList" || + column.bind.type == "checkbox" || + column.bind.type == "treeSelect" + ) { + // if (typeof val === 'string' && val.indexOf(',') != -1) { + return this.getSelectFormatter(column, val + ""); + // } + } + let source = column.bind.data.filter((x) => { + // return x.key != "" && x.key == val; + // 2020.06.06淇鍗曠嫭浣跨敤table缁勪欢鏃�,key涓烘暟瀛�0鏃惰浆鎹㈡垚鏂囨湰澶辫触鐨勯棶棰� + return x.key !== "" && x.key !== undefined && x.key + "" === val + ""; + }); + if (source && source.length > 0) val = source[0].label || source[0].value; + return val; + }, + getSelectFormatter(column, val) { + // 缂栬緫澶氶�塼able鏄剧ず + let valArr = val.split(","); + for (let index = 0; index < valArr.length; index++) { + (column.bind.orginData && column.bind.orginData.length + ? column.bind.orginData + : column.bind.data + ).forEach((x) => { + // 2020.06.06淇鏁版嵁婧愪负selectList鏃�,key涓烘暟瀛�0鏃朵笉鑳借浆鎹㈡枃鏈殑闂 + if ( + x.key !== "" && + x.key !== undefined && + x.key + "" == valArr[index] + "" + ) { + valArr[index] = x.label || x.value; + } + }); + } + return valArr.join(","); + }, + onChange(scope, val, event, column) { + // 2020.09.03淇onChange涓嶈Е鍙戠殑闂 + let row = scope.row; + if (column.onChange && !column.onChange(row, val, event)) { + return; + } + // 杈撳叆妗嗘眰鍜屽疄鏃惰绠� + this.getInputSummaries(scope, val, event, column); + }, + // input杈撳叆瀹炴椂姹傚拰 + getInputSummaries(scope, val, event, column) { + // column鍒楄缃簡summary灞炴�х殑鎵嶈绠楀�� + if (!column.summary) return; + let sum = 0; + // let _index = 0; + (this.url ? this.rowData : this.tableData).forEach((x, index) => { + if (x.hasOwnProperty(column.field) && !isNaN(x[column.field])) { + // _index = index; + sum += x[column.field] * 1; + } + }); + if (sum) { + if (column.summary == "avg") { + sum = sum / (this.rowData.length || this.tableData.length || 1); + } + sum = + (sum * 1.0).toFixed(column.numberLength || 2).replace(".00", "") * + 1.0; + } + this.summaryData[this.summaryIndex[column.field]] = sum; + }, + getSummaryData({ columns, data }) { + return this.summaryData; + }, + getCellStyle(row) { + // 2020.12.13澧炲姞璁剧疆鍗曞厓鏍奸鑹� + if (row.column.property) { + return ( + this.cellStyleColumns[row.column.property] && + this.cellStyleColumns[row.column.property]( + row.row, + row.rowIndex, + row.columnIndex + ) + ); + } + }, + compareDate(date1, date2) { + if (!date2) { + return true; + } + return ( + date1.valueOf() < + (typeof date2 == "number" ? date2 : new Date(date2).valueOf()) + ); + }, + getDateOptions(date, item) { + //2021.07.17璁剧疆鏃堕棿鍙�夎寖鍥� + 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) + ); + }, + getDateFormat(column) { + //瑙乭ttps://day.js.org/docs/zh-CN/display/format + return column.edit.type == "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss"; + }, + userSelect(selection, row) { + this.selectRows = selection; + if (!this.single) { + this.$emit("rowChange", { row, selection }); + } + }, + isEmptyTag(row, column) { + if (!row[column.field] && row[column.field] != "0") { + return "empty-tag"; + } + return ""; + }, + filterChildrenColumn(children) { + if (!children) { + return []; + } + return children.filter((x) => { + return !x.hidden; + }); + }, + initColumnDisabled(row, column) { + return column.getDisabled && column.getDisabled(row, column); + }, + showUpload(row, column) { + this.fileInfo = (row[column.field] || "") + .split(",") + .filter((x) => { + return x; + }) + .map((item) => { + return { path: item, name: "" }; + }); + this.currentRow = row; + this.currentColumn = column; + if (this.currentColumn.edit.autoUpload === undefined) { + this.currentColumn.edit.autoUpload = true; + } + if (this.currentColumn.edit.multiple === undefined) { + this.currentColumn.edit.multiple = false; + } + + if (this.currentColumn.edit.url === undefined) { + this.uploadUrl = + "api/" + + (this.url || "").replace("/api", "api").split("/")[1] + + "/upload"; + } else { + this.uploadUrl = this.currentColumn.edit.url; + } + this.uploadModel = true; + }, + uploadAfter(result, files) { + this.currentColumn.uploadAfter && + this.currentColumn.uploadAfter(result, files); + return true; + }, + saveUpload() { + //鐢熸垚淇濆瓨鍚庤繑鍥炵殑璺緞 + let arr = this.fileInfo.map((x) => { + if (x.path) { + return x.path; + } + return result.data + x.name; + }); + + this.currentRow[this.currentColumn.field] = arr.join(","); + this.uploadModel = false; + return true; + }, + }, +}); +</script> +<style lang="less" scoped> +.vol-table { + position: relative; + .mask { + opacity: 0.2; + position: absolute; + width: 100%; + height: 100%; + background: #d0d0d0; + z-index: 100; + } + .message { + text-align: center; + color: #635c5c; + font-size: 15px; + font-weight: 600; + background: #eee; + transform: translateY(-50%); + top: 50%; + position: absolute; + z-index: 200; + left: 0; + right: 0; + width: 150px; + margin: 0 auto; + line-height: 40px; + border-radius: 4px; + border: 1px solid #a09e9e; + } +} +.e-item { + display: flex; + > div:first-child { + flex: 1; + } +} +.vol-table ::v-deep(.el-pager .number) { + padding: 0 7px; + border-radius: 5px; + border: 1px solid #e6e6e6; + margin-left: 8px; + font-weight: 500; + min-width: 28px; +} +.vol-table ::v-deep(.el-pager .number.active) { + background: #ed4014; + color: #fff; +} +.vol-table .t-file { + color: #1e8cff; + cursor: pointer; + border-bottom: 1px solid; + padding-bottom: 2px; +} +.vol-table .empty-tag { + border: none; + background: none; +} +.v-table ::v-deep(.el-date-editor .el-icon-date), +.v-table ::v-deep(.el-date-editor .el-icon-time) { + width: 10px; +} + +.column-required { + position: relative; + color: #f20303; + font-size: 14px; + top: 2px; + right: 2px; +} +</style> + +<style scoped> +/* .v-table ::v-deep(.even-row){ + background: rgb(245,247,250); +} */ +.pagination { + text-align: right; + padding: 2px 28px; + border: 1px solid #eee; + border-top: 0px; +} +/* .v-table ::v-deep(.el-input .el-input__inner) { + padding: 0 7px; +} */ +.v-table ::v-deep(.el-table__header th) { + /* padding: 0px !important; */ + background-color: #f8f8f9 !important; + font-size: 13px; + height: 46px; + color: #616161; +} + +.v-table ::v-deep(.el-table__header th.is-sortable) { + padding: 3px !important; +} +.vol-table.text-inline ::v-deep(.el-table__body .cell), +.vol-table.text-inline ::v-deep(.el-table__header-wrapper .cell) { + word-break: inherit !important; + white-space: nowrap !important; +} +/* .v-table ::v-deep(.el-table__body td) { + padding: 9px 0 !important; +} */ + +.v-table ::v-deep(.el-table__footer td) { + padding: 7px 0 !important; +} + +.vol-table ::v-deep(.el-table-column--selection .cell) { + display: inline; +} +.vol-table.text-inline ::v-deep(.el-table th > .cell) { + white-space: nowrap !important; +} + +.vol-table .table-img { + height: 40px; + border-radius: 5px; + margin-right: 10px; + width: 40px; + object-fit: cover; +} +.vol-table .table-img:hover { + cursor: pointer; +} + +.vol-table ::v-deep(.cell) { + padding: 2px 10px; +} +.vol-table ::v-deep(.cell .el-tag) { + padding: 5px 9px; +} +.table-input { + color: rgb(104, 103, 103); + padding: 3px 10px; + height: 32px; + line-height: 32px; + width: 100%; + border-radius: 4px; + border: 1px solid #dcdcdc; +} +.table-input:focus { + outline: 1px solid #49a3fd; +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable/VolTableRender.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable/VolTableRender.js" new file mode 100644 index 0000000..988a672 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolTable/VolTableRender.js" @@ -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(); + } +}; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolUpload.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolUpload.vue" new file mode 100644 index 0000000..4c7d696 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/basic/VolUpload.vue" @@ -0,0 +1,880 @@ +<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> +let OSS = require('ali-oss'); +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: { + //涓婁紶鐨剈rl + 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: true + }, + compressMinSize: { + //鍘嬬缉鐨勬渶灏忔瘮渚� + type: Number, + default: 0.1 + } + }, + data() { + return { + errorImg: 'this.src="' + require('@/assets/imgs/error-img.png') + '"', + changed: false, //鎵嬪姩涓婁紶鎴愬姛鍚庣姝㈤噸澶嶄笂浼狅紝蹇呴』閲嶆柊閫夋嫨 + model: true, + files: [], + bigImg: '', + imgTypes: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp', 'jfif'], + 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; + //濡傛灉浼犲叆浜咶ileInfo闇�瑕佽嚜琛屽鐞嗙Щ闄ileInfo + 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) { + //濡傛灉浼犲叆浜咶ileInfo闇�瑕佽嚜琛屽鐞嗙Щ闄ileInfo + //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; + }, + convertToFile(dataurl, filename) { + let arr = dataurl.split(','); + let mime = arr[0].match(/:(.*?);/)[1]; + let suffix = mime.split('/')[1]; + let bstr = atob(arr[1]); + let n = bstr.length; + let u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + // new File杩斿洖File瀵硅薄 绗竴涓弬鏁版槸 ArraryBuffer 鎴� Bolb 鎴朅rrary 绗簩涓弬鏁版槸鏂囦欢鍚� + // 绗笁涓弬鏁版槸 瑕佹斁鍒版枃浠朵腑鐨勫唴瀹圭殑 MIME 绫诲瀷 + return new File([u8arr], `${filename}.${suffix}`, { + type: mime, + input: true + }); + }, + async compressImg(file) { + let fileSize = file.size / 1024 / 1024; + let read = new FileReader(); + read.readAsDataURL(file); + return new Promise((resolve, reject) => { + read.onload = (e) => { + let img = new Image(); + img.src = e.target.result; + let _this = this; + img.onload = function() { + //榛樿鎸夋瘮渚嬪帇缂� + let w = this.width; + let h = this.height; + let canvas = document.createElement('canvas'); + let ctx = canvas.getContext('2d'); + canvas.setAttribute('width', w); + canvas.setAttribute('height', h); + ctx.drawImage(this, 0, 0, w, h); + let rate = 0.3; + if (fileSize > 2) { + rate = 0.1; + } else if (fileSize > 1) { + rate = 0.1; + } + if (_this.compressMinSize > rate) { + rate = _this.compressMinSize; + } + // rate=1; + let base64 = canvas.toDataURL('image/jpeg', rate); + resolve(_this.convertToFile(base64, file.name)); + }; + }; + }); + }, + async uploadOSS() { + this.http.get('api/alioss/getAccessToken', {}, false).then(async (x) => { + if (!x.status) return this.$Message.error(x.message); + let client = new OSS({ + // yourRegion濉啓Bucket鎵�鍦ㄥ湴鍩熴�備互鍗庝笢1锛堟澀宸烇級涓轰緥锛孯egion濉啓涓簅ss-cn-hangzhou銆� + region: x.data.region, + // 浠嶴TS鏈嶅姟鑾峰彇鐨勪复鏃惰闂瘑閽ワ紙AccessKey ID鍜孉ccessKey Secret锛夈�� + accessKeyId: x.data.accessKeyId, + accessKeySecret: x.data.accessKeySecret, + // 浠嶴TS鏈嶅姟鑾峰彇鐨勫畨鍏ㄤ护鐗岋紙SecurityToken锛夈�� + stsToken: x.data.securityToken, + // 濉啓Bucket鍚嶇О銆� + bucket: x.data.bucket + }); + console.log(this.files); + for (let index = 0; index < this.files.length; index++) { + const file = this.files[index]; + if (file.input) { + let result = await client.put( + x.data.bucketFolder + '/' + x.data.unique + file.name, + file + ); + file.path = result.url; + file.newName = x.data.unique + file.name; + } + } + + this.fileInfo.splice(0); + // } + let _files = this.files.map((file) => { + return { + name: file.newName || file.name, + path: file.path + }; + }); + this.fileInfo.push(..._files); + //2021.09.25淇鏂囦欢涓婁紶鍚庝笉鑳藉悓鏃朵笅杞界殑闂 + this.files = _files; + }); + return; + }, + async upload(vail) { + if (vail && !this.checkFile()) return false; + if (!this.url) { + return this.$message.error('娌℃湁閰嶇疆濂経rl'); + } + if (!this.files || this.files.length == 0) { + return this.$message.error('璇烽�夋嫨鏂囦欢'); + } + //澧炲姞涓婁紶鏃惰嚜瀹氫箟鍙傛暟锛屽悗鍙颁娇鐢ㄨ幏鍙朥tilities.HttpContext.Current.Request.Query["瀛楁"] + let params={}; + if (!this.uploadBefore(this.files,params)) { + return; + } + let paramText=""; + if (Object.keys(params).length) { + paramText="?1=1"; + for (const key in params) { + let value=params[key]; + if(typeof(value)=='object'){ + value=JSON.stringify(value) + } + paramText+=`&${key}=${value}` + } + } + + this.loadingStatus = true; + this.loadText = '涓婁紶涓�..'; + if (window.oss && window.oss.ali.use) { + await this.uploadOSS(); + this.loadingStatus = false; + this.loadText = '涓婁紶鏂囦欢'; + if (!this.uploadAfter({status:true}, this.files)) { + this.changed = false; + return; + } else { + this.changed = true; + } + this.$message.success('涓婁紶鎴愬姛'); + return; + } + + var forms = new FormData(); + for (let index = 0; index < this.files.length; index++) { + let file = this.files[index]; + if (file.input) { + let name = file.name.split('.'); + name = name[name.length - 1].toLocaleLowerCase(); + let isImg = this.imgTypes.indexOf(name) != -1; + if (isImg && (name == 'jpg' || name == 'jpeg')) { + //>200KB鐨勫紑鍚帇缂� + if (isImg && file.size / 1024 / 1024 > 0.2) { + console.log('鍘嬬缉鍓�' + file.size); + file = await this.compressImg(file); + file.compress = true; + this.files[index] = file; + this.files[index].input = true; + console.log('鍘嬬缉鍚�' + file.size); + } + } + forms.append('fileInput', file, file.name); + } + } + // forms.append("fileInput", this.files); + + this.http + .post(this.url+paramText, forms, this.autoUpload ? '姝e湪涓婁紶鏂囦欢' : '') + .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' || this.imgTypes.indexOf(format) > -1) { + if (checkFileType == 'img') { + if (this.imgTypes.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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue" new file mode 100644 index 0000000..8c8e704 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue" @@ -0,0 +1,13 @@ +<template> + +</template> + +<script> +export default { + +} +</script> + +<style> + +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/VolWangEditor.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/VolWangEditor.vue" new file mode 100644 index 0000000..d3813ae --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/editor/VolWangEditor.vue" @@ -0,0 +1,154 @@ +<template> + <div class="hello" ref="volWangEditor"></div> +</template> + +<script> +import E from "wangeditor"; +export default { + props: { + url: { + //涓婁紶鍥剧墖鐨剈rl + 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.volWangEditor); + 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("鏈厤缃畊rl"); + 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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/401.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/401.vue" new file mode 100644 index 0000000..33e44fb --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/401.vue" @@ -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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/404.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/404.vue" new file mode 100644 index 0000000..bd6db8f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/404.vue" @@ -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> + + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/Message.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/Message.vue" new file mode 100644 index 0000000..8952162 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/Message.vue" @@ -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> + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/RedirectError.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/RedirectError.vue" new file mode 100644 index 0000000..5457065 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/RedirectError.vue" @@ -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> + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/coding.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/coding.vue" new file mode 100644 index 0000000..5569483 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/redirect/coding.vue" @@ -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: "鐢ㄤ緥姝e湪鏁寸悊涓�", + text: "璇︾粏鐢ㄤ緥鍦ㄦ鍑嗗涓�,鐩墠鍙弬鑰僛娴嬭瘯瀹屾暣绀轰緥]鐨勪娇鐢ㄦ柟娉�" + }; + } +}; +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/data_A.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/data_A.js" new file mode 100644 index 0000000..f6dc85c --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/data_A.js" @@ -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 +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/force-directed.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/force-directed.js" new file mode 100644 index 0000000..f144365 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/force-directed.js" @@ -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 +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/index.css" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/index.css" new file mode 100644 index 0000000..4b08c41 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/index.css" @@ -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; +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js" new file mode 100644 index 0000000..9cd1055 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js" @@ -0,0 +1,15711 @@ +/** + * jsBezier + * + * Copyright (c) 2010 - 2017 jsPlumb (hello@jsplumbtoolkit.com) + * + * licensed under the MIT license. + * + * a set of Bezier curve functions that deal with Beziers, used by jsPlumb, and perhaps useful for other people. These functions work with Bezier + * curves of arbitrary degree. + * + * - functions are all in the 'jsBezier' namespace. + * + * - all input points should be in the format {x:.., y:..}. all output points are in this format too. + * + * - all input curves should be in the format [ {x:.., y:..}, {x:.., y:..}, {x:.., y:..}, {x:.., y:..} ] + * + * - 'location' as used as an input here refers to a decimal in the range 0-1 inclusive, which indicates a point some proportion along the length + * of the curve. location as output has the same format and meaning. + * + * + * Function List: + * -------------- + * + * distanceFromCurve(point, curve) + * + * Calculates the distance that the given point lies from the given Bezier. Note that it is computed relative to the center of the Bezier, + * so if you have stroked the curve with a wide pen you may wish to take that into account! The distance returned is relative to the values + * of the curve and the point - it will most likely be pixels. + * + * gradientAtPoint(curve, location) + * + * Calculates the gradient to the curve at the given location, as a decimal between 0 and 1 inclusive. + * + * gradientAtPointAlongCurveFrom (curve, location) + * + * Calculates the gradient at the point on the given curve that is 'distance' units from location. + * + * nearestPointOnCurve(point, curve) + * + * Calculates the nearest point to the given point on the given curve. The return value of this is a JS object literal, containing both the + *point's coordinates and also the 'location' of the point (see above), for example: { point:{x:551,y:150}, location:0.263365 }. + * + * pointOnCurve(curve, location) + * + * Calculates the coordinates of the point on the given Bezier curve at the given location. + * + * pointAlongCurveFrom(curve, location, distance) + * + * Calculates the coordinates of the point on the given curve that is 'distance' units from location. 'distance' should be in the same coordinate + * space as that used to construct the Bezier curve. For an HTML Canvas usage, for example, distance would be a measure of pixels. + * + * locationAlongCurveFrom(curve, location, distance) + * + * Calculates the location on the given curve that is 'distance' units from location. 'distance' should be in the same coordinate + * space as that used to construct the Bezier curve. For an HTML Canvas usage, for example, distance would be a measure of pixels. + * + * perpendicularToCurveAt(curve, location, length, distance) + * + * Calculates the perpendicular to the given curve at the given location. length is the length of the line you wish for (it will be centered + * on the point at 'location'). distance is optional, and allows you to specify a point along the path from the given location as the center of + * the perpendicular returned. The return value of this is an array of two points: [ {x:...,y:...}, {x:...,y:...} ]. + * + * + */ + +(function() { + + var root = this; + + if(typeof Math.sgn == "undefined") { + Math.sgn = function(x) { return x == 0 ? 0 : x > 0 ? 1 :-1; }; + } + + var Vectors = { + subtract : function(v1, v2) { return {x:v1.x - v2.x, y:v1.y - v2.y }; }, + dotProduct : function(v1, v2) { return (v1.x * v2.x) + (v1.y * v2.y); }, + square : function(v) { return Math.sqrt((v.x * v.x) + (v.y * v.y)); }, + scale : function(v, s) { return {x:v.x * s, y:v.y * s }; } + }, + + maxRecursion = 64, + flatnessTolerance = Math.pow(2.0,-maxRecursion-1); + + /** + * Calculates the distance that the point lies from the curve. + * + * @param point a point in the form {x:567, y:3342} + * @param curve a Bezier curve in the form [{x:..., y:...}, {x:..., y:...}, {x:..., y:...}, {x:..., y:...}]. note that this is currently + * hardcoded to assume cubiz beziers, but would be better off supporting any degree. + * @return a JS object literal containing location and distance, for example: {location:0.35, distance:10}. Location is analogous to the location + * argument you pass to the pointOnPath function: it is a ratio of distance travelled along the curve. Distance is the distance in pixels from + * the point to the curve. + */ + var _distanceFromCurve = function(point, curve) { + var candidates = [], + w = _convertToBezier(point, curve), + degree = curve.length - 1, higherDegree = (2 * degree) - 1, + numSolutions = _findRoots(w, higherDegree, candidates, 0), + v = Vectors.subtract(point, curve[0]), dist = Vectors.square(v), t = 0.0; + + for (var i = 0; i < numSolutions; i++) { + v = Vectors.subtract(point, _bezier(curve, degree, candidates[i], null, null)); + var newDist = Vectors.square(v); + if (newDist < dist) { + dist = newDist; + t = candidates[i]; + } + } + v = Vectors.subtract(point, curve[degree]); + newDist = Vectors.square(v); + if (newDist < dist) { + dist = newDist; + t = 1.0; + } + return {location:t, distance:dist}; + }; + /** + * finds the nearest point on the curve to the given point. + */ + var _nearestPointOnCurve = function(point, curve) { + var td = _distanceFromCurve(point, curve); + return {point:_bezier(curve, curve.length - 1, td.location, null, null), location:td.location}; + }; + var _convertToBezier = function(point, curve) { + var degree = curve.length - 1, higherDegree = (2 * degree) - 1, + c = [], d = [], cdTable = [], w = [], + z = [ [1.0, 0.6, 0.3, 0.1], [0.4, 0.6, 0.6, 0.4], [0.1, 0.3, 0.6, 1.0] ]; + + for (var i = 0; i <= degree; i++) c[i] = Vectors.subtract(curve[i], point); + for (var i = 0; i <= degree - 1; i++) { + d[i] = Vectors.subtract(curve[i+1], curve[i]); + d[i] = Vectors.scale(d[i], 3.0); + } + for (var row = 0; row <= degree - 1; row++) { + for (var column = 0; column <= degree; column++) { + if (!cdTable[row]) cdTable[row] = []; + cdTable[row][column] = Vectors.dotProduct(d[row], c[column]); + } + } + for (i = 0; i <= higherDegree; i++) { + if (!w[i]) w[i] = []; + w[i].y = 0.0; + w[i].x = parseFloat(i) / higherDegree; + } + var n = degree, m = degree-1; + for (var k = 0; k <= n + m; k++) { + var lb = Math.max(0, k - m), + ub = Math.min(k, n); + for (i = lb; i <= ub; i++) { + var j = k - i; + w[i+j].y += cdTable[j][i] * z[j][i]; + } + } + return w; + }; + /** + * counts how many roots there are. + */ + var _findRoots = function(w, degree, t, depth) { + var left = [], right = [], + left_count, right_count, + left_t = [], right_t = []; + + switch (_getCrossingCount(w, degree)) { + case 0 : { + return 0; + } + case 1 : { + if (depth >= maxRecursion) { + t[0] = (w[0].x + w[degree].x) / 2.0; + return 1; + } + if (_isFlatEnough(w, degree)) { + t[0] = _computeXIntercept(w, degree); + return 1; + } + break; + } + } + _bezier(w, degree, 0.5, left, right); + left_count = _findRoots(left, degree, left_t, depth+1); + right_count = _findRoots(right, degree, right_t, depth+1); + for (var i = 0; i < left_count; i++) t[i] = left_t[i]; + for (var i = 0; i < right_count; i++) t[i+left_count] = right_t[i]; + return (left_count+right_count); + }; + var _getCrossingCount = function(curve, degree) { + var n_crossings = 0, sign, old_sign; + sign = old_sign = Math.sgn(curve[0].y); + for (var i = 1; i <= degree; i++) { + sign = Math.sgn(curve[i].y); + if (sign != old_sign) n_crossings++; + old_sign = sign; + } + return n_crossings; + }; + var _isFlatEnough = function(curve, degree) { + var error, + intercept_1, intercept_2, left_intercept, right_intercept, + a, b, c, det, dInv, a1, b1, c1, a2, b2, c2; + a = curve[0].y - curve[degree].y; + b = curve[degree].x - curve[0].x; + c = curve[0].x * curve[degree].y - curve[degree].x * curve[0].y; + + var max_distance_above, max_distance_below; + max_distance_above = max_distance_below = 0.0; + + for (var i = 1; i < degree; i++) { + var value = a * curve[i].x + b * curve[i].y + c; + if (value > max_distance_above) + max_distance_above = value; + else if (value < max_distance_below) + max_distance_below = value; + } + + a1 = 0.0; b1 = 1.0; c1 = 0.0; a2 = a; b2 = b; + c2 = c - max_distance_above; + det = a1 * b2 - a2 * b1; + dInv = 1.0/det; + intercept_1 = (b1 * c2 - b2 * c1) * dInv; + a2 = a; b2 = b; c2 = c - max_distance_below; + det = a1 * b2 - a2 * b1; + dInv = 1.0/det; + intercept_2 = (b1 * c2 - b2 * c1) * dInv; + left_intercept = Math.min(intercept_1, intercept_2); + right_intercept = Math.max(intercept_1, intercept_2); + error = right_intercept - left_intercept; + return (error < flatnessTolerance)? 1 : 0; + }; + var _computeXIntercept = function(curve, degree) { + var XLK = 1.0, YLK = 0.0, + XNM = curve[degree].x - curve[0].x, YNM = curve[degree].y - curve[0].y, + XMK = curve[0].x - 0.0, YMK = curve[0].y - 0.0, + det = XNM*YLK - YNM*XLK, detInv = 1.0/det, + S = (XNM*YMK - YNM*XMK) * detInv; + return 0.0 + XLK * S; + }; + var _bezier = function(curve, degree, t, left, right) { + var temp = [[]]; + for (var j =0; j <= degree; j++) temp[0][j] = curve[j]; + for (var i = 1; i <= degree; i++) { + for (var j =0 ; j <= degree - i; j++) { + if (!temp[i]) temp[i] = []; + if (!temp[i][j]) temp[i][j] = {}; + temp[i][j].x = (1.0 - t) * temp[i-1][j].x + t * temp[i-1][j+1].x; + temp[i][j].y = (1.0 - t) * temp[i-1][j].y + t * temp[i-1][j+1].y; + } + } + if (left != null) + for (j = 0; j <= degree; j++) left[j] = temp[j][0]; + if (right != null) + for (j = 0; j <= degree; j++) right[j] = temp[degree-j][j]; + + return (temp[degree][0]); + }; + + var _curveFunctionCache = {}; + var _getCurveFunctions = function(order) { + var fns = _curveFunctionCache[order]; + if (!fns) { + fns = []; + var f_term = function() { return function(t) { return Math.pow(t, order); }; }, + l_term = function() { return function(t) { return Math.pow((1-t), order); }; }, + c_term = function(c) { return function(t) { return c; }; }, + t_term = function() { return function(t) { return t; }; }, + one_minus_t_term = function() { return function(t) { return 1-t; }; }, + _termFunc = function(terms) { + return function(t) { + var p = 1; + for (var i = 0; i < terms.length; i++) p = p * terms[i](t); + return p; + }; + }; + + fns.push(new f_term()); // first is t to the power of the curve order + for (var i = 1; i < order; i++) { + var terms = [new c_term(order)]; + for (var j = 0 ; j < (order - i); j++) terms.push(new t_term()); + for (var j = 0 ; j < i; j++) terms.push(new one_minus_t_term()); + fns.push(new _termFunc(terms)); + } + fns.push(new l_term()); // last is (1-t) to the power of the curve order + + _curveFunctionCache[order] = fns; + } + + return fns; + }; + + + /** + * calculates a point on the curve, for a Bezier of arbitrary order. + * @param curve an array of control points, eg [{x:10,y:20}, {x:50,y:50}, {x:100,y:100}, {x:120,y:100}]. For a cubic bezier this should have four points. + * @param location a decimal indicating the distance along the curve the point should be located at. this is the distance along the curve as it travels, taking the way it bends into account. should be a number from 0 to 1, inclusive. + */ + var _pointOnPath = function(curve, location) { + var cc = _getCurveFunctions(curve.length - 1), + _x = 0, _y = 0; + for (var i = 0; i < curve.length ; i++) { + _x = _x + (curve[i].x * cc[i](location)); + _y = _y + (curve[i].y * cc[i](location)); + } + + return {x:_x, y:_y}; + }; + + var _dist = function(p1,p2) { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + }; + + var _isPoint = function(curve) { + return curve[0].x === curve[1].x && curve[0].y === curve[1].y; + }; + + /** + * finds the point that is 'distance' along the path from 'location'. this method returns both the x,y location of the point and also + * its 'location' (proportion of travel along the path); the method below - _pointAlongPathFrom - calls this method and just returns the + * point. + */ + var _pointAlongPath = function(curve, location, distance) { + + if (_isPoint(curve)) { + return { + point:curve[0], + location:location + }; + } + + var prev = _pointOnPath(curve, location), + tally = 0, + curLoc = location, + direction = distance > 0 ? 1 : -1, + cur = null; + + while (tally < Math.abs(distance)) { + curLoc += (0.005 * direction); + cur = _pointOnPath(curve, curLoc); + tally += _dist(cur, prev); + prev = cur; + } + return {point:cur, location:curLoc}; + }; + + var _length = function(curve) { + if (_isPoint(curve)) return 0; + + var prev = _pointOnPath(curve, 0), + tally = 0, + curLoc = 0, + direction = 1, + cur = null; + + while (curLoc < 1) { + curLoc += (0.005 * direction); + cur = _pointOnPath(curve, curLoc); + tally += _dist(cur, prev); + prev = cur; + } + return tally; + }; + + /** + * finds the point that is 'distance' along the path from 'location'. + */ + var _pointAlongPathFrom = function(curve, location, distance) { + return _pointAlongPath(curve, location, distance).point; + }; + + /** + * finds the location that is 'distance' along the path from 'location'. + */ + var _locationAlongPathFrom = function(curve, location, distance) { + return _pointAlongPath(curve, location, distance).location; + }; + + /** + * returns the gradient of the curve at the given location, which is a decimal between 0 and 1 inclusive. + * + * thanks // http://bimixual.org/AnimationLibrary/beziertangents.html + */ + var _gradientAtPoint = function(curve, location) { + var p1 = _pointOnPath(curve, location), + p2 = _pointOnPath(curve.slice(0, curve.length - 1), location), + dy = p2.y - p1.y, dx = p2.x - p1.x; + return dy === 0 ? Infinity : Math.atan(dy / dx); + }; + + /** + returns the gradient of the curve at the point which is 'distance' from the given location. + if this point is greater than location 1, the gradient at location 1 is returned. + if this point is less than location 0, the gradient at location 0 is returned. + */ + var _gradientAtPointAlongPathFrom = function(curve, location, distance) { + var p = _pointAlongPath(curve, location, distance); + if (p.location > 1) p.location = 1; + if (p.location < 0) p.location = 0; + return _gradientAtPoint(curve, p.location); + }; + + /** + * calculates a line that is 'length' pixels long, perpendicular to, and centered on, the path at 'distance' pixels from the given location. + * if distance is not supplied, the perpendicular for the given location is computed (ie. we set distance to zero). + */ + var _perpendicularToPathAt = function(curve, location, length, distance) { + distance = distance == null ? 0 : distance; + var p = _pointAlongPath(curve, location, distance), + m = _gradientAtPoint(curve, p.location), + _theta2 = Math.atan(-1 / m), + y = length / 2 * Math.sin(_theta2), + x = length / 2 * Math.cos(_theta2); + return [{x:p.point.x + x, y:p.point.y + y}, {x:p.point.x - x, y:p.point.y - y}]; + }; + + /** + * Calculates all intersections of the given line with the given curve. + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param curve + * @returns {Array} + */ + var _lineIntersection = function(x1, y1, x2, y2, curve) { + var a = y2 - y1, + b = x1 - x2, + c = (x1 * (y1 - y2)) + (y1 * (x2-x1)), + coeffs = _computeCoefficients(curve), + p = [ + (a*coeffs[0][0]) + (b * coeffs[1][0]), + (a*coeffs[0][1])+(b*coeffs[1][1]), + (a*coeffs[0][2])+(b*coeffs[1][2]), + (a*coeffs[0][3])+(b*coeffs[1][3]) + c + ], + r = _cubicRoots.apply(null, p), + intersections = []; + + if (r != null) { + + for (var i = 0; i < 3; i++) { + var t = r[i], + t2 = Math.pow(t, 2), + t3 = Math.pow(t, 3), + x = [ + (coeffs[0][0] * t3) + (coeffs[0][1] * t2) + (coeffs[0][2] * t) + coeffs[0][3], + (coeffs[1][0] * t3) + (coeffs[1][1] * t2) + (coeffs[1][2] * t) + coeffs[1][3] + ]; + + // check bounds of the line + var s; + if ((x2 - x1) !== 0) { + s = (x[0] - x1) / (x2 - x1); + } + else { + s = (x[1] - y1) / (y2 - y1); + } + + if (t >= 0 && t <= 1.0 && s >= 0 && s <= 1.0) { + intersections.push(x); + } + } + } + + return intersections; + }; + + /** + * Calculates all intersections of the given box with the given curve. + * @param x X position of top left corner of box + * @param y Y position of top left corner of box + * @param w width of box + * @param h height of box + * @param curve + * @returns {Array} + */ + var _boxIntersection = function(x, y, w, h, curve) { + var i = []; + i.push.apply(i, _lineIntersection(x, y, x + w, y, curve)); + i.push.apply(i, _lineIntersection(x + w, y, x + w, y + h, curve)); + i.push.apply(i, _lineIntersection(x + w, y + h, x, y + h, curve)); + i.push.apply(i, _lineIntersection(x, y + h, x, y, curve)); + return i; + }; + + /** + * Calculates all intersections of the given bounding box with the given curve. + * @param boundingBox Bounding box, in { x:.., y:..., w:..., h:... } format. + * @param curve + * @returns {Array} + */ + var _boundingBoxIntersection = function(boundingBox, curve) { + var i = []; + i.push.apply(i, _lineIntersection(boundingBox.x, boundingBox.y, boundingBox.x + boundingBox.w, boundingBox.y, curve)); + i.push.apply(i, _lineIntersection(boundingBox.x + boundingBox.w, boundingBox.y, boundingBox.x + boundingBox.w, boundingBox.y + boundingBox.h, curve)); + i.push.apply(i, _lineIntersection(boundingBox.x + boundingBox.w, boundingBox.y + boundingBox.h, boundingBox.x, boundingBox.y + boundingBox.h, curve)); + i.push.apply(i, _lineIntersection(boundingBox.x, boundingBox.y + boundingBox.h, boundingBox.x, boundingBox.y, curve)); + return i; + }; + + + function _computeCoefficientsForAxis(curve, axis) { + return [ + -(curve[0][axis]) + (3*curve[1][axis]) + (-3 * curve[2][axis]) + curve[3][axis], + (3*(curve[0][axis])) - (6*(curve[1][axis])) + (3*(curve[2][axis])), + -3*curve[0][axis] + 3*curve[1][axis], + curve[0][axis] + ]; + } + + function _computeCoefficients(curve) + { + return [ + _computeCoefficientsForAxis(curve, "x"), + _computeCoefficientsForAxis(curve, "y") + ]; + } + + function sgn(x) { + return x < 0 ? -1 : x > 0 ? 1 : 0; + } + + function _cubicRoots(a, b, c, d) { + var A = b / a, + B = c / a, + C = d / a, + Q = (3*B - Math.pow(A, 2))/9, + R = (9*A*B - 27*C - 2*Math.pow(A, 3))/54, + D = Math.pow(Q, 3) + Math.pow(R, 2), + S, + T, + t = []; + + if (D >= 0) // complex or duplicate roots + { + S = sgn(R + Math.sqrt(D))*Math.pow(Math.abs(R + Math.sqrt(D)),(1/3)); + T = sgn(R - Math.sqrt(D))*Math.pow(Math.abs(R - Math.sqrt(D)),(1/3)); + + t[0] = -A/3 + (S + T); + t[1] = -A/3 - (S + T)/2; + t[2] = -A/3 - (S + T)/2; + + /*discard complex roots*/ + if (Math.abs(Math.sqrt(3)*(S - T)/2) !== 0) { + t[1] = -1; + t[2] = -1; + } + } + else // distinct real roots + { + var th = Math.acos(R/Math.sqrt(-Math.pow(Q, 3))); + t[0] = 2*Math.sqrt(-Q)*Math.cos(th/3) - A/3; + t[1] = 2*Math.sqrt(-Q)*Math.cos((th + 2*Math.PI)/3) - A/3; + t[2] = 2*Math.sqrt(-Q)*Math.cos((th + 4*Math.PI)/3) - A/3; + } + + // discard out of spec roots + for (var i = 0; i < 3; i++) { + if (t[i] < 0 || t[i] > 1.0) { + t[i] = -1; + } + } + + return t; + } + + var jsBezier = this.jsBezier = { + distanceFromCurve : _distanceFromCurve, + gradientAtPoint : _gradientAtPoint, + gradientAtPointAlongCurveFrom : _gradientAtPointAlongPathFrom, + nearestPointOnCurve : _nearestPointOnCurve, + pointOnCurve : _pointOnPath, + pointAlongCurveFrom : _pointAlongPathFrom, + perpendicularToCurveAt : _perpendicularToPathAt, + locationAlongCurveFrom:_locationAlongPathFrom, + getLength:_length, + lineIntersection:_lineIntersection, + boxIntersection:_boxIntersection, + boundingBoxIntersection:_boundingBoxIntersection, + version:"0.9.0" + }; + + if (typeof exports !== "undefined") { + exports.jsBezier = jsBezier; + } + +}).call(typeof window !== 'undefined' ? window : this); + +/** + * Biltong v0.4.0 + * + * Various geometry functions written as part of jsPlumb and perhaps useful for others. + * + * Copyright (c) 2017 jsPlumb + * https://jsplumbtoolkit.com + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +;(function() { + + "use strict"; + var root = this; + + var Biltong = root.Biltong = { + version:"0.4.0" + }; + + if (typeof exports !== "undefined") { + exports.Biltong = Biltong; + } + + var _isa = function(a) { return Object.prototype.toString.call(a) === "[object Array]"; }, + _pointHelper = function(p1, p2, fn) { + p1 = _isa(p1) ? p1 : [p1.x, p1.y]; + p2 = _isa(p2) ? p2 : [p2.x, p2.y]; + return fn(p1, p2); + }, + /** + * @name Biltong.gradient + * @function + * @desc Calculates the gradient of a line between the two points. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Float} The gradient of a line between the two points. + */ + _gradient = Biltong.gradient = function(p1, p2) { + return _pointHelper(p1, p2, function(_p1, _p2) { + if (_p2[0] == _p1[0]) + return _p2[1] > _p1[1] ? Infinity : -Infinity; + else if (_p2[1] == _p1[1]) + return _p2[0] > _p1[0] ? 0 : -0; + else + return (_p2[1] - _p1[1]) / (_p2[0] - _p1[0]); + }); + }, + /** + * @name Biltong.normal + * @function + * @desc Calculates the gradient of a normal to a line between the two points. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Float} The gradient of a normal to a line between the two points. + */ + _normal = Biltong.normal = function(p1, p2) { + return -1 / _gradient(p1, p2); + }, + /** + * @name Biltong.lineLength + * @function + * @desc Calculates the length of a line between the two points. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Float} The length of a line between the two points. + */ + _lineLength = Biltong.lineLength = function(p1, p2) { + return _pointHelper(p1, p2, function(_p1, _p2) { + return Math.sqrt(Math.pow(_p2[1] - _p1[1], 2) + Math.pow(_p2[0] - _p1[0], 2)); + }); + }, + /** + * @name Biltong.quadrant + * @function + * @desc Calculates the quadrant in which the angle between the two points lies. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Integer} The quadrant - 1 for upper right, 2 for lower right, 3 for lower left, 4 for upper left. + */ + _quadrant = Biltong.quadrant = function(p1, p2) { + return _pointHelper(p1, p2, function(_p1, _p2) { + if (_p2[0] > _p1[0]) { + return (_p2[1] > _p1[1]) ? 2 : 1; + } + else if (_p2[0] == _p1[0]) { + return _p2[1] > _p1[1] ? 2 : 1; + } + else { + return (_p2[1] > _p1[1]) ? 3 : 4; + } + }); + }, + /** + * @name Biltong.theta + * @function + * @desc Calculates the angle between the two points. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Float} The angle between the two points. + */ + _theta = Biltong.theta = function(p1, p2) { + return _pointHelper(p1, p2, function(_p1, _p2) { + var m = _gradient(_p1, _p2), + t = Math.atan(m), + s = _quadrant(_p1, _p2); + if ((s == 4 || s== 3)) t += Math.PI; + if (t < 0) t += (2 * Math.PI); + + return t; + }); + }, + /** + * @name Biltong.intersects + * @function + * @desc Calculates whether or not the two rectangles intersect. + * @param {Rectangle} r1 First rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}` + * @param {Rectangle} r2 Second rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}` + * @return {Boolean} True if the rectangles intersect, false otherwise. + */ + _intersects = Biltong.intersects = function(r1, r2) { + var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h, + a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h; + + return ( (x1 <= a1 && a1 <= x2) && (y1 <= b1 && b1 <= y2) ) || + ( (x1 <= a2 && a2 <= x2) && (y1 <= b1 && b1 <= y2) ) || + ( (x1 <= a1 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) || + ( (x1 <= a2 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) || + ( (a1 <= x1 && x1 <= a2) && (b1 <= y1 && y1 <= b2) ) || + ( (a1 <= x2 && x2 <= a2) && (b1 <= y1 && y1 <= b2) ) || + ( (a1 <= x1 && x1 <= a2) && (b1 <= y2 && y2 <= b2) ) || + ( (a1 <= x2 && x1 <= a2) && (b1 <= y2 && y2 <= b2) ); + }, + /** + * @name Biltong.encloses + * @function + * @desc Calculates whether or not r2 is completely enclosed by r1. + * @param {Rectangle} r1 First rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}` + * @param {Rectangle} r2 Second rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}` + * @param {Boolean} [allowSharedEdges=false] If true, the concept of enclosure allows for one or more edges to be shared by the two rectangles. + * @return {Boolean} True if r1 encloses r2, false otherwise. + */ + _encloses = Biltong.encloses = function(r1, r2, allowSharedEdges) { + var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h, + a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h, + c = function(v1, v2, v3, v4) { return allowSharedEdges ? v1 <= v2 && v3>= v4 : v1 < v2 && v3 > v4; }; + + return c(x1,a1,x2,a2) && c(y1,b1,y2,b2); + }, + _segmentMultipliers = [null, [1, -1], [1, 1], [-1, 1], [-1, -1] ], + _inverseSegmentMultipliers = [null, [-1, -1], [-1, 1], [1, 1], [1, -1] ], + /** + * @name Biltong.pointOnLine + * @function + * @desc Calculates a point on the line from `fromPoint` to `toPoint` that is `distance` units along the length of the line. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Point} Point on the line, in the form `{ x:..., y:... }`. + */ + _pointOnLine = Biltong.pointOnLine = function(fromPoint, toPoint, distance) { + var m = _gradient(fromPoint, toPoint), + s = _quadrant(fromPoint, toPoint), + segmentMultiplier = distance > 0 ? _segmentMultipliers[s] : _inverseSegmentMultipliers[s], + theta = Math.atan(m), + y = Math.abs(distance * Math.sin(theta)) * segmentMultiplier[1], + x = Math.abs(distance * Math.cos(theta)) * segmentMultiplier[0]; + return { x:fromPoint.x + x, y:fromPoint.y + y }; + }, + /** + * @name Biltong.perpendicularLineTo + * @function + * @desc Calculates a line of length `length` that is perpendicular to the line from `fromPoint` to `toPoint` and passes through `toPoint`. + * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties. + * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties. + * @return {Line} Perpendicular line, in the form `[ { x:..., y:... }, { x:..., y:... } ]`. + */ + _perpendicularLineTo = Biltong.perpendicularLineTo = function(fromPoint, toPoint, length) { + var m = _gradient(fromPoint, toPoint), + theta2 = Math.atan(-1 / m), + y = length / 2 * Math.sin(theta2), + x = length / 2 * Math.cos(theta2); + return [{x:toPoint.x + x, y:toPoint.y + y}, {x:toPoint.x - x, y:toPoint.y - y}]; + }; +}).call(typeof window !== 'undefined' ? window : this); +; +(function () { + + "use strict"; + + /** + * Creates a Touch object. + * @param view + * @param target + * @param pageX + * @param pageY + * @param screenX + * @param screenY + * @param clientX + * @param clientY + * @returns {Touch} + * @private + */ + function _touch(view, target, pageX, pageY, screenX, screenY, clientX, clientY) { + + return new Touch({ + target:target, + identifier:_uuid(), + pageX: pageX, + pageY: pageY, + screenX: screenX, + screenY: screenY, + clientX: clientX || screenX, + clientY: clientY || screenY + }); + } + + /** + * Create a synthetic touch list from the given list of Touch objects. + * @returns {Array} + * @private + */ + function _touchList() { + var list = []; + Array.prototype.push.apply(list, arguments); + list.item = function(index) { return this[index]; }; + return list; + } + + /** + * Create a Touch object and then insert it into a synthetic touch list, returning the list.s + * @param view + * @param target + * @param pageX + * @param pageY + * @param screenX + * @param screenY + * @param clientX + * @param clientY + * @returns {Array} + * @private + */ + function _touchAndList(view, target, pageX, pageY, screenX, screenY, clientX, clientY) { + return _touchList(_touch.apply(null, arguments)); + } + + var root = this, + matchesSelector = function (el, selector, ctx) { + ctx = ctx || el.parentNode; + var possibles = ctx.querySelectorAll(selector); + for (var i = 0; i < possibles.length; i++) { + if (possibles[i] === el) { + return true; + } + } + return false; + }, + _gel = function (el) { + return (typeof el == "string" || el.constructor === String) ? document.getElementById(el) : el; + }, + _t = function (e) { + return e.srcElement || e.target; + }, + // + // gets path info for the given event - the path from target to obj, in the event's bubble chain. if doCompute + // is false we just return target for the path. + // + _pi = function(e, target, obj, doCompute) { + if (!doCompute) return { path:[target], end:1 }; + else if (typeof e.path !== "undefined" && e.path.indexOf) { + return { path: e.path, end: e.path.indexOf(obj) }; + } else { + var out = { path:[], end:-1 }, _one = function(el) { + out.path.push(el); + if (el === obj) { + out.end = out.path.length - 1; + } + else if (el.parentNode != null) { + _one(el.parentNode) + } + }; + _one(target); + return out; + } + }, + _d = function (l, fn) { + for (var i = 0, j = l.length; i < j; i++) { + if (l[i] == fn) break; + } + if (i < l.length) l.splice(i, 1); + }, + guid = 1, + // + // this function generates a guid for every handler, sets it on the handler, then adds + // it to the associated object's map of handlers for the given event. this is what enables us + // to unbind all events of some type, or all events (the second of which can be requested by the user, + // but it also used by Mottle when an element is removed.) + _store = function (obj, event, fn) { + var g = guid++; + obj.__ta = obj.__ta || {}; + obj.__ta[event] = obj.__ta[event] || {}; + // store each handler with a unique guid. + obj.__ta[event][g] = fn; + // set the guid on the handler. + fn.__tauid = g; + return g; + }, + _unstore = function (obj, event, fn) { + obj.__ta && obj.__ta[event] && delete obj.__ta[event][fn.__tauid]; + // a handler might have attached extra functions, so we unbind those too. + if (fn.__taExtra) { + for (var i = 0; i < fn.__taExtra.length; i++) { + _unbind(obj, fn.__taExtra[i][0], fn.__taExtra[i][1]); + } + fn.__taExtra.length = 0; + } + // a handler might have attached an unstore callback + fn.__taUnstore && fn.__taUnstore(); + }, + _curryChildFilter = function (children, obj, fn, evt) { + if (children == null) return fn; + else { + var c = children.split(","), + _fn = function (e) { + _fn.__tauid = fn.__tauid; + var t = _t(e), target = t; // t is the target element on which the event occurred. it is the + // element we will wish to pass to any callbacks. + var pathInfo = _pi(e, t, obj, children != null) + if (pathInfo.end != -1) { + for (var p = 0; p < pathInfo.end; p++) { + target = pathInfo.path[p]; + for (var i = 0; i < c.length; i++) { + if (matchesSelector(target, c[i], obj)) { + fn.apply(target, arguments); + } + } + } + } + }; + registerExtraFunction(fn, evt, _fn); + return _fn; + } + }, + // + // registers an 'extra' function on some event listener function we were given - a function that we + // created and bound to the element as part of our housekeeping, and which we want to unbind and remove + // whenever the given function is unbound. + registerExtraFunction = function (fn, evt, newFn) { + fn.__taExtra = fn.__taExtra || []; + fn.__taExtra.push([evt, newFn]); + }, + DefaultHandler = function (obj, evt, fn, children) { + if (isTouchDevice && touchMap[evt]) { + var tfn = _curryChildFilter(children, obj, fn, touchMap[evt]); + _bind(obj, touchMap[evt], tfn , fn); + } + if (evt === "focus" && obj.getAttribute("tabindex") == null) { + obj.setAttribute("tabindex", "1"); + } + _bind(obj, evt, _curryChildFilter(children, obj, fn, evt), fn); + }, + SmartClickHandler = function (obj, evt, fn, children) { + if (obj.__taSmartClicks == null) { + var down = function (e) { + obj.__tad = _pageLocation(e); + }, + up = function (e) { + obj.__tau = _pageLocation(e); + }, + click = function (e) { + if (obj.__tad && obj.__tau && obj.__tad[0] === obj.__tau[0] && obj.__tad[1] === obj.__tau[1]) { + for (var i = 0; i < obj.__taSmartClicks.length; i++) + obj.__taSmartClicks[i].apply(_t(e), [ e ]); + } + }; + DefaultHandler(obj, "mousedown", down, children); + DefaultHandler(obj, "mouseup", up, children); + DefaultHandler(obj, "click", click, children); + obj.__taSmartClicks = []; + } + + // store in the list of callbacks + obj.__taSmartClicks.push(fn); + // the unstore function removes this function from the object's listener list for this type. + fn.__taUnstore = function () { + _d(obj.__taSmartClicks, fn); + }; + }, + _tapProfiles = { + "tap": {touches: 1, taps: 1}, + "dbltap": {touches: 1, taps: 2}, + "contextmenu": {touches: 2, taps: 1} + }, + TapHandler = function (clickThreshold, dblClickThreshold) { + return function (obj, evt, fn, children) { + // if event is contextmenu, for devices which are mouse only, we want to + // use the default bind. + if (evt == "contextmenu" && isMouseDevice) + DefaultHandler(obj, evt, fn, children); + else { + // the issue here is that this down handler gets registered only for the + // child nodes in the first registration. in fact it should be registered with + // no child selector and then on down we should cycle through the registered + // functions to see if one of them matches. on mouseup we should execute ALL of + // the functions whose children are either null or match the element. + if (obj.__taTapHandler == null) { + var tt = obj.__taTapHandler = { + tap: [], + dbltap: [], + contextmenu: [], + down: false, + taps: 0, + downSelectors: [] + }; + var down = function (e) { + var target = _t(e), pathInfo = _pi(e, target, obj, children != null), finished = false; + for (var p = 0; p < pathInfo.end; p++) { + if (finished) return; + target = pathInfo.path[p]; + for (var i = 0; i < tt.downSelectors.length; i++) { + if (tt.downSelectors[i] == null || matchesSelector(target, tt.downSelectors[i], obj)) { + tt.down = true; + setTimeout(clearSingle, clickThreshold); + setTimeout(clearDouble, dblClickThreshold); + finished = true; + break; // we only need one match on mousedown + } + } + } + }, + up = function (e) { + if (tt.down) { + var target = _t(e), currentTarget, pathInfo; + tt.taps++; + var tc = _touchCount(e); + for (var eventId in _tapProfiles) { + if (_tapProfiles.hasOwnProperty(eventId)) { + var p = _tapProfiles[eventId]; + if (p.touches === tc && (p.taps === 1 || p.taps === tt.taps)) { + for (var i = 0; i < tt[eventId].length; i++) { + pathInfo = _pi(e, target, obj, tt[eventId][i][1] != null); + for (var pLoop = 0; pLoop < pathInfo.end; pLoop++) { + currentTarget = pathInfo.path[pLoop]; + // this is a single event registration handler. + if (tt[eventId][i][1] == null || matchesSelector(currentTarget, tt[eventId][i][1], obj)) { + tt[eventId][i][0].apply(currentTarget, [ e ]); + break; + } + } + } + } + } + } + } + }, + clearSingle = function () { + tt.down = false; + }, + clearDouble = function () { + tt.taps = 0; + }; + + DefaultHandler(obj, "mousedown", down); + DefaultHandler(obj, "mouseup", up); + } + // add this child selector (it can be null, that's fine). + obj.__taTapHandler.downSelectors.push(children); + + obj.__taTapHandler[evt].push([fn, children]); + // the unstore function removes this function from the object's listener list for this type. + fn.__taUnstore = function () { + _d(obj.__taTapHandler[evt], fn); + }; + } + }; + }, + meeHelper = function (type, evt, obj, target) { + for (var i in obj.__tamee[type]) { + if (obj.__tamee[type].hasOwnProperty(i)) { + obj.__tamee[type][i].apply(target, [ evt ]); + } + } + }, + MouseEnterExitHandler = function () { + var activeElements = []; + return function (obj, evt, fn, children) { + if (!obj.__tamee) { + // __tamee holds a flag saying whether the mouse is currently "in" the element, and a list of + // both mouseenter and mouseexit functions. + obj.__tamee = { over: false, mouseenter: [], mouseexit: [] }; + // register over and out functions + var over = function (e) { + var t = _t(e); + if ((children == null && (t == obj && !obj.__tamee.over)) || (matchesSelector(t, children, obj) && (t.__tamee == null || !t.__tamee.over))) { + meeHelper("mouseenter", e, obj, t); + t.__tamee = t.__tamee || {}; + t.__tamee.over = true; + activeElements.push(t); + } + }, + out = function (e) { + var t = _t(e); + // is the current target one of the activeElements? and is the + // related target NOT a descendant of it? + for (var i = 0; i < activeElements.length; i++) { + if (t == activeElements[i] && !matchesSelector((e.relatedTarget || e.toElement), "*", t)) { + t.__tamee.over = false; + activeElements.splice(i, 1); + meeHelper("mouseexit", e, obj, t); + } + } + }; + + _bind(obj, "mouseover", _curryChildFilter(children, obj, over, "mouseover"), over); + _bind(obj, "mouseout", _curryChildFilter(children, obj, out, "mouseout"), out); + } + + fn.__taUnstore = function () { + delete obj.__tamee[evt][fn.__tauid]; + }; + + _store(obj, evt, fn); + obj.__tamee[evt][fn.__tauid] = fn; + }; + }, + isTouchDevice = "ontouchstart" in document.documentElement, + isMouseDevice = "onmousedown" in document.documentElement, + touchMap = { "mousedown": "touchstart", "mouseup": "touchend", "mousemove": "touchmove" }, + touchstart = "touchstart", touchend = "touchend", touchmove = "touchmove", + iev = (function () { + var rv = -1; + if (navigator.appName == 'Microsoft Internet Explorer') { + var ua = navigator.userAgent, + re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } + return rv; + })(), + isIELT9 = iev > -1 && iev < 9, + _genLoc = function (e, prefix) { + if (e == null) return [ 0, 0 ]; + var ts = _touches(e), t = _getTouch(ts, 0); + return [t[prefix + "X"], t[prefix + "Y"]]; + }, + _pageLocation = function (e) { + if (e == null) return [ 0, 0 ]; + if (isIELT9) { + return [ e.clientX + document.documentElement.scrollLeft, e.clientY + document.documentElement.scrollTop ]; + } + else { + return _genLoc(e, "page"); + } + }, + _screenLocation = function (e) { + return _genLoc(e, "screen"); + }, + _clientLocation = function (e) { + return _genLoc(e, "client"); + }, + _getTouch = function (touches, idx) { + return touches.item ? touches.item(idx) : touches[idx]; + }, + _touches = function (e) { + return e.touches && e.touches.length > 0 ? e.touches : + e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches : + e.targetTouches && e.targetTouches.length > 0 ? e.targetTouches : + [ e ]; + }, + _touchCount = function (e) { + return _touches(e).length; + }, + //http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html + _bind = function (obj, type, fn, originalFn) { + _store(obj, type, fn); + originalFn.__tauid = fn.__tauid; + if (obj.addEventListener) + obj.addEventListener(type, fn, false); + else if (obj.attachEvent) { + var key = type + fn.__tauid; + obj["e" + key] = fn; + // TODO look at replacing with .call(..) + obj[key] = function () { + obj["e" + key] && obj["e" + key](window.event); + }; + obj.attachEvent("on" + type, obj[key]); + } + }, + _unbind = function (obj, type, fn) { + if (fn == null) return; + _each(obj, function () { + var _el = _gel(this); + _unstore(_el, type, fn); + // it has been bound if there is a tauid. otherwise it was not bound and we can ignore it. + if (fn.__tauid != null) { + if (_el.removeEventListener) { + _el.removeEventListener(type, fn, false); + if (isTouchDevice && touchMap[type]) _el.removeEventListener(touchMap[type], fn, false); + } + else if (this.detachEvent) { + var key = type + fn.__tauid; + _el[key] && _el.detachEvent("on" + type, _el[key]); + _el[key] = null; + _el["e" + key] = null; + } + } + + // if a touch event was also registered, deregister now. + if (fn.__taTouchProxy) { + _unbind(obj, fn.__taTouchProxy[1], fn.__taTouchProxy[0]); + } + }); + }, + _each = function (obj, fn) { + if (obj == null) return; + // if a list (or list-like), use it. if a string, get a list + // by running the string through querySelectorAll. else, assume + // it's an Element. + // obj.top is "unknown" in IE8. + obj = (typeof Window !== "undefined" && (typeof obj.top !== "unknown" && obj == obj.top)) ? [ obj ] : + (typeof obj !== "string") && (obj.tagName == null && obj.length != null) ? obj : + typeof obj === "string" ? document.querySelectorAll(obj) + : [ obj ]; + + for (var i = 0; i < obj.length; i++) + fn.apply(obj[i]); + }, + _uuid = function () { + return ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + })); + }; + + /** + * Mottle offers support for abstracting out the differences + * between touch and mouse devices, plus "smart click" functionality + * (don't fire click if the mouse has moved between mousedown and mouseup), + * and synthesized click/tap events. + * @class Mottle + * @constructor + * @param {Object} params Constructor params + * @param {Number} [params.clickThreshold=250] Threshold, in milliseconds beyond which a touchstart followed by a touchend is not considered to be a click. + * @param {Number} [params.dblClickThreshold=450] Threshold, in milliseconds beyond which two successive tap events are not considered to be a click. + * @param {Boolean} [params.smartClicks=false] If true, won't fire click events if the mouse has moved between mousedown and mouseup. Note that this functionality + * requires that Mottle consume the mousedown event, and so may not be viable in all use cases. + */ + root.Mottle = function (params) { + params = params || {}; + var clickThreshold = params.clickThreshold || 250, + dblClickThreshold = params.dblClickThreshold || 450, + mouseEnterExitHandler = new MouseEnterExitHandler(), + tapHandler = new TapHandler(clickThreshold, dblClickThreshold), + _smartClicks = params.smartClicks, + _doBind = function (obj, evt, fn, children) { + if (fn == null) return; + _each(obj, function () { + var _el = _gel(this); + if (_smartClicks && evt === "click") + SmartClickHandler(_el, evt, fn, children); + else if (evt === "tap" || evt === "dbltap" || evt === "contextmenu") { + tapHandler(_el, evt, fn, children); + } + else if (evt === "mouseenter" || evt == "mouseexit") + mouseEnterExitHandler(_el, evt, fn, children); + else + DefaultHandler(_el, evt, fn, children); + }); + }; + + /** + * Removes an element from the DOM, and deregisters all event handlers for it. You should use this + * to ensure you don't leak memory. + * @method remove + * @param {String|Element} el Element, or id of the element, to remove. + * @return {Mottle} The current Mottle instance; you can chain this method. + */ + this.remove = function (el) { + _each(el, function () { + var _el = _gel(this); + if (_el.__ta) { + for (var evt in _el.__ta) { + if (_el.__ta.hasOwnProperty(evt)) { + for (var h in _el.__ta[evt]) { + if (_el.__ta[evt].hasOwnProperty(h)) + _unbind(_el, evt, _el.__ta[evt][h]); + } + } + } + } + _el.parentNode && _el.parentNode.removeChild(_el); + }); + return this; + }; + + /** + * Register an event handler, optionally as a delegate for some set of descendant elements. Note + * that this method takes either 3 or 4 arguments - if you supply 3 arguments it is assumed you have + * omitted the `children` parameter, and that the event handler should be bound directly to the given element. + * @method on + * @param {Element[]|Element|String} el Either an Element, or a CSS spec for a list of elements, or an array of Elements. + * @param {String} [children] Comma-delimited list of selectors identifying allowed children. + * @param {String} event Event ID. + * @param {Function} fn Event handler function. + * @return {Mottle} The current Mottle instance; you can chain this method. + */ + this.on = function (el, event, children, fn) { + var _el = arguments[0], + _c = arguments.length == 4 ? arguments[2] : null, + _e = arguments[1], + _f = arguments[arguments.length - 1]; + + _doBind(_el, _e, _f, _c); + return this; + }; + + /** + * Cancel delegate event handling for the given function. Note that unlike with 'on' you do not supply + * a list of child selectors here: it removes event delegation from all of the child selectors for which the + * given function was registered (if any). + * @method off + * @param {Element[]|Element|String} el Element - or ID of element - from which to remove event listener. + * @param {String} event Event ID. + * @param {Function} fn Event handler function. + * @return {Mottle} The current Mottle instance; you can chain this method. + */ + this.off = function (el, event, fn) { + _unbind(el, event, fn); + return this; + }; + + /** + * Triggers some event for a given element. + * @method trigger + * @param {Element} el Element for which to trigger the event. + * @param {String} event Event ID. + * @param {Event} originalEvent The original event. Should be optional of course, but currently is not, due + * to the jsPlumb use case that caused this method to be added. + * @param {Object} [payload] Optional object to set as `payload` on the generated event; useful for message passing. + * @return {Mottle} The current Mottle instance; you can chain this method. + */ + this.trigger = function (el, event, originalEvent, payload) { + // MouseEvent undefined in old IE; that's how we know it's a mouse event. A fine Microsoft paradox. + var originalIsMouse = isMouseDevice && (typeof MouseEvent === "undefined" || originalEvent == null || originalEvent.constructor === MouseEvent); + + var eventToBind = (isTouchDevice && !isMouseDevice && touchMap[event]) ? touchMap[event] : event, + bindingAMouseEvent = !(isTouchDevice && !isMouseDevice && touchMap[event]); + + var pl = _pageLocation(originalEvent), sl = _screenLocation(originalEvent), cl = _clientLocation(originalEvent); + _each(el, function () { + var _el = _gel(this), evt; + originalEvent = originalEvent || { + screenX: sl[0], + screenY: sl[1], + clientX: cl[0], + clientY: cl[1] + }; + + var _decorate = function (_evt) { + if (payload) _evt.payload = payload; + }; + + var eventGenerators = { + "TouchEvent": function (evt) { + + var touchList = _touchAndList(window, _el, 0, pl[0], pl[1], sl[0], sl[1], cl[0], cl[1]), + init = evt.initTouchEvent || evt.initEvent; + + init(eventToBind, true, true, window, null, sl[0], sl[1], + cl[0], cl[1], false, false, false, false, + touchList, touchList, touchList, 1, 0); + }, + "MouseEvents": function (evt) { + evt.initMouseEvent(eventToBind, true, true, window, 0, + sl[0], sl[1], + cl[0], cl[1], + false, false, false, false, 1, _el); + } + }; + + if (document.createEvent) { + + var ite = !bindingAMouseEvent && !originalIsMouse && (isTouchDevice && touchMap[event]), + evtName = ite ? "TouchEvent" : "MouseEvents"; + + evt = document.createEvent(evtName); + eventGenerators[evtName](evt); + _decorate(evt); + _el.dispatchEvent(evt); + } + else if (document.createEventObject) { + evt = document.createEventObject(); + evt.eventType = evt.eventName = eventToBind; + evt.screenX = sl[0]; + evt.screenY = sl[1]; + evt.clientX = cl[0]; + evt.clientY = cl[1]; + _decorate(evt); + _el.fireEvent('on' + eventToBind, evt); + } + }); + return this; + } + }; + + /** + * Static method to assist in 'consuming' an element: uses `stopPropagation` where available, or sets + * `e.returnValue=false` where it is not. + * @method Mottle.consume + * @param {Event} e Event to consume + * @param {Boolean} [doNotPreventDefault=false] If true, does not call `preventDefault()` on the event. + */ + root.Mottle.consume = function (e, doNotPreventDefault) { + if (e.stopPropagation) + e.stopPropagation(); + else + e.returnValue = false; + + if (!doNotPreventDefault && e.preventDefault) + e.preventDefault(); + }; + + /** + * Gets the page location corresponding to the given event. For touch events this means get the page location of the first touch. + * @method Mottle.pageLocation + * @param {Event} e Event to get page location for. + * @return {Number[]} [left, top] for the given event. + */ + root.Mottle.pageLocation = _pageLocation; + + /** + * Forces touch events to be turned "on". Useful for testing: even if you don't have a touch device, you can still + * trigger a touch event when this is switched on and it will be captured and acted on. + * @method setForceTouchEvents + * @param {Boolean} value If true, force touch events to be on. + */ + root.Mottle.setForceTouchEvents = function (value) { + isTouchDevice = value; + }; + + /** + * Forces mouse events to be turned "on". Useful for testing: even if you don't have a mouse, you can still + * trigger a mouse event when this is switched on and it will be captured and acted on. + * @method setForceMouseEvents + * @param {Boolean} value If true, force mouse events to be on. + */ + root.Mottle.setForceMouseEvents = function (value) { + isMouseDevice = value; + }; + + root.Mottle.version = "0.8.0"; + + if (typeof exports !== "undefined") { + exports.Mottle = root.Mottle; + } + +}).call(typeof window === "undefined" ? this : window); + +/** + drag/drop functionality for use with jsPlumb but with + no knowledge of jsPlumb. supports multiple scopes (separated by whitespace), dragging + multiple elements, constrain to parent, drop filters, drag start filters, custom + css classes. + + a lot of the functionality of this script is expected to be plugged in: + + addClass + removeClass + + addEvent + removeEvent + + getPosition + setPosition + getSize + + indexOf + intersects + + the name came from here: + + http://mrsharpoblunto.github.io/foswig.js/ + + copyright 2016 jsPlumb + */ + +;(function() { + + "use strict"; + var root = this; + + var _suggest = function(list, item, head) { + if (list.indexOf(item) === -1) { + head ? list.unshift(item) : list.push(item); + return true; + } + return false; + }; + + var _vanquish = function(list, item) { + var idx = list.indexOf(item); + if (idx !== -1) list.splice(idx, 1); + }; + + var _difference = function(l1, l2) { + var d = []; + for (var i = 0; i < l1.length; i++) { + if (l2.indexOf(l1[i]) === -1) + d.push(l1[i]); + } + return d; + }; + + var _isString = function(f) { + return f == null ? false : (typeof f === "string" || f.constructor === String); + }; + + var getOffsetRect = function (elem) { + // (1) + var box = elem.getBoundingClientRect(), + body = document.body, + docElem = document.documentElement, + // (2) + scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop, + scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft, + // (3) + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + // (4) + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; + + return { top: Math.round(top), left: Math.round(left) }; + }; + + var matchesSelector = function(el, selector, ctx) { + ctx = ctx || el.parentNode; + var possibles = ctx.querySelectorAll(selector); + for (var i = 0; i < possibles.length; i++) { + if (possibles[i] === el) + return true; + } + return false; + }; + + var findDelegateElement = function(parentElement, childElement, selector) { + if (matchesSelector(childElement, selector, parentElement)) { + return childElement; + } else { + var currentParent = childElement.parentNode; + while (currentParent != null && currentParent !== parentElement) { + if (matchesSelector(currentParent, selector, parentElement)) { + return currentParent; + } else { + currentParent = currentParent.parentNode; + } + } + } + }; + + /** + * Finds all elements matching the given selector, for the given parent. In order to support "scoped root" selectors, + * ie. things like "> .someClass", that is .someClass elements that are direct children of `parentElement`, we have to + * jump through a small hoop here: when a delegate draggable is registered, we write a `katavorio-draggable` attribute + * on the element on which the draggable is registered. Then when this method runs, we grab the value of that attribute and + * prepend it as part of the selector we're looking for. So "> .someClass" ends up being written as + * "[katavorio-draggable='...' > .someClass]", which works with querySelectorAll. + * + * @param availableSelectors + * @param parentElement + * @param childElement + * @returns {*} + */ + var findMatchingSelector = function(availableSelectors, parentElement, childElement) { + var el = null; + var draggableId = parentElement.getAttribute("katavorio-draggable"), + prefix = draggableId != null ? "[katavorio-draggable='" + draggableId + "'] " : ""; + + for (var i = 0; i < availableSelectors.length; i++) { + el = findDelegateElement(parentElement, childElement, prefix + availableSelectors[i].selector); + if (el != null) { + if (availableSelectors[i].filter) { + var matches = matchesSelector(childElement, availableSelectors[i].filter, el), + exclude = availableSelectors[i].filterExclude === true; + + if ( (exclude && !matches) || matches) { + return null; + } + + } + return [ availableSelectors[i], el ]; + } + } + return null; + }; + + var iev = (function() { + var rv = -1; + if (navigator.appName === 'Microsoft Internet Explorer') { + var ua = navigator.userAgent, + re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } + return rv; + })(), + DEFAULT_GRID_X = 10, + DEFAULT_GRID_Y = 10, + isIELT9 = iev > -1 && iev < 9, + isIE9 = iev === 9, + _pl = function(e) { + if (isIELT9) { + return [ e.clientX + document.documentElement.scrollLeft, e.clientY + document.documentElement.scrollTop ]; + } + else { + var ts = _touches(e), t = _getTouch(ts, 0); + // for IE9 pageX might be null if the event was synthesized. We try for pageX/pageY first, + // falling back to clientX/clientY if necessary. In every other browser we want to use pageX/pageY. + return isIE9 ? [t.pageX || t.clientX, t.pageY || t.clientY] : [t.pageX, t.pageY]; + } + }, + _getTouch = function(touches, idx) { return touches.item ? touches.item(idx) : touches[idx]; }, + _touches = function(e) { + return e.touches && e.touches.length > 0 ? e.touches : + e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches : + e.targetTouches && e.targetTouches.length > 0 ? e.targetTouches : + [ e ]; + }, + _classes = { + delegatedDraggable:"katavorio-delegated-draggable", // elements that are the delegated drag handler for a bunch of other elements + draggable:"katavorio-draggable", // draggable elements + droppable:"katavorio-droppable", // droppable elements + drag : "katavorio-drag", // elements currently being dragged + selected:"katavorio-drag-selected", // elements in current drag selection + active : "katavorio-drag-active", // droppables that are targets of a currently dragged element + hover : "katavorio-drag-hover", // droppables over which a matching drag element is hovering + noSelect : "katavorio-drag-no-select", // added to the body to provide a hook to suppress text selection + ghostProxy:"katavorio-ghost-proxy", // added to a ghost proxy element in use when a drag has exited the bounds of its parent. + clonedDrag:"katavorio-clone-drag" // added to a node that is a clone of an element created at the start of a drag + }, + _defaultScope = "katavorio-drag-scope", + _events = [ "stop", "start", "drag", "drop", "over", "out", "beforeStart" ], + _devNull = function() {}, + _true = function() { return true; }, + _foreach = function(l, fn, from) { + for (var i = 0; i < l.length; i++) { + if (l[i] != from) + fn(l[i]); + } + }, + _setDroppablesActive = function(dd, val, andHover, drag) { + _foreach(dd, function(e) { + e.setActive(val); + if (val) e.updatePosition(); + if (andHover) e.setHover(drag, val); + }); + }, + _each = function(obj, fn) { + if (obj == null) return; + obj = !_isString(obj) && (obj.tagName == null && obj.length != null) ? obj : [ obj ]; + for (var i = 0; i < obj.length; i++) + fn.apply(obj[i], [ obj[i] ]); + }, + _consume = function(e) { + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + else { + e.returnValue = false; + } + }, + _defaultInputFilterSelector = "input,textarea,select,button,option", + // + // filters out events on all input elements, like textarea, checkbox, input, select. + _inputFilter = function(e, el, _katavorio) { + var t = e.srcElement || e.target; + return !matchesSelector(t, _katavorio.getInputFilterSelector(), el); + }; + + var Super = function(el, params, css, scope) { + this.params = params || {}; + this.el = el; + this.params.addClass(this.el, this._class); + this.uuid = _uuid(); + var enabled = true; + this.setEnabled = function(e) { enabled = e; }; + this.isEnabled = function() { return enabled; }; + this.toggleEnabled = function() { enabled = !enabled; }; + this.setScope = function(scopes) { + this.scopes = scopes ? scopes.split(/\s+/) : [ scope ]; + }; + this.addScope = function(scopes) { + var m = {}; + _each(this.scopes, function(s) { m[s] = true;}); + _each(scopes ? scopes.split(/\s+/) : [], function(s) { m[s] = true;}); + this.scopes = []; + for (var i in m) this.scopes.push(i); + }; + this.removeScope = function(scopes) { + var m = {}; + _each(this.scopes, function(s) { m[s] = true;}); + _each(scopes ? scopes.split(/\s+/) : [], function(s) { delete m[s];}); + this.scopes = []; + for (var i in m) this.scopes.push(i); + }; + this.toggleScope = function(scopes) { + var m = {}; + _each(this.scopes, function(s) { m[s] = true;}); + _each(scopes ? scopes.split(/\s+/) : [], function(s) { + if (m[s]) delete m[s]; + else m[s] = true; + }); + this.scopes = []; + for (var i in m) this.scopes.push(i); + }; + this.setScope(params.scope); + this.k = params.katavorio; + return params.katavorio; + }; + + var TRUE = function() { return true; }; + var FALSE = function() { return false; }; + + var Drag = function(el, params, css, scope) { + this._class = css.draggable; + var k = Super.apply(this, arguments); + this.rightButtonCanDrag = this.params.rightButtonCanDrag; + var downAt = [0,0], posAtDown = null, pagePosAtDown = null, pageDelta = [0,0], moving = false, initialScroll = [0,0], + consumeStartEvent = this.params.consumeStartEvent !== false, + dragEl = this.el, + clone = this.params.clone, + scroll = this.params.scroll, + _multipleDrop = params.multipleDrop !== false, + isConstrained = false, + useGhostProxy = params.ghostProxy === true ? TRUE : params.ghostProxy && typeof params.ghostProxy === "function" ? params.ghostProxy : FALSE, + ghostProxy = function(el) { return el.cloneNode(true); }, + elementToDrag = null, + availableSelectors = [], + activeSelectorParams = null, // which, if any, selector config is currently active. + ghostProxyParent = params.ghostProxyParent, + currentParentPosition, + ghostParentPosition, + ghostDx, + ghostDy; + + // if an initial selector was provided, push the entire set of params as a selector config. + if (params.selector) { + var draggableId = el.getAttribute("katavorio-draggable"); + if (draggableId == null) { + draggableId = "" + new Date().getTime(); + el.setAttribute("katavorio-draggable", draggableId); + } + + availableSelectors.push(params); + } + + var snapThreshold = params.snapThreshold, + _snap = function(pos, gridX, gridY, thresholdX, thresholdY) { + var _dx = Math.floor(pos[0] / gridX), + _dxl = gridX * _dx, + _dxt = _dxl + gridX, + _x = Math.abs(pos[0] - _dxl) <= thresholdX ? _dxl : Math.abs(_dxt - pos[0]) <= thresholdX ? _dxt : pos[0]; + + var _dy = Math.floor(pos[1] / gridY), + _dyl = gridY * _dy, + _dyt = _dyl + gridY, + _y = Math.abs(pos[1] - _dyl) <= thresholdY ? _dyl : Math.abs(_dyt - pos[1]) <= thresholdY ? _dyt : pos[1]; + + return [ _x, _y]; + }; + + this.posses = []; + this.posseRoles = {}; + + this.toGrid = function(pos) { + if (this.params.grid == null) { + return pos; + } + else { + var tx = this.params.grid ? this.params.grid[0] / 2 : snapThreshold ? snapThreshold : DEFAULT_GRID_X / 2, + ty = this.params.grid ? this.params.grid[1] / 2 : snapThreshold ? snapThreshold : DEFAULT_GRID_Y / 2; + + return _snap(pos, this.params.grid[0], this.params.grid[1], tx, ty); + } + }; + + this.snap = function(x, y) { + if (dragEl == null) return; + x = x || (this.params.grid ? this.params.grid[0] : DEFAULT_GRID_X); + y = y || (this.params.grid ? this.params.grid[1] : DEFAULT_GRID_Y); + var p = this.params.getPosition(dragEl), + tx = this.params.grid ? this.params.grid[0] / 2 : snapThreshold, + ty = this.params.grid ? this.params.grid[1] / 2 : snapThreshold, + snapped = _snap(p, x, y, tx, ty); + + this.params.setPosition(dragEl, snapped); + return snapped; + }; + + this.setUseGhostProxy = function(val) { + useGhostProxy = val ? TRUE : FALSE; + }; + + var constrain; + var negativeFilter = function(pos) { + return (params.allowNegative === false) ? [ Math.max (0, pos[0]), Math.max(0, pos[1]) ] : pos; + }; + + var _setConstrain = function(value) { + constrain = typeof value === "function" ? value : value ? function(pos, dragEl, _constrainRect, _size) { + return negativeFilter([ + Math.max(0, Math.min(_constrainRect.w - _size[0], pos[0])), + Math.max(0, Math.min(_constrainRect.h - _size[1], pos[1])) + ]); + }.bind(this) : function(pos) { return negativeFilter(pos); }; + }.bind(this); + + _setConstrain(typeof this.params.constrain === "function" ? this.params.constrain : (this.params.constrain || this.params.containment)); + + + /** + * Sets whether or not the Drag is constrained. A value of 'true' means constrain to parent bounds; a function + * will be executed and returns true if the position is allowed. + * @param value + */ + this.setConstrain = function(value) { + _setConstrain(value); + }; + + var revertFunction; + /** + * Sets a function to call on drag stop, which, if it returns true, indicates that the given element should + * revert to its position before the previous drag. + * @param fn + */ + this.setRevert = function(fn) { + revertFunction = fn; + }; + + if (this.params.revert) { + revertFunction = this.params.revert; + } + + var _assignId = function(obj) { + if (typeof obj === "function") { + obj._katavorioId = _uuid(); + return obj._katavorioId; + } else { + return obj; + } + }, + // a map of { spec -> [ fn, exclusion ] } entries. + _filters = {}, + _testFilter = function(e) { + for (var key in _filters) { + var f = _filters[key]; + var rv = f[0](e); + if (f[1]) rv = !rv; + if (!rv) return false; + } + return true; + }, + _setFilter = this.setFilter = function(f, _exclude) { + if (f) { + var key = _assignId(f); + _filters[key] = [ + function(e) { + var t = e.srcElement || e.target, m; + if (_isString(f)) { + m = matchesSelector(t, f, el); + } + else if (typeof f === "function") { + m = f(e, el); + } + return m; + }, + _exclude !== false + ]; + + } + }, + _addFilter = this.addFilter = _setFilter, + _removeFilter = this.removeFilter = function(f) { + var key = typeof f === "function" ? f._katavorioId : f; + delete _filters[key]; + }; + + this.clearAllFilters = function() { + _filters = {}; + }; + + this.canDrag = this.params.canDrag || _true; + + var constrainRect, + matchingDroppables = [], + intersectingDroppables = []; + + this.addSelector = function(params) { + if (params.selector) { + availableSelectors.push(params); + } + }; + + this.downListener = function(e) { + if (e.defaultPrevented) { return; } + var isNotRightClick = this.rightButtonCanDrag || (e.which !== 3 && e.button !== 2); + if (isNotRightClick && this.isEnabled() && this.canDrag()) { + + var _f = _testFilter(e) && _inputFilter(e, this.el, this.k); + if (_f) { + + activeSelectorParams = null; + elementToDrag = null; + + // if (selector) { + // elementToDrag = findDelegateElement(this.el, e.target || e.srcElement, selector); + // if(elementToDrag == null) { + // return; + // } + // } + if (availableSelectors.length > 0) { + var match = findMatchingSelector(availableSelectors, this.el, e.target || e.srcElement); + if (match != null) { + activeSelectorParams = match[0]; + elementToDrag = match[1]; + } + // elementToDrag = findDelegateElement(this.el, e.target || e.srcElement, selector); + if(elementToDrag == null) { + return; + } + } + else { + elementToDrag = this.el; + } + + if (clone) { + dragEl = elementToDrag.cloneNode(true); + this.params.addClass(dragEl, _classes.clonedDrag); + + dragEl.setAttribute("id", null); + dragEl.style.position = "absolute"; + + if (this.params.parent != null) { + var p = this.params.getPosition(this.el); + dragEl.style.left = p[0] + "px"; + dragEl.style.top = p[1] + "px"; + this.params.parent.appendChild(dragEl); + } else { + // the clone node is added to the body; getOffsetRect gives us a value + // relative to the body. + var b = getOffsetRect(elementToDrag); + dragEl.style.left = b.left + "px"; + dragEl.style.top = b.top + "px"; + + document.body.appendChild(dragEl); + } + + } else { + dragEl = elementToDrag; + } + + consumeStartEvent && _consume(e); + downAt = _pl(e); + if (dragEl && dragEl.parentNode) + { + initialScroll = [dragEl.parentNode.scrollLeft, dragEl.parentNode.scrollTop]; + } + // + this.params.bind(document, "mousemove", this.moveListener); + this.params.bind(document, "mouseup", this.upListener); + k.markSelection(this); + k.markPosses(this); + this.params.addClass(document.body, css.noSelect); + _dispatch("beforeStart", {el:this.el, pos:posAtDown, e:e, drag:this}); + } + else if (this.params.consumeFilteredEvents) { + _consume(e); + } + } + }.bind(this); + + this.moveListener = function(e) { + if (downAt) { + if (!moving) { + var _continue = _dispatch("start", {el:this.el, pos:posAtDown, e:e, drag:this}); + if (_continue !== false) { + if (!downAt) { + return; + } + this.mark(true); + moving = true; + } else { + this.abort(); + } + } + + // it is possible that the start event caused the drag to be aborted. So we check + // again that we are currently dragging. + if (downAt) { + intersectingDroppables.length = 0; + var pos = _pl(e), dx = pos[0] - downAt[0], dy = pos[1] - downAt[1], + z = this.params.ignoreZoom ? 1 : k.getZoom(); + if (dragEl && dragEl.parentNode) + { + dx += dragEl.parentNode.scrollLeft - initialScroll[0]; + dy += dragEl.parentNode.scrollTop - initialScroll[1]; + } + dx /= z; + dy /= z; + this.moveBy(dx, dy, e); + k.updateSelection(dx, dy, this); + k.updatePosses(dx, dy, this); + } + } + }.bind(this); + + this.upListener = function(e) { + if (downAt) { + downAt = null; + this.params.unbind(document, "mousemove", this.moveListener); + this.params.unbind(document, "mouseup", this.upListener); + this.params.removeClass(document.body, css.noSelect); + this.unmark(e); + k.unmarkSelection(this, e); + k.unmarkPosses(this, e); + this.stop(e); + + k.notifyPosseDragStop(this, e); + moving = false; + intersectingDroppables.length = 0; + + if (clone) { + dragEl && dragEl.parentNode && dragEl.parentNode.removeChild(dragEl); + dragEl = null; + } else { + if (revertFunction && revertFunction(dragEl, this.params.getPosition(dragEl)) === true) { + this.params.setPosition(dragEl, posAtDown); + _dispatch("revert", dragEl); + } + } + + } + }.bind(this); + + this.getFilters = function() { return _filters; }; + + this.abort = function() { + if (downAt != null) { + this.upListener(); + } + }; + + /** + * Returns the element that was last dragged. This may be some original element from the DOM, or if `clone` is + * set, then its actually a copy of some original DOM element. In some client calls to this method, it is the + * actual element that was dragged that is desired. In others, it is the original DOM element that the user + * wishes to get - in which case, pass true for `retrieveOriginalElement`. + * + * @returns {*} + */ + this.getDragElement = function(retrieveOriginalElement) { + return retrieveOriginalElement ? elementToDrag || this.el : dragEl || this.el; + }; + + var listeners = {"start":[], "drag":[], "stop":[], "over":[], "out":[], "beforeStart":[], "revert":[] }; + if (params.events.start) listeners.start.push(params.events.start); + if (params.events.beforeStart) listeners.beforeStart.push(params.events.beforeStart); + if (params.events.stop) listeners.stop.push(params.events.stop); + if (params.events.drag) listeners.drag.push(params.events.drag); + if (params.events.revert) listeners.revert.push(params.events.revert); + + this.on = function(evt, fn) { + if (listeners[evt]) listeners[evt].push(fn); + }; + + this.off = function(evt, fn) { + if (listeners[evt]) { + var l = []; + for (var i = 0; i < listeners[evt].length; i++) { + if (listeners[evt][i] !== fn) l.push(listeners[evt][i]); + } + listeners[evt] = l; + } + }; + + var _dispatch = function(evt, value) { + var result = null; + if (activeSelectorParams && activeSelectorParams[evt]) { + result = activeSelectorParams[evt](value); + } else if (listeners[evt]) { + for (var i = 0; i < listeners[evt].length; i++) { + try { + var v = listeners[evt][i](value); + if (v != null) { + result = v; + } + } + catch (e) { } + } + } + return result; + }; + + this.notifyStart = function(e) { + _dispatch("start", {el:this.el, pos:this.params.getPosition(dragEl), e:e, drag:this}); + }; + + this.stop = function(e, force) { + if (force || moving) { + var positions = [], + sel = k.getSelection(), + dPos = this.params.getPosition(dragEl); + + if (sel.length > 0) { + for (var i = 0; i < sel.length; i++) { + var p = this.params.getPosition(sel[i].el); + positions.push([ sel[i].el, { left: p[0], top: p[1] }, sel[i] ]); + } + } + else { + positions.push([ dragEl, {left:dPos[0], top:dPos[1]}, this ]); + } + + _dispatch("stop", { + el: dragEl, + pos: ghostProxyOffsets || dPos, + finalPos:dPos, + e: e, + drag: this, + selection:positions + }); + } + }; + + this.mark = function(andNotify) { + posAtDown = this.params.getPosition(dragEl); + pagePosAtDown = this.params.getPosition(dragEl, true); + pageDelta = [pagePosAtDown[0] - posAtDown[0], pagePosAtDown[1] - posAtDown[1]]; + this.size = this.params.getSize(dragEl); + matchingDroppables = k.getMatchingDroppables(this); + _setDroppablesActive(matchingDroppables, true, false, this); + this.params.addClass(dragEl, this.params.dragClass || css.drag); + + var cs; + if (this.params.getConstrainingRectangle) { + cs = this.params.getConstrainingRectangle(dragEl) + } else { + cs = this.params.getSize(dragEl.parentNode); + } + constrainRect = {w: cs[0], h: cs[1]}; + + ghostDx = 0; + ghostDy = 0; + + if (andNotify) { + k.notifySelectionDragStart(this); + } + }; + var ghostProxyOffsets; + this.unmark = function(e, doNotCheckDroppables) { + _setDroppablesActive(matchingDroppables, false, true, this); + + if (isConstrained && useGhostProxy(elementToDrag, dragEl)) { + ghostProxyOffsets = [dragEl.offsetLeft - ghostDx, dragEl.offsetTop - ghostDy]; + dragEl.parentNode.removeChild(dragEl); + dragEl = elementToDrag; + } + else { + ghostProxyOffsets = null; + } + + this.params.removeClass(dragEl, this.params.dragClass || css.drag); + matchingDroppables.length = 0; + isConstrained = false; + if (!doNotCheckDroppables) { + if (intersectingDroppables.length > 0 && ghostProxyOffsets) { + params.setPosition(elementToDrag, ghostProxyOffsets); + } + intersectingDroppables.sort(_rankSort); + for (var i = 0; i < intersectingDroppables.length; i++) { + var retVal = intersectingDroppables[i].drop(this, e); + if (retVal === true) break; + } + } + }; + this.moveBy = function(dx, dy, e) { + intersectingDroppables.length = 0; + + var desiredLoc = this.toGrid([posAtDown[0] + dx, posAtDown[1] + dy]), + cPos = constrain(desiredLoc, dragEl, constrainRect, this.size); + + // if we should use a ghost proxy... + if (useGhostProxy(this.el, dragEl)) { + // and the element has been dragged outside of its parent bounds + if (desiredLoc[0] !== cPos[0] || desiredLoc[1] !== cPos[1]) { + + // ...if ghost proxy not yet created + if (!isConstrained) { + // create it + var gp = ghostProxy(elementToDrag); + params.addClass(gp, _classes.ghostProxy); + + if (ghostProxyParent) { + ghostProxyParent.appendChild(gp); + // find offset between drag el's parent the ghost parent + currentParentPosition = params.getPosition(elementToDrag.parentNode, true); + ghostParentPosition = params.getPosition(params.ghostProxyParent, true); + ghostDx = currentParentPosition[0] - ghostParentPosition[0]; + ghostDy = currentParentPosition[1] - ghostParentPosition[1]; + + } else { + elementToDrag.parentNode.appendChild(gp); + } + + // the ghost proxy is the drag element + dragEl = gp; + // set this flag so we dont recreate the ghost proxy + isConstrained = true; + } + // now the drag position can be the desired position, as the ghost proxy can support it. + cPos = desiredLoc; + } + else { + // if the element is not outside of its parent bounds, and ghost proxy is in place, + if (isConstrained) { + // remove the ghost proxy from the dom + dragEl.parentNode.removeChild(dragEl); + // reset the drag element to the original element + dragEl = elementToDrag; + // clear this flag. + isConstrained = false; + currentParentPosition = null; + ghostParentPosition = null; + ghostDx = 0; + ghostDy = 0; + } + } + } + + var rect = { x:cPos[0], y:cPos[1], w:this.size[0], h:this.size[1]}, + pageRect = { x:rect.x + pageDelta[0], y:rect.y + pageDelta[1], w:rect.w, h:rect.h}, + focusDropElement = null; + + this.params.setPosition(dragEl, [cPos[0] + ghostDx, cPos[1] + ghostDy]); + + for (var i = 0; i < matchingDroppables.length; i++) { + var r2 = { x:matchingDroppables[i].pagePosition[0], y:matchingDroppables[i].pagePosition[1], w:matchingDroppables[i].size[0], h:matchingDroppables[i].size[1]}; + if (this.params.intersects(pageRect, r2) && (_multipleDrop || focusDropElement == null || focusDropElement === matchingDroppables[i].el) && matchingDroppables[i].canDrop(this)) { + if (!focusDropElement) focusDropElement = matchingDroppables[i].el; + intersectingDroppables.push(matchingDroppables[i]); + matchingDroppables[i].setHover(this, true, e); + } + else if (matchingDroppables[i].isHover()) { + matchingDroppables[i].setHover(this, false, e); + } + } + + _dispatch("drag", {el:this.el, pos:cPos, e:e, drag:this}); + + /* test to see if the parent needs to be scrolled (future) + if (scroll) { + var pnsl = dragEl.parentNode.scrollLeft, pnst = dragEl.parentNode.scrollTop; + console.log("scroll!", pnsl, pnst); + }*/ + }; + this.destroy = function() { + this.params.unbind(this.el, "mousedown", this.downListener); + this.params.unbind(document, "mousemove", this.moveListener); + this.params.unbind(document, "mouseup", this.upListener); + this.downListener = null; + this.upListener = null; + this.moveListener = null; + }; + + // init:register mousedown, and perhaps set a filter + this.params.bind(this.el, "mousedown", this.downListener); + + // if handle provided, use that. otherwise, try to set a filter. + // note that a `handle` selector always results in filterExclude being set to false, ie. + // the selector defines the handle element(s). + if (this.params.handle) + _setFilter(this.params.handle, false); + else + _setFilter(this.params.filter, this.params.filterExclude); + }; + + var Drop = function(el, params, css, scope) { + this._class = css.droppable; + this.params = params || {}; + this.rank = params.rank || 0; + this._activeClass = this.params.activeClass || css.active; + this._hoverClass = this.params.hoverClass || css.hover; + Super.apply(this, arguments); + var hover = false; + this.allowLoopback = this.params.allowLoopback !== false; + + this.setActive = function(val) { + this.params[val ? "addClass" : "removeClass"](this.el, this._activeClass); + }; + + this.updatePosition = function() { + this.position = this.params.getPosition(this.el); + this.pagePosition = this.params.getPosition(this.el, true); + this.size = this.params.getSize(this.el); + }; + + this.canDrop = this.params.canDrop || function(drag) { + return true; + }; + + this.isHover = function() { return hover; }; + + this.setHover = function(drag, val, e) { + // if turning off hover but this was not the drag that caused the hover, ignore. + if (val || this.el._katavorioDragHover == null || this.el._katavorioDragHover === drag.el._katavorio) { + this.params[val ? "addClass" : "removeClass"](this.el, this._hoverClass); + this.el._katavorioDragHover = val ? drag.el._katavorio : null; + if (hover !== val) { + this.params.events[val ? "over" : "out"]({el: this.el, e: e, drag: drag, drop: this}); + } + hover = val; + } + }; + + /** + * A drop event. `drag` is the corresponding Drag object, which may be a Drag for some specific element, or it + * may be a Drag on some element acting as a delegate for elements contained within it. + * @param drag + * @param event + * @returns {*} + */ + this.drop = function(drag, event) { + return this.params.events["drop"]({ drag:drag, e:event, drop:this }); + }; + + this.destroy = function() { + this._class = null; + this._activeClass = null; + this._hoverClass = null; + hover = null; + }; + }; + + var _uuid = function() { + return ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); + return v.toString(16); + })); + }; + + var _rankSort = function(a,b) { + return a.rank < b.rank ? 1 : a.rank > b.rank ? -1 : 0; + }; + + var _gel = function(el) { + if (el == null) return null; + el = (typeof el === "string" || el.constructor === String) ? document.getElementById(el) : el; + if (el == null) return null; + el._katavorio = el._katavorio || _uuid(); + return el; + }; + + root.Katavorio = function(katavorioParams) { + + var _selection = [], + _selectionMap = {}; + + this._dragsByScope = {}; + this._dropsByScope = {}; + var _zoom = 1, + _reg = function(obj, map) { + _each(obj, function(_obj) { + for(var i = 0; i < _obj.scopes.length; i++) { + map[_obj.scopes[i]] = map[_obj.scopes[i]] || []; + map[_obj.scopes[i]].push(_obj); + } + }); + }, + _unreg = function(obj, map) { + var c = 0; + _each(obj, function(_obj) { + for(var i = 0; i < _obj.scopes.length; i++) { + if (map[_obj.scopes[i]]) { + var idx = katavorioParams.indexOf(map[_obj.scopes[i]], _obj); + if (idx !== -1) { + map[_obj.scopes[i]].splice(idx, 1); + c++; + } + } + } + }); + + return c > 0 ; + }, + _getMatchingDroppables = this.getMatchingDroppables = function(drag) { + var dd = [], _m = {}; + for (var i = 0; i < drag.scopes.length; i++) { + var _dd = this._dropsByScope[drag.scopes[i]]; + if (_dd) { + for (var j = 0; j < _dd.length; j++) { + if (_dd[j].canDrop(drag) && !_m[_dd[j].uuid] && (_dd[j].allowLoopback || _dd[j].el !== drag.el)) { + _m[_dd[j].uuid] = true; + dd.push(_dd[j]); + } + } + } + } + dd.sort(_rankSort); + return dd; + }, + _prepareParams = function(p) { + p = p || {}; + var _p = { + events:{} + }, i; + for (i in katavorioParams) _p[i] = katavorioParams[i]; + for (i in p) _p[i] = p[i]; + // events + + for (i = 0; i < _events.length; i++) { + _p.events[_events[i]] = p[_events[i]] || _devNull; + } + _p.katavorio = this; + return _p; + }.bind(this), + _mistletoe = function(existingDrag, params) { + for (var i = 0; i < _events.length; i++) { + if (params[_events[i]]) { + existingDrag.on(_events[i], params[_events[i]]); + } + } + }.bind(this), + _css = {}, + overrideCss = katavorioParams.css || {}, + _scope = katavorioParams.scope || _defaultScope; + + // prepare map of css classes based on defaults frst, then optional overrides + for (var i in _classes) _css[i] = _classes[i]; + for (var i in overrideCss) _css[i] = overrideCss[i]; + + var inputFilterSelector = katavorioParams.inputFilterSelector || _defaultInputFilterSelector; + /** + * Gets the selector identifying which input elements to filter from drag events. + * @method getInputFilterSelector + * @return {String} Current input filter selector. + */ + this.getInputFilterSelector = function() { return inputFilterSelector; }; + + /** + * Sets the selector identifying which input elements to filter from drag events. + * @method setInputFilterSelector + * @param {String} selector Input filter selector to set. + * @return {Katavorio} Current instance; method may be chained. + */ + this.setInputFilterSelector = function(selector) { + inputFilterSelector = selector; + return this; + }; + + /** + * Either makes the given element draggable, or identifies it as an element inside which some identified list + * of elements may be draggable. + * @param el + * @param params + * @returns {Array} + */ + this.draggable = function(el, params) { + var o = []; + _each(el, function (_el) { + _el = _gel(_el); + if (_el != null) { + if (_el._katavorioDrag == null) { + var p = _prepareParams(params); + _el._katavorioDrag = new Drag(_el, p, _css, _scope); + _reg(_el._katavorioDrag, this._dragsByScope); + o.push(_el._katavorioDrag); + katavorioParams.addClass(_el, p.selector ? _css.delegatedDraggable : _css.draggable); + } + else { + _mistletoe(_el._katavorioDrag, params); + } + } + }.bind(this)); + return o; + }; + + this.droppable = function(el, params) { + var o = []; + _each(el, function(_el) { + _el = _gel(_el); + if (_el != null) { + var drop = new Drop(_el, _prepareParams(params), _css, _scope); + _el._katavorioDrop = _el._katavorioDrop || []; + _el._katavorioDrop.push(drop); + _reg(drop, this._dropsByScope); + o.push(drop); + katavorioParams.addClass(_el, _css.droppable); + } + }.bind(this)); + return o; + }; + + /** + * @name Katavorio#select + * @function + * @desc Adds an element to the current selection (for multiple node drag) + * @param {Element|String} DOM element - or id of the element - to add. + */ + this.select = function(el) { + _each(el, function() { + var _el = _gel(this); + if (_el && _el._katavorioDrag) { + if (!_selectionMap[_el._katavorio]) { + _selection.push(_el._katavorioDrag); + _selectionMap[_el._katavorio] = [ _el, _selection.length - 1 ]; + katavorioParams.addClass(_el, _css.selected); + } + } + }); + return this; + }; + + /** + * @name Katavorio#deselect + * @function + * @desc Removes an element from the current selection (for multiple node drag) + * @param {Element|String} DOM element - or id of the element - to remove. + */ + this.deselect = function(el) { + _each(el, function() { + var _el = _gel(this); + if (_el && _el._katavorio) { + var e = _selectionMap[_el._katavorio]; + if (e) { + var _s = []; + for (var i = 0; i < _selection.length; i++) + if (_selection[i].el !== _el) _s.push(_selection[i]); + _selection = _s; + delete _selectionMap[_el._katavorio]; + katavorioParams.removeClass(_el, _css.selected); + } + } + }); + return this; + }; + + this.deselectAll = function() { + for (var i in _selectionMap) { + var d = _selectionMap[i]; + katavorioParams.removeClass(d[0], _css.selected); + } + + _selection.length = 0; + _selectionMap = {}; + }; + + this.markSelection = function(drag) { + _foreach(_selection, function(e) { e.mark(); }, drag); + }; + + this.markPosses = function(drag) { + if (drag.posses) { + _each(drag.posses, function(p) { + if (drag.posseRoles[p] && _posses[p]) { + _foreach(_posses[p].members, function (d) { + d.mark(); + }, drag); + } + }) + } + }; + + this.unmarkSelection = function(drag, event) { + _foreach(_selection, function(e) { e.unmark(event); }, drag); + }; + + this.unmarkPosses = function(drag, event) { + if (drag.posses) { + _each(drag.posses, function(p) { + if (drag.posseRoles[p] && _posses[p]) { + _foreach(_posses[p].members, function (d) { + d.unmark(event, true); + }, drag); + } + }); + } + }; + + this.getSelection = function() { return _selection.slice(0); }; + + this.updateSelection = function(dx, dy, drag) { + _foreach(_selection, function(e) { e.moveBy(dx, dy); }, drag); + }; + + var _posseAction = function(fn, drag) { + if (drag.posses) { + _each(drag.posses, function(p) { + if (drag.posseRoles[p] && _posses[p]) { + _foreach(_posses[p].members, function (e) { + fn(e); + }, drag); + } + }); + } + }; + + this.updatePosses = function(dx, dy, drag) { + _posseAction(function(e) { e.moveBy(dx, dy); }, drag); + }; + + this.notifyPosseDragStop = function(drag, evt) { + _posseAction(function(e) { e.stop(evt, true); }, drag); + }; + + this.notifySelectionDragStop = function(drag, evt) { + _foreach(_selection, function(e) { e.stop(evt, true); }, drag); + }; + + this.notifySelectionDragStart = function(drag, evt) { + _foreach(_selection, function(e) { e.notifyStart(evt);}, drag); + }; + + this.setZoom = function(z) { _zoom = z; }; + this.getZoom = function() { return _zoom; }; + + // does the work of changing scopes + var _scopeManip = function(kObj, scopes, map, fn) { + _each(kObj, function(_kObj) { + _unreg(_kObj, map); // deregister existing scopes + _kObj[fn](scopes); // set scopes + _reg(_kObj, map); // register new ones + }); + }; + + _each([ "set", "add", "remove", "toggle"], function(v) { + this[v + "Scope"] = function(el, scopes) { + _scopeManip(el._katavorioDrag, scopes, this._dragsByScope, v + "Scope"); + _scopeManip(el._katavorioDrop, scopes, this._dropsByScope, v + "Scope"); + }.bind(this); + this[v + "DragScope"] = function(el, scopes) { + _scopeManip(el.constructor === Drag ? el : el._katavorioDrag, scopes, this._dragsByScope, v + "Scope"); + }.bind(this); + this[v + "DropScope"] = function(el, scopes) { + _scopeManip(el.constructor === Drop ? el : el._katavorioDrop, scopes, this._dropsByScope, v + "Scope"); + }.bind(this); + }.bind(this)); + + this.snapToGrid = function(x, y) { + for (var s in this._dragsByScope) { + _foreach(this._dragsByScope[s], function(d) { d.snap(x, y); }); + } + }; + + this.getDragsForScope = function(s) { return this._dragsByScope[s]; }; + this.getDropsForScope = function(s) { return this._dropsByScope[s]; }; + + var _destroy = function(el, type, map) { + el = _gel(el); + if (el[type]) { + + // remove from selection, if present. + var selIdx = _selection.indexOf(el[type]); + if (selIdx >= 0) { + _selection.splice(selIdx, 1); + } + + if (_unreg(el[type], map)) { + _each(el[type], function(kObj) { kObj.destroy() }); + } + + delete el[type]; + } + }; + + var _removeListener = function(el, type, evt, fn) { + el = _gel(el); + if (el[type]) { + el[type].off(evt, fn); + } + }; + + this.elementRemoved = function(el) { + this.destroyDraggable(el); + this.destroyDroppable(el); + }; + + /** + * Either completely remove drag functionality from the given element, or remove a specific event handler. If you + * call this method with a single argument - the element - all drag functionality is removed from it. Otherwise, if + * you provide an event name and listener function, this function is de-registered (if found). + * @param el Element to update + * @param {string} [evt] Optional event name to unsubscribe + * @param {Function} [fn] Optional function to unsubscribe + */ + this.destroyDraggable = function(el, evt, fn) { + if (arguments.length === 1) { + _destroy(el, "_katavorioDrag", this._dragsByScope); + } else { + _removeListener(el, "_katavorioDrag", evt, fn); + } + }; + + /** + * Either completely remove drop functionality from the given element, or remove a specific event handler. If you + * call this method with a single argument - the element - all drop functionality is removed from it. Otherwise, if + * you provide an event name and listener function, this function is de-registered (if found). + * @param el Element to update + * @param {string} [evt] Optional event name to unsubscribe + * @param {Function} [fn] Optional function to unsubscribe + */ + this.destroyDroppable = function(el, evt, fn) { + if (arguments.length === 1) { + _destroy(el, "_katavorioDrop", this._dropsByScope); + } else { + _removeListener(el, "_katavorioDrop", evt, fn); + } + }; + + this.reset = function() { + this._dragsByScope = {}; + this._dropsByScope = {}; + _selection = []; + _selectionMap = {}; + _posses = {}; + }; + + // ----- groups + var _posses = {}; + + var _processOneSpec = function(el, _spec, dontAddExisting) { + var posseId = _isString(_spec) ? _spec : _spec.id; + var active = _isString(_spec) ? true : _spec.active !== false; + var posse = _posses[posseId] || (function() { + var g = {name:posseId, members:[]}; + _posses[posseId] = g; + return g; + })(); + _each(el, function(_el) { + if (_el._katavorioDrag) { + + if (dontAddExisting && _el._katavorioDrag.posseRoles[posse.name] != null) return; + + _suggest(posse.members, _el._katavorioDrag); + _suggest(_el._katavorioDrag.posses, posse.name); + _el._katavorioDrag.posseRoles[posse.name] = active; + } + }); + return posse; + }; + + /** + * Add the given element to the posse with the given id, creating the group if it at first does not exist. + * @method addToPosse + * @param {Element} el Element to add. + * @param {String...|Object...} spec Variable args parameters. Each argument can be a either a String, indicating + * the ID of a Posse to which the element should be added as an active participant, or an Object containing + * `{ id:"posseId", active:false/true}`. In the latter case, if `active` is not provided it is assumed to be + * true. + * @returns {Posse|Posse[]} The Posse(s) to which the element(s) was/were added. + */ + this.addToPosse = function(el, spec) { + + var posses = []; + + for (var i = 1; i < arguments.length; i++) { + posses.push(_processOneSpec(el, arguments[i])); + } + + return posses.length === 1 ? posses[0] : posses; + }; + + /** + * Sets the posse(s) for the element with the given id, creating those that do not yet exist, and removing from + * the element any current Posses that are not specified by this method call. This method will not change the + * active/passive state if it is given a posse in which the element is already a member. + * @method setPosse + * @param {Element} el Element to set posse(s) on. + * @param {String...|Object...} spec Variable args parameters. Each argument can be a either a String, indicating + * the ID of a Posse to which the element should be added as an active participant, or an Object containing + * `{ id:"posseId", active:false/true}`. In the latter case, if `active` is not provided it is assumed to be + * true. + * @returns {Posse|Posse[]} The Posse(s) to which the element(s) now belongs. + */ + this.setPosse = function(el, spec) { + + var posses = []; + + for (var i = 1; i < arguments.length; i++) { + posses.push(_processOneSpec(el, arguments[i], true).name); + } + + _each(el, function(_el) { + if (_el._katavorioDrag) { + var diff = _difference(_el._katavorioDrag.posses, posses); + var p = []; + Array.prototype.push.apply(p, _el._katavorioDrag.posses); + for (var i = 0; i < diff.length; i++) { + this.removeFromPosse(_el, diff[i]); + } + } + }.bind(this)); + + return posses.length === 1 ? posses[0] : posses; + }; + + /** + * Remove the given element from the given posse(s). + * @method removeFromPosse + * @param {Element} el Element to remove. + * @param {String...} posseId Varargs parameter: one value for each posse to remove the element from. + */ + this.removeFromPosse = function(el, posseId) { + if (arguments.length < 2) throw new TypeError("No posse id provided for remove operation"); + for(var i = 1; i < arguments.length; i++) { + posseId = arguments[i]; + _each(el, function (_el) { + if (_el._katavorioDrag && _el._katavorioDrag.posses) { + var d = _el._katavorioDrag; + _each(posseId, function (p) { + _vanquish(_posses[p].members, d); + _vanquish(d.posses, p); + delete d.posseRoles[p]; + }); + } + }); + } + }; + + /** + * Remove the given element from all Posses to which it belongs. + * @method removeFromAllPosses + * @param {Element|Element[]} el Element to remove from Posses. + */ + this.removeFromAllPosses = function(el) { + _each(el, function(_el) { + if (_el._katavorioDrag && _el._katavorioDrag.posses) { + var d = _el._katavorioDrag; + _each(d.posses, function(p) { + _vanquish(_posses[p].members, d); + }); + d.posses.length = 0; + d.posseRoles = {}; + } + }); + }; + + /** + * Changes the participation state for the element in the Posse with the given ID. + * @param {Element|Element[]} el Element(s) to change state for. + * @param {String} posseId ID of the Posse to change element state for. + * @param {Boolean} state True to make active, false to make passive. + */ + this.setPosseState = function(el, posseId, state) { + var posse = _posses[posseId]; + if (posse) { + _each(el, function(_el) { + if (_el._katavorioDrag && _el._katavorioDrag.posses) { + _el._katavorioDrag.posseRoles[posse.name] = state; + } + }); + } + }; + + }; + + root.Katavorio.version = "1.0.0"; + + if (typeof exports !== "undefined") { + exports.Katavorio = root.Katavorio; + } + +}).call(typeof window !== 'undefined' ? window : this); + + +(function() { + + var root = this; + root.jsPlumbUtil = root.jsPlumbUtil || {}; + var jsPlumbUtil = root.jsPlumbUtil; + + if (typeof exports !=='undefined') { exports.jsPlumbUtil = jsPlumbUtil;} + + + /** + * Tests if the given object is an Array. + * @param a + */ + function isArray(a) { + return Object.prototype.toString.call(a) === "[object Array]"; + } + jsPlumbUtil.isArray = isArray; + /** + * Tests if the given object is a Number. + * @param n + */ + function isNumber(n) { + return Object.prototype.toString.call(n) === "[object Number]"; + } + jsPlumbUtil.isNumber = isNumber; + function isString(s) { + return typeof s === "string"; + } + jsPlumbUtil.isString = isString; + function isBoolean(s) { + return typeof s === "boolean"; + } + jsPlumbUtil.isBoolean = isBoolean; + function isNull(s) { + return s == null; + } + jsPlumbUtil.isNull = isNull; + function isObject(o) { + return o == null ? false : Object.prototype.toString.call(o) === "[object Object]"; + } + jsPlumbUtil.isObject = isObject; + function isDate(o) { + return Object.prototype.toString.call(o) === "[object Date]"; + } + jsPlumbUtil.isDate = isDate; + function isFunction(o) { + return Object.prototype.toString.call(o) === "[object Function]"; + } + jsPlumbUtil.isFunction = isFunction; + function isNamedFunction(o) { + return isFunction(o) && o.name != null && o.name.length > 0; + } + jsPlumbUtil.isNamedFunction = isNamedFunction; + function isEmpty(o) { + for (var i in o) { + if (o.hasOwnProperty(i)) { + return false; + } + } + return true; + } + jsPlumbUtil.isEmpty = isEmpty; + function clone(a) { + if (isString(a)) { + return "" + a; + } + else if (isBoolean(a)) { + return !!a; + } + else if (isDate(a)) { + return new Date(a.getTime()); + } + else if (isFunction(a)) { + return a; + } + else if (isArray(a)) { + var b = []; + for (var i = 0; i < a.length; i++) { + b.push(clone(a[i])); + } + return b; + } + else if (isObject(a)) { + var c = {}; + for (var j in a) { + c[j] = clone(a[j]); + } + return c; + } + else { + return a; + } + } + jsPlumbUtil.clone = clone; + function merge(a, b, collations, overwrites) { + // first change the collations array - if present - into a lookup table, because its faster. + var cMap = {}, ar, i, oMap = {}; + collations = collations || []; + overwrites = overwrites || []; + for (i = 0; i < collations.length; i++) { + cMap[collations[i]] = true; + } + for (i = 0; i < overwrites.length; i++) { + oMap[overwrites[i]] = true; + } + var c = clone(a); + for (i in b) { + if (c[i] == null || oMap[i]) { + c[i] = b[i]; + } + else if (isString(b[i]) || isBoolean(b[i])) { + if (!cMap[i]) { + c[i] = b[i]; // if we dont want to collate, just copy it in. + } + else { + ar = []; + // if c's object is also an array we can keep its values. + ar.push.apply(ar, isArray(c[i]) ? c[i] : [c[i]]); + ar.push.apply(ar, isBoolean(b[i]) ? b[i] : [b[i]]); + c[i] = ar; + } + } + else { + if (isArray(b[i])) { + ar = []; + // if c's object is also an array we can keep its values. + if (isArray(c[i])) { + ar.push.apply(ar, c[i]); + } + ar.push.apply(ar, b[i]); + c[i] = ar; + } + else if (isObject(b[i])) { + // overwrite c's value with an object if it is not already one. + if (!isObject(c[i])) { + c[i] = {}; + } + for (var j in b[i]) { + c[i][j] = b[i][j]; + } + } + } + } + return c; + } + jsPlumbUtil.merge = merge; + function replace(inObj, path, value) { + if (inObj == null) { + return; + } + var q = inObj, t = q; + path.replace(/([^\.])+/g, function (term, lc, pos, str) { + var array = term.match(/([^\[0-9]+){1}(\[)([0-9+])/), last = pos + term.length >= str.length, _getArray = function () { + return t[array[1]] || (function () { + t[array[1]] = []; + return t[array[1]]; + })(); + }; + if (last) { + // set term = value on current t, creating term as array if necessary. + if (array) { + _getArray()[array[3]] = value; + } + else { + t[term] = value; + } + } + else { + // set to current t[term], creating t[term] if necessary. + if (array) { + var a_1 = _getArray(); + t = a_1[array[3]] || (function () { + a_1[array[3]] = {}; + return a_1[array[3]]; + })(); + } + else { + t = t[term] || (function () { + t[term] = {}; + return t[term]; + })(); + } + } + return ""; + }); + return inObj; + } + jsPlumbUtil.replace = replace; + // + // chain a list of functions, supplied by [ object, method name, args ], and return on the first + // one that returns the failValue. if none return the failValue, return the successValue. + // + function functionChain(successValue, failValue, fns) { + for (var i = 0; i < fns.length; i++) { + var o = fns[i][0][fns[i][1]].apply(fns[i][0], fns[i][2]); + if (o === failValue) { + return o; + } + } + return successValue; + } + jsPlumbUtil.functionChain = functionChain; + /** + * + * Take the given model and expand out any parameters. 'functionPrefix' is optional, and if present, helps jsplumb figure out what to do if a value is a Function. + * if you do not provide it (and doNotExpandFunctions is null, or false), jsplumb will run the given values through any functions it finds, and use the function's + * output as the value in the result. if you do provide the prefix, only functions that are named and have this prefix + * will be executed; other functions will be passed as values to the output. + * + * @param model + * @param values + * @param functionPrefix + * @param doNotExpandFunctions + * @returns {any} + */ + function populate(model, values, functionPrefix, doNotExpandFunctions) { + // for a string, see if it has parameter matches, and if so, try to make the substitutions. + var getValue = function (fromString) { + var matches = fromString.match(/(\${.*?})/g); + if (matches != null) { + for (var i = 0; i < matches.length; i++) { + var val = values[matches[i].substring(2, matches[i].length - 1)] || ""; + if (val != null) { + fromString = fromString.replace(matches[i], val); + } + } + } + return fromString; + }; + // process one entry. + var _one = function (d) { + if (d != null) { + if (isString(d)) { + return getValue(d); + } + else if (isFunction(d) && !doNotExpandFunctions && (functionPrefix == null || (d.name || "").indexOf(functionPrefix) === 0)) { + return d(values); + } + else if (isArray(d)) { + var r = []; + for (var i = 0; i < d.length; i++) { + r.push(_one(d[i])); + } + return r; + } + else if (isObject(d)) { + var s = {}; + for (var j in d) { + s[j] = _one(d[j]); + } + return s; + } + else { + return d; + } + } + }; + return _one(model); + } + jsPlumbUtil.populate = populate; + /** + * Find the index of a given object in an array. + * @param a The array to search + * @param f The function to run on each element. Return true if the element matches. + * @returns {number} -1 if not found, otherwise the index in the array. + */ + function findWithFunction(a, f) { + if (a) { + for (var i = 0; i < a.length; i++) { + if (f(a[i])) { + return i; + } + } + } + return -1; + } + jsPlumbUtil.findWithFunction = findWithFunction; + /** + * Remove some element from an array by matching each element in the array against some predicate function. Note that this + * is an in-place removal; the array is altered. + * @param a The array to search + * @param f The function to run on each element. Return true if the element matches. + * @returns {boolean} true if removed, false otherwise. + */ + function removeWithFunction(a, f) { + var idx = findWithFunction(a, f); + if (idx > -1) { + a.splice(idx, 1); + } + return idx !== -1; + } + jsPlumbUtil.removeWithFunction = removeWithFunction; + /** + * Remove some element from an array by simple lookup in the array for the given element. Note that this + * is an in-place removal; the array is altered. + * @param l The array to search + * @param v The value to remove. + * @returns {boolean} true if removed, false otherwise. + */ + function remove(l, v) { + var idx = l.indexOf(v); + if (idx > -1) { + l.splice(idx, 1); + } + return idx !== -1; + } + jsPlumbUtil.remove = remove; + /** + * Add some element to the given array, unless it is determined that it is already in the array. + * @param list The array to add the element to. + * @param item The item to add. + * @param hashFunction A function to use to determine if the given item already exists in the array. + */ + function addWithFunction(list, item, hashFunction) { + if (findWithFunction(list, hashFunction) === -1) { + list.push(item); + } + } + jsPlumbUtil.addWithFunction = addWithFunction; + /** + * Add some element to a list that is contained in a map of lists. + * @param map The map of [ key -> list ] entries + * @param key The name of the list to insert into + * @param value The value to insert + * @param insertAtStart Whether or not to insert at the start; defaults to false. + */ + function addToList(map, key, value, insertAtStart) { + var l = map[key]; + if (l == null) { + l = []; + map[key] = l; + } + l[insertAtStart ? "unshift" : "push"](value); + return l; + } + jsPlumbUtil.addToList = addToList; + /** + * Add an item to a list, unless it is already in the list. The test for pre-existence is a simple list lookup. + * If you want to do something more complex, perhaps #addWithFunction might help. + * @param list List to add the item to + * @param item Item to add + * @param insertAtHead Whether or not to insert at the start; defaults to false. + */ + function suggest(list, item, insertAtHead) { + if (list.indexOf(item) === -1) { + if (insertAtHead) { + list.unshift(item); + } + else { + list.push(item); + } + return true; + } + return false; + } + jsPlumbUtil.suggest = suggest; + /** + * Extends the given obj (which can be an array) with the given constructor function, prototype functions, and class members, any of which may be null. + * @param child + * @param parent + * @param _protoFn + */ + function extend(child, parent, _protoFn) { + var i; + parent = isArray(parent) ? parent : [parent]; + var _copyProtoChain = function (focus) { + var proto = focus.__proto__; + while (proto != null) { + if (proto.prototype != null) { + for (var j in proto.prototype) { + if (proto.prototype.hasOwnProperty(j) && !child.prototype.hasOwnProperty(j)) { + child.prototype[j] = proto.prototype[j]; + } + } + proto = proto.prototype.__proto__; + } + else { + proto = null; + } + } + }; + for (i = 0; i < parent.length; i++) { + for (var j in parent[i].prototype) { + if (parent[i].prototype.hasOwnProperty(j) && !child.prototype.hasOwnProperty(j)) { + child.prototype[j] = parent[i].prototype[j]; + } + } + _copyProtoChain(parent[i]); + } + var _makeFn = function (name, protoFn) { + return function () { + for (i = 0; i < parent.length; i++) { + if (parent[i].prototype[name]) { + parent[i].prototype[name].apply(this, arguments); + } + } + return protoFn.apply(this, arguments); + }; + }; + var _oneSet = function (fns) { + for (var k in fns) { + child.prototype[k] = _makeFn(k, fns[k]); + } + }; + if (arguments.length > 2) { + for (i = 2; i < arguments.length; i++) { + _oneSet(arguments[i]); + } + } + return child; + } + jsPlumbUtil.extend = extend; + /** + * Generate a UUID. + */ + function uuid() { + return ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + })); + } + jsPlumbUtil.uuid = uuid; + /** + * Trim a string. + * @param s String to trim + * @returns the String with leading and trailing whitespace removed. + */ + function fastTrim(s) { + if (s == null) { + return null; + } + var str = s.replace(/^\s\s*/, ''), ws = /\s/, i = str.length; + while (ws.test(str.charAt(--i))) { + } + return str.slice(0, i + 1); + } + jsPlumbUtil.fastTrim = fastTrim; + function each(obj, fn) { + obj = obj.length == null || typeof obj === "string" ? [obj] : obj; + for (var i = 0; i < obj.length; i++) { + fn(obj[i]); + } + } + jsPlumbUtil.each = each; + function map(obj, fn) { + var o = []; + for (var i = 0; i < obj.length; i++) { + o.push(fn(obj[i])); + } + return o; + } + jsPlumbUtil.map = map; + function mergeWithParents(type, map, parentAttribute) { + parentAttribute = parentAttribute || "parent"; + var _def = function (id) { + return id ? map[id] : null; + }; + var _parent = function (def) { + return def ? _def(def[parentAttribute]) : null; + }; + var _one = function (parent, def) { + if (parent == null) { + return def; + } + else { + var overrides = ["anchor", "anchors", "cssClass", "connector", "paintStyle", "hoverPaintStyle", "endpoint", "endpoints"]; + if (def.mergeStrategy === "override") { + Array.prototype.push.apply(overrides, ["events", "overlays"]); + } + var d_1 = merge(parent, def, [], overrides); + return _one(_parent(parent), d_1); + } + }; + var _getDef = function (t) { + if (t == null) { + return {}; + } + if (typeof t === "string") { + return _def(t); + } + else if (t.length) { + var done = false, i = 0, _dd = void 0; + while (!done && i < t.length) { + _dd = _getDef(t[i]); + if (_dd) { + done = true; + } + else { + i++; + } + } + return _dd; + } + }; + var d = _getDef(type); + if (d) { + return _one(_parent(d), d); + } + else { + return {}; + } + } + jsPlumbUtil.mergeWithParents = mergeWithParents; + jsPlumbUtil.logEnabled = true; + function log() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + if (jsPlumbUtil.logEnabled && typeof console !== "undefined") { + try { + var msg = arguments[arguments.length - 1]; + console.log(msg); + } + catch (e) { + } + } + } + jsPlumbUtil.log = log; + /** + * Wraps one function with another, creating a placeholder for the + * wrapped function if it was null. this is used to wrap the various + * drag/drop event functions - to allow jsPlumb to be notified of + * important lifecycle events without imposing itself on the user's + * drag/drop functionality. + * @method jsPlumbUtil.wrap + * @param {Function} wrappedFunction original function to wrap; may be null. + * @param {Function} newFunction function to wrap the original with. + * @param {Object} [returnOnThisValue] Optional. Indicates that the wrappedFunction should + * not be executed if the newFunction returns a value matching 'returnOnThisValue'. + * note that this is a simple comparison and only works for primitives right now. + */ + function wrap(wrappedFunction, newFunction, returnOnThisValue) { + return function () { + var r = null; + try { + if (newFunction != null) { + r = newFunction.apply(this, arguments); + } + } + catch (e) { + log("jsPlumb function failed : " + e); + } + if ((wrappedFunction != null) && (returnOnThisValue == null || (r !== returnOnThisValue))) { + try { + r = wrappedFunction.apply(this, arguments); + } + catch (e) { + log("wrapped function failed : " + e); + } + } + return r; + }; + } + jsPlumbUtil.wrap = wrap; + var EventGenerator = /** @class */ (function () { + function EventGenerator() { + var _this = this; + this._listeners = {}; + this.eventsSuspended = false; + this.tick = false; + // this is a list of events that should re-throw any errors that occur during their dispatch. + this.eventsToDieOn = { "ready": true }; + this.queue = []; + this.bind = function (event, listener, insertAtStart) { + var _one = function (evt) { + addToList(_this._listeners, evt, listener, insertAtStart); + listener.__jsPlumb = listener.__jsPlumb || {}; + listener.__jsPlumb[uuid()] = evt; + }; + if (typeof event === "string") { + _one(event); + } + else if (event.length != null) { + for (var i = 0; i < event.length; i++) { + _one(event[i]); + } + } + return _this; + }; + this.fire = function (event, value, originalEvent) { + if (!this.tick) { + this.tick = true; + if (!this.eventsSuspended && this._listeners[event]) { + var l = this._listeners[event].length, i = 0, _gone = false, ret = null; + if (!this.shouldFireEvent || this.shouldFireEvent(event, value, originalEvent)) { + while (!_gone && i < l && ret !== false) { + // doing it this way rather than catching and then possibly re-throwing means that an error propagated by this + // method will have the whole call stack available in the debugger. + if (this.eventsToDieOn[event]) { + this._listeners[event][i].apply(this, [value, originalEvent]); + } + else { + try { + ret = this._listeners[event][i].apply(this, [value, originalEvent]); + } + catch (e) { + log("jsPlumb: fire failed for event " + event + " : " + e); + } + } + i++; + if (this._listeners == null || this._listeners[event] == null) { + _gone = true; + } + } + } + } + this.tick = false; + this._drain(); + } + else { + this.queue.unshift(arguments); + } + return this; + }; + this._drain = function () { + var n = _this.queue.pop(); + if (n) { + _this.fire.apply(_this, n); + } + }; + this.unbind = function (eventOrListener, listener) { + if (arguments.length === 0) { + this._listeners = {}; + } + else if (arguments.length === 1) { + if (typeof eventOrListener === "string") { + delete this._listeners[eventOrListener]; + } + else if (eventOrListener.__jsPlumb) { + var evt = void 0; + for (var i in eventOrListener.__jsPlumb) { + evt = eventOrListener.__jsPlumb[i]; + remove(this._listeners[evt] || [], eventOrListener); + } + } + } + else if (arguments.length === 2) { + remove(this._listeners[eventOrListener] || [], listener); + } + return this; + }; + this.getListener = function (forEvent) { + return _this._listeners[forEvent]; + }; + this.setSuspendEvents = function (val) { + _this.eventsSuspended = val; + }; + this.isSuspendEvents = function () { + return _this.eventsSuspended; + }; + this.silently = function (fn) { + _this.setSuspendEvents(true); + try { + fn(); + } + catch (e) { + log("Cannot execute silent function " + e); + } + _this.setSuspendEvents(false); + }; + this.cleanupListeners = function () { + for (var i in _this._listeners) { + _this._listeners[i] = null; + } + }; + } + return EventGenerator; + }()); + jsPlumbUtil.EventGenerator = EventGenerator; + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains utility functions that run in browsers only. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +;(function() { + + "use strict"; + + var root = this; + + root.jsPlumbUtil.matchesSelector = function(el, selector, ctx) { + ctx = ctx || el.parentNode; + var possibles = ctx.querySelectorAll(selector); + for (var i = 0; i < possibles.length; i++) { + if (possibles[i] === el) { + return true; + } + } + return false; + }; + + root.jsPlumbUtil.consume = function(e, doNotPreventDefault) { + if (e.stopPropagation) { + e.stopPropagation(); + } + else { + e.returnValue = false; + } + + if (!doNotPreventDefault && e.preventDefault){ + e.preventDefault(); + } + }; + + /* + * Function: sizeElement + * Helper to size and position an element. You would typically use + * this when writing your own Connector or Endpoint implementation. + * + * Parameters: + * x - [int] x position for the element origin + * y - [int] y position for the element origin + * w - [int] width of the element + * h - [int] height of the element + * + */ + root.jsPlumbUtil.sizeElement = function(el, x, y, w, h) { + if (el) { + el.style.height = h + "px"; + el.height = h; + el.style.width = w + "px"; + el.width = w; + el.style.left = x + "px"; + el.style.top = y + "px"; + } + }; + +}).call(typeof window !== 'undefined' ? window : this); + +;(function() { + + var DEFAULT_OPTIONS = { + deriveAnchor:function(edge, index, ep, conn) { + return { + top:["TopRight", "TopLeft"], + bottom:["BottomRight", "BottomLeft"] + }[edge][index]; + } + }; + + var root = this; + + var ListManager = function(jsPlumbInstance) { + + this.count = 0; + this.instance = jsPlumbInstance; + this.lists = {}; + + this.instance.addList = function(el, options) { + return this.listManager.addList(el, options); + }; + + this.instance.removeList = function(el) { + this.listManager.removeList(el); + }; + + this.instance.bind("manageElement", function(p) { + + //look for [jtk-scrollable-list] elements and attach scroll listeners if necessary + var scrollableLists = this.instance.getSelector(p.el, "[jtk-scrollable-list]"); + for (var i = 0; i < scrollableLists.length; i++) { + this.addList(scrollableLists[i]); + } + + }.bind(this)); + + this.instance.bind("unmanageElement", function(p) { + this.removeList(p.el); + }); + + + this.instance.bind("connection", function(c, evt) { + if (evt == null) { + // not added by mouse. look for an ancestor of the source and/or target element that is a scrollable list, and run + // its scroll method. + this._maybeUpdateParentList(c.source); + this._maybeUpdateParentList(c.target); + } + }.bind(this)); + }; + + root.jsPlumbListManager = ListManager; + + ListManager.prototype = { + + addList : function(el, options) { + var dp = this.instance.extend({}, DEFAULT_OPTIONS); + options = this.instance.extend(dp, options || {}); + var id = [this.instance.getInstanceIndex(), this.count++].join("_"); + this.lists[id] = new List(this.instance, el, options, id); + }, + + removeList:function(el) { + var list = this.lists[el._jsPlumbList]; + if (list) { + list.destroy(); + delete this.lists[el._jsPlumbList]; + } + }, + + _maybeUpdateParentList:function (el) { + var parent = el.parentNode, container = this.instance.getContainer(); + while(parent != null && parent !== container) { + if (parent._jsPlumbList != null && this.lists[parent._jsPlumbList] != null) { + parent._jsPlumbScrollHandler(); + return + } + parent = parent.parentNode; + } + } + + + }; + + var List = function(instance, el, options, id) { + + el["_jsPlumbList"] = id; + + // + // Derive an anchor to use for the current situation. In contrast to the way we derive an endpoint, here we use `anchor` from the options, if present, as + // our first choice, and then `deriveAnchor` as our next choice. There is a default `deriveAnchor` implementation that uses TopRight/TopLeft for top and + // BottomRight/BottomLeft for bottom. + // + // edge - "top" or "bottom" + // index - 0 when endpoint is connection source, 1 when endpoint is connection target + // ep - the endpoint that is being proxied + // conn - the connection that is being proxied + // + function deriveAnchor(edge, index, ep, conn) { + return options.anchor ? options.anchor : options.deriveAnchor(edge, index, ep, conn); + } + + // + // Derive an endpoint to use for the current situation. We'll use a `deriveEndpoint` function passed in to the options as our first choice, + // followed by `endpoint` (an endpoint spec) from the options, and failing either of those we just use the `type` of the endpoint that is being proxied. + // + // edge - "top" or "bottom" + // index - 0 when endpoint is connection source, 1 when endpoint is connection target + // endpoint - the endpoint that is being proxied + // connection - the connection that is being proxied + // + function deriveEndpoint(edge, index, ep, conn) { + return options.deriveEndpoint ? options.deriveEndpoint(edge, index, ep, conn) : options.endpoint ? options.endpoint : ep.type; + } + + // + // look for a parent of the given scrollable list that is draggable, and then update the child offsets for it. this should not + // be necessary in the delegated drag stuff from the upcoming 3.0.0 release. + // + function _maybeUpdateDraggable(el) { + var parent = el.parentNode, container = instance.getContainer(); + while(parent != null && parent !== container) { + if (instance.hasClass(parent, "jtk-managed")) { + instance.recalculateOffsets(parent); + return + } + parent = parent.parentNode; + } + } + + var scrollHandler = function(e) { + + var children = instance.getSelector(el, ".jtk-managed"); + var elId = instance.getId(el); + + for (var i = 0; i < children.length; i++) { + + if (children[i].offsetTop < el.scrollTop) { + if (!children[i]._jsPlumbProxies) { + children[i]._jsPlumbProxies = children[i]._jsPlumbProxies || []; + instance.select({source: children[i]}).each(function (c) { + + + instance.proxyConnection(c, 0, el, elId, function () { + return deriveEndpoint("top", 0, c.endpoints[0], c); + }, function () { + return deriveAnchor("top", 0, c.endpoints[0], c); + }); + children[i]._jsPlumbProxies.push([c, 0]); + }); + + instance.select({target: children[i]}).each(function (c) { + instance.proxyConnection(c, 1, el, elId, function () { + return deriveEndpoint("top", 1, c.endpoints[1], c); + }, function () { + return deriveAnchor("top", 1, c.endpoints[1], c); + }); + children[i]._jsPlumbProxies.push([c, 1]); + }); + } + } + // + else if (children[i].offsetTop > el.scrollTop + el.offsetHeight) { + if (!children[i]._jsPlumbProxies) { + children[i]._jsPlumbProxies = children[i]._jsPlumbProxies || []; + + instance.select({source: children[i]}).each(function (c) { + instance.proxyConnection(c, 0, el, elId, function () { + return deriveEndpoint("bottom", 0, c.endpoints[0], c); + }, function () { + return deriveAnchor("bottom", 0, c.endpoints[0], c); + }); + children[i]._jsPlumbProxies.push([c, 0]); + }); + + instance.select({target: children[i]}).each(function (c) { + instance.proxyConnection(c, 1, el, elId, function () { + return deriveEndpoint("bottom", 1, c.endpoints[1], c); + }, function () { + return deriveAnchor("bottom", 1, c.endpoints[1], c); + }); + children[i]._jsPlumbProxies.push([c, 1]); + }); + } + } else if (children[i]._jsPlumbProxies) { + for (var j = 0; j < children[i]._jsPlumbProxies.length; j++) { + instance.unproxyConnection(children[i]._jsPlumbProxies[j][0], children[i]._jsPlumbProxies[j][1], elId); + } + + delete children[i]._jsPlumbProxies; + } + + instance.revalidate(children[i]); + } + + _maybeUpdateDraggable(el); + }; + + instance.setAttribute(el, "jtk-scrollable-list", "true"); + el._jsPlumbScrollHandler = scrollHandler; + instance.on(el, "scroll", scrollHandler); + scrollHandler(); // run it once; there may be connections already. + + this.destroy = function() { + instance.off(el, "scroll", scrollHandler); + delete el._jsPlumbScrollHandler; + + var children = instance.getSelector(el, ".jtk-managed"); + var elId = instance.getId(el); + + for (var i = 0; i < children.length; i++) { + if (children[i]._jsPlumbProxies) { + for (var j = 0; j < children[i]._jsPlumbProxies.length; j++) { + instance.unproxyConnection(children[i]._jsPlumbProxies[j][0], children[i]._jsPlumbProxies[j][1], elId); + } + + delete children[i]._jsPlumbProxies; + } + } + }; + }; + + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the core code. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +;(function () { + + "use strict"; + + var root = this; + + var _ju = root.jsPlumbUtil, + + /** + * creates a timestamp, using milliseconds since 1970, but as a string. + */ + _timestamp = function () { + return "" + (new Date()).getTime(); + }, + + // helper method to update the hover style whenever it, or paintStyle, changes. + // we use paintStyle as the foundation and merge hoverPaintStyle over the + // top. + _updateHoverStyle = function (component) { + if (component._jsPlumb.paintStyle && component._jsPlumb.hoverPaintStyle) { + var mergedHoverStyle = {}; + jsPlumb.extend(mergedHoverStyle, component._jsPlumb.paintStyle); + jsPlumb.extend(mergedHoverStyle, component._jsPlumb.hoverPaintStyle); + delete component._jsPlumb.hoverPaintStyle; + // we want the fill of paintStyle to override a gradient, if possible. + if (mergedHoverStyle.gradient && component._jsPlumb.paintStyle.fill) { + delete mergedHoverStyle.gradient; + } + component._jsPlumb.hoverPaintStyle = mergedHoverStyle; + } + }, + events = ["tap", "dbltap", "click", "dblclick", "mouseover", "mouseout", "mousemove", "mousedown", "mouseup", "contextmenu" ], + eventFilters = { "mouseout": "mouseleave", "mouseexit": "mouseleave" }, + _updateAttachedElements = function (component, state, timestamp, sourceElement) { + var affectedElements = component.getAttachedElements(); + if (affectedElements) { + for (var i = 0, j = affectedElements.length; i < j; i++) { + if (!sourceElement || sourceElement !== affectedElements[i]) { + affectedElements[i].setHover(state, true, timestamp); // tell the attached elements not to inform their own attached elements. + } + } + } + }, + _splitType = function (t) { + return t == null ? null : t.split(" "); + }, + _mapType = function(map, obj, typeId) { + for (var i in obj) { + map[i] = typeId; + } + }, + _each = function(fn, obj) { + obj = _ju.isArray(obj) || (obj.length != null && !_ju.isString(obj)) ? obj : [ obj ]; + for (var i = 0; i < obj.length; i++) { + try { + fn.apply(obj[i], [ obj[i] ]); + } + catch (e) { + _ju.log(".each iteration failed : " + e); + } + } + }, + _applyTypes = function (component, params, doNotRepaint) { + if (component.getDefaultType) { + var td = component.getTypeDescriptor(), map = {}; + var defType = component.getDefaultType(); + var o = _ju.merge({}, defType); + _mapType(map, defType, "__default"); + for (var i = 0, j = component._jsPlumb.types.length; i < j; i++) { + var tid = component._jsPlumb.types[i]; + if (tid !== "__default") { + var _t = component._jsPlumb.instance.getType(tid, td); + if (_t != null) { + + var overrides = ["anchor", "anchors", "connector", "paintStyle", "hoverPaintStyle", "endpoint", "endpoints", "connectorOverlays", "connectorStyle", "connectorHoverStyle", "endpointStyle", "endpointHoverStyle"]; + var collations = [ ]; + + if (_t.mergeStrategy === "override") { + Array.prototype.push.apply(overrides, ["events", "overlays", "cssClass"]); + } else { + collations.push("cssClass"); + } + + o = _ju.merge(o, _t, collations, overrides); + _mapType(map, _t, tid); + } + } + } + + if (params) { + o = _ju.populate(o, params, "_"); + } + + component.applyType(o, doNotRepaint, map); + if (!doNotRepaint) { + component.repaint(); + } + } + }, + +// ------------------------------ BEGIN jsPlumbUIComponent -------------------------------------------- + + jsPlumbUIComponent = root.jsPlumbUIComponent = function (params) { + + _ju.EventGenerator.apply(this, arguments); + + var self = this, + a = arguments, + idPrefix = self.idPrefix, + id = idPrefix + (new Date()).getTime(); + + this._jsPlumb = { + instance: params._jsPlumb, + parameters: params.parameters || {}, + paintStyle: null, + hoverPaintStyle: null, + paintStyleInUse: null, + hover: false, + beforeDetach: params.beforeDetach, + beforeDrop: params.beforeDrop, + overlayPlacements: [], + hoverClass: params.hoverClass || params._jsPlumb.Defaults.HoverClass, + types: [], + typeCache:{} + }; + + this.cacheTypeItem = function(key, item, typeId) { + this._jsPlumb.typeCache[typeId] = this._jsPlumb.typeCache[typeId] || {}; + this._jsPlumb.typeCache[typeId][key] = item; + }; + this.getCachedTypeItem = function(key, typeId) { + return this._jsPlumb.typeCache[typeId] ? this._jsPlumb.typeCache[typeId][key] : null; + }; + + this.getId = function () { + return id; + }; + +// ----------------------------- default type -------------------------------------------- + + + var o = params.overlays || [], oo = {}; + if (this.defaultOverlayKeys) { + for (var i = 0; i < this.defaultOverlayKeys.length; i++) { + Array.prototype.push.apply(o, this._jsPlumb.instance.Defaults[this.defaultOverlayKeys[i]] || []); + } + + for (i = 0; i < o.length; i++) { + // if a string, convert to object representation so that we can store the typeid on it. + // also assign an id. + var fo = jsPlumb.convertToFullOverlaySpec(o[i]); + oo[fo[1].id] = fo; + } + } + + var _defaultType = { + overlays:oo, + parameters: params.parameters || {}, + scope: params.scope || this._jsPlumb.instance.getDefaultScope() + }; + this.getDefaultType = function() { + return _defaultType; + }; + this.appendToDefaultType = function(obj) { + for (var i in obj) { + _defaultType[i] = obj[i]; + } + }; + +// ----------------------------- end default type -------------------------------------------- + + // all components can generate events + + if (params.events) { + for (var evtName in params.events) { + self.bind(evtName, params.events[evtName]); + } + } + + // all components get this clone function. + // TODO issue 116 showed a problem with this - it seems 'a' that is in + // the clone function's scope is shared by all invocations of it, the classic + // JS closure problem. for now, jsPlumb does a version of this inline where + // it used to call clone. but it would be nice to find some time to look + // further at this. + this.clone = function () { + var o = Object.create(this.constructor.prototype); + this.constructor.apply(o, a); + return o; + }.bind(this); + + // user can supply a beforeDetach callback, which will be executed before a detach + // is performed; returning false prevents the detach. + this.isDetachAllowed = function (connection) { + var r = true; + if (this._jsPlumb.beforeDetach) { + try { + r = this._jsPlumb.beforeDetach(connection); + } + catch (e) { + _ju.log("jsPlumb: beforeDetach callback failed", e); + } + } + return r; + }; + + // user can supply a beforeDrop callback, which will be executed before a dropped + // connection is confirmed. user can return false to reject connection. + this.isDropAllowed = function (sourceId, targetId, scope, connection, dropEndpoint, source, target) { + var r = this._jsPlumb.instance.checkCondition("beforeDrop", { + sourceId: sourceId, + targetId: targetId, + scope: scope, + connection: connection, + dropEndpoint: dropEndpoint, + source: source, target: target + }); + if (this._jsPlumb.beforeDrop) { + try { + r = this._jsPlumb.beforeDrop({ + sourceId: sourceId, + targetId: targetId, + scope: scope, + connection: connection, + dropEndpoint: dropEndpoint, + source: source, target: target + }); + } + catch (e) { + _ju.log("jsPlumb: beforeDrop callback failed", e); + } + } + return r; + }; + + var domListeners = []; + + // sets the component associated with listener events. for instance, an overlay delegates + // its events back to a connector. but if the connector is swapped on the underlying connection, + // then this component must be changed. This is called by setConnector in the Connection class. + this.setListenerComponent = function (c) { + for (var i = 0; i < domListeners.length; i++) { + domListeners[i][3] = c; + } + }; + + + }; + + var _removeTypeCssHelper = function (component, typeIndex) { + var typeId = component._jsPlumb.types[typeIndex], + type = component._jsPlumb.instance.getType(typeId, component.getTypeDescriptor()); + + if (type != null && type.cssClass && component.canvas) { + component._jsPlumb.instance.removeClass(component.canvas, type.cssClass); + } + }; + + _ju.extend(root.jsPlumbUIComponent, _ju.EventGenerator, { + + getParameter: function (name) { + return this._jsPlumb.parameters[name]; + }, + + setParameter: function (name, value) { + this._jsPlumb.parameters[name] = value; + }, + + getParameters: function () { + return this._jsPlumb.parameters; + }, + + setParameters: function (p) { + this._jsPlumb.parameters = p; + }, + + getClass:function() { + return jsPlumb.getClass(this.canvas); + }, + + hasClass:function(clazz) { + return jsPlumb.hasClass(this.canvas, clazz); + }, + + addClass: function (clazz) { + jsPlumb.addClass(this.canvas, clazz); + }, + + removeClass: function (clazz) { + jsPlumb.removeClass(this.canvas, clazz); + }, + + updateClasses: function (classesToAdd, classesToRemove) { + jsPlumb.updateClasses(this.canvas, classesToAdd, classesToRemove); + }, + + setType: function (typeId, params, doNotRepaint) { + this.clearTypes(); + this._jsPlumb.types = _splitType(typeId) || []; + _applyTypes(this, params, doNotRepaint); + }, + + getType: function () { + return this._jsPlumb.types; + }, + + reapplyTypes: function (params, doNotRepaint) { + _applyTypes(this, params, doNotRepaint); + }, + + hasType: function (typeId) { + return this._jsPlumb.types.indexOf(typeId) !== -1; + }, + + addType: function (typeId, params, doNotRepaint) { + var t = _splitType(typeId), _cont = false; + if (t != null) { + for (var i = 0, j = t.length; i < j; i++) { + if (!this.hasType(t[i])) { + this._jsPlumb.types.push(t[i]); + _cont = true; + } + } + if (_cont) { + _applyTypes(this, params, doNotRepaint); + } + } + }, + + removeType: function (typeId, params, doNotRepaint) { + var t = _splitType(typeId), _cont = false, _one = function (tt) { + var idx = this._jsPlumb.types.indexOf(tt); + if (idx !== -1) { + // remove css class if necessary + _removeTypeCssHelper(this, idx); + this._jsPlumb.types.splice(idx, 1); + return true; + } + return false; + }.bind(this); + + if (t != null) { + for (var i = 0, j = t.length; i < j; i++) { + _cont = _one(t[i]) || _cont; + } + if (_cont) { + _applyTypes(this, params, doNotRepaint); + } + } + }, + clearTypes: function (params, doNotRepaint) { + var i = this._jsPlumb.types.length; + for (var j = 0; j < i; j++) { + _removeTypeCssHelper(this, 0); + this._jsPlumb.types.splice(0, 1); + } + _applyTypes(this, params, doNotRepaint); + }, + + toggleType: function (typeId, params, doNotRepaint) { + var t = _splitType(typeId); + if (t != null) { + for (var i = 0, j = t.length; i < j; i++) { + var idx = this._jsPlumb.types.indexOf(t[i]); + if (idx !== -1) { + _removeTypeCssHelper(this, idx); + this._jsPlumb.types.splice(idx, 1); + } + else { + this._jsPlumb.types.push(t[i]); + } + } + + _applyTypes(this, params, doNotRepaint); + } + }, + applyType: function (t, doNotRepaint) { + this.setPaintStyle(t.paintStyle, doNotRepaint); + this.setHoverPaintStyle(t.hoverPaintStyle, doNotRepaint); + if (t.parameters) { + for (var i in t.parameters) { + this.setParameter(i, t.parameters[i]); + } + } + this._jsPlumb.paintStyleInUse = this.getPaintStyle(); + }, + setPaintStyle: function (style, doNotRepaint) { + // this._jsPlumb.paintStyle = jsPlumb.extend({}, style); + // TODO figure out if we want components to clone paintStyle so as not to share it. + this._jsPlumb.paintStyle = style; + this._jsPlumb.paintStyleInUse = this._jsPlumb.paintStyle; + _updateHoverStyle(this); + if (!doNotRepaint) { + this.repaint(); + } + }, + getPaintStyle: function () { + return this._jsPlumb.paintStyle; + }, + setHoverPaintStyle: function (style, doNotRepaint) { + //this._jsPlumb.hoverPaintStyle = jsPlumb.extend({}, style); + // TODO figure out if we want components to clone paintStyle so as not to share it. + this._jsPlumb.hoverPaintStyle = style; + _updateHoverStyle(this); + if (!doNotRepaint) { + this.repaint(); + } + }, + getHoverPaintStyle: function () { + return this._jsPlumb.hoverPaintStyle; + }, + destroy: function (force) { + if (force || this.typeId == null) { + this.cleanupListeners(); // this is on EventGenerator + this.clone = null; + this._jsPlumb = null; + } + }, + + isHover: function () { + return this._jsPlumb.hover; + }, + + setHover: function (hover, ignoreAttachedElements, timestamp) { + // while dragging, we ignore these events. this keeps the UI from flashing and + // swishing and whatevering. + if (this._jsPlumb && !this._jsPlumb.instance.currentlyDragging && !this._jsPlumb.instance.isHoverSuspended()) { + + this._jsPlumb.hover = hover; + var method = hover ? "addClass" : "removeClass"; + + if (this.canvas != null) { + if (this._jsPlumb.instance.hoverClass != null) { + this._jsPlumb.instance[method](this.canvas, this._jsPlumb.instance.hoverClass); + } + if (this._jsPlumb.hoverClass != null) { + this._jsPlumb.instance[method](this.canvas, this._jsPlumb.hoverClass); + } + } + if (this._jsPlumb.hoverPaintStyle != null) { + this._jsPlumb.paintStyleInUse = hover ? this._jsPlumb.hoverPaintStyle : this._jsPlumb.paintStyle; + if (!this._jsPlumb.instance.isSuspendDrawing()) { + timestamp = timestamp || _timestamp(); + this.repaint({timestamp: timestamp, recalc: false}); + } + } + // get the list of other affected elements, if supported by this component. + // for a connection, its the endpoints. for an endpoint, its the connections! surprise. + if (this.getAttachedElements && !ignoreAttachedElements) { + _updateAttachedElements(this, hover, _timestamp(), this); + } + } + } + }); + +// ------------------------------ END jsPlumbUIComponent -------------------------------------------- + + var _jsPlumbInstanceIndex = 0, + getInstanceIndex = function () { + var i = _jsPlumbInstanceIndex + 1; + _jsPlumbInstanceIndex++; + return i; + }; + + var jsPlumbInstance = root.jsPlumbInstance = function (_defaults) { + + this.version = "2.12.6"; + + this.Defaults = { + Anchor: "Bottom", + Anchors: [ null, null ], + ConnectionsDetachable: true, + ConnectionOverlays: [ ], + Connector: "Bezier", + Container: null, + DoNotThrowErrors: false, + DragOptions: { }, + DropOptions: { }, + Endpoint: "Dot", + EndpointOverlays: [ ], + Endpoints: [ null, null ], + EndpointStyle: { fill: "#456" }, + EndpointStyles: [ null, null ], + EndpointHoverStyle: null, + EndpointHoverStyles: [ null, null ], + HoverPaintStyle: null, + LabelStyle: { color: "black" }, + LogEnabled: false, + Overlays: [ ], + MaxConnections: 1, + PaintStyle: { "stroke-width": 4, stroke: "#456" }, + ReattachConnections: false, + RenderMode: "svg", + Scope: "jsPlumb_DefaultScope" + }; + + if (_defaults) { + jsPlumb.extend(this.Defaults, _defaults); + } + + this.logEnabled = this.Defaults.LogEnabled; + this._connectionTypes = {}; + this._endpointTypes = {}; + + _ju.EventGenerator.apply(this); + + var _currentInstance = this, + _instanceIndex = getInstanceIndex(), + _bb = _currentInstance.bind, + _initialDefaults = {}, + _zoom = 1, + _info = function (el) { + if (el == null) { + return null; + } + else if (el.nodeType === 3 || el.nodeType === 8) { + return { el:el, text:true }; + } + else { + var _el = _currentInstance.getElement(el); + return { el: _el, id: (_ju.isString(el) && _el == null) ? el : _getId(_el) }; + } + }; + + this.getInstanceIndex = function () { + return _instanceIndex; + }; + + // CONVERTED + this.setZoom = function (z, repaintEverything) { + _zoom = z; + _currentInstance.fire("zoom", _zoom); + if (repaintEverything) { + _currentInstance.repaintEverything(); + } + return true; + }; + // CONVERTED + this.getZoom = function () { + return _zoom; + }; + + for (var i in this.Defaults) { + _initialDefaults[i] = this.Defaults[i]; + } + + var _container, _containerDelegations = []; + this.unbindContainer = function() { + if (_container != null && _containerDelegations.length > 0) { + for (var i = 0; i < _containerDelegations.length; i++) { + _currentInstance.off(_container, _containerDelegations[i][0], _containerDelegations[i][1]); + } + } + }; + this.setContainer = function (c) { + + this.unbindContainer(); + + // get container as dom element. + c = this.getElement(c); + // move existing connections and endpoints, if any. + this.select().each(function (conn) { + conn.moveParent(c); + }); + this.selectEndpoints().each(function (ep) { + ep.moveParent(c); + }); + + // set container. + var previousContainer = _container; + _container = c; + _containerDelegations.length = 0; + var eventAliases = { + "endpointclick":"endpointClick", + "endpointdblclick":"endpointDblClick" + }; + + var _oneDelegateHandler = function (id, e, componentType) { + var t = e.srcElement || e.target, + jp = (t && t.parentNode ? t.parentNode._jsPlumb : null) || (t ? t._jsPlumb : null) || (t && t.parentNode && t.parentNode.parentNode ? t.parentNode.parentNode._jsPlumb : null); + if (jp) { + jp.fire(id, jp, e); + var alias = componentType ? eventAliases[componentType + id] || id : id; + // jsplumb also fires every event coming from components/overlays. That's what the test for `jp.component` is for. + _currentInstance.fire(alias, jp.component || jp, e); + } + }; + + var _addOneDelegate = function(eventId, selector, fn) { + _containerDelegations.push([eventId, fn]); + _currentInstance.on(_container, eventId, selector, fn); + }; + + // delegate one event on the container to jsplumb elements. it might be possible to + // abstract this out: each of endpoint, connection and overlay could register themselves with + // jsplumb as "component types" or whatever, and provide a suitable selector. this would be + // done by the renderer (although admittedly from 2.0 onwards we're not supporting vml anymore) + var _oneDelegate = function (id) { + // connections. + _addOneDelegate(id, ".jtk-connector", function (e) { + _oneDelegateHandler(id, e); + }); + // endpoints. note they can have an enclosing div, or not. + _addOneDelegate(id, ".jtk-endpoint", function (e) { + _oneDelegateHandler(id, e, "endpoint"); + }); + // overlays + _addOneDelegate(id, ".jtk-overlay", function (e) { + _oneDelegateHandler(id, e); + }); + }; + + for (var i = 0; i < events.length; i++) { + _oneDelegate(events[i]); + } + + // managed elements + for (var elId in managedElements) { + var el = managedElements[elId].el; + if (el.parentNode === previousContainer) { + previousContainer.removeChild(el); + _container.appendChild(el); + } + } + + }; + this.getContainer = function () { + return _container; + }; + + this.bind = function (event, fn) { + if ("ready" === event && initialized) { + fn(); + } + else { + _bb.apply(_currentInstance, [event, fn]); + } + }; + + _currentInstance.importDefaults = function (d) { + for (var i in d) { + _currentInstance.Defaults[i] = d[i]; + } + if (d.Container) { + _currentInstance.setContainer(d.Container); + } + + return _currentInstance; + }; + + _currentInstance.restoreDefaults = function () { + _currentInstance.Defaults = jsPlumb.extend({}, _initialDefaults); + return _currentInstance; + }; + + var log = null, + initialized = false, + // TODO remove from window scope + connections = [], + // map of element id -> endpoint lists. an element can have an arbitrary + // number of endpoints on it, and not all of them have to be connected + // to anything. + endpointsByElement = {}, + endpointsByUUID = {}, + managedElements = {}, + offsets = {}, + offsetTimestamps = {}, + draggableStates = {}, + connectionBeingDragged = false, + sizes = [], + _suspendDrawing = false, + _suspendedAt = null, + DEFAULT_SCOPE = this.Defaults.Scope, + _curIdStamp = 1, + _idstamp = function () { + return "" + _curIdStamp++; + }, + + // + // appends an element to some other element, which is calculated as follows: + // + // 1. if Container exists, use that element. + // 2. if the 'parent' parameter exists, use that. + // 3. otherwise just use the root element. + // + // + _appendElement = function (el, parent) { + if (_container) { + _container.appendChild(el); + } + else if (!parent) { + this.appendToRoot(el); + } + else { + this.getElement(parent).appendChild(el); + } + }.bind(this), + + // + // Draws an endpoint and its connections. this is the main entry point into drawing connections as well + // as endpoints, since jsPlumb is endpoint-centric under the hood. + // + // @param element element to draw (of type library specific element object) + // @param ui UI object from current library's event system. optional. + // @param timestamp timestamp for this paint cycle. used to speed things up a little by cutting down the amount of offset calculations we do. + // @param clearEdits defaults to false; indicates that mouse edits for connectors should be cleared + /// + _draw = function (element, ui, timestamp, clearEdits) { + + if (!_suspendDrawing) { + var id = _getId(element), + repaintEls, + dm = _currentInstance.getDragManager(); + + if (dm) { + repaintEls = dm.getElementsForDraggable(id); + } + + if (timestamp == null) { + timestamp = _timestamp(); + } + + // update the offset of everything _before_ we try to draw anything. + var o = _updateOffset({ elId: id, offset: ui, recalc: false, timestamp: timestamp }); + + if (repaintEls && o && o.o) { + for (var i in repaintEls) { + _updateOffset({ + elId: repaintEls[i].id, + offset: { + left: o.o.left + repaintEls[i].offset.left, + top: o.o.top + repaintEls[i].offset.top + }, + recalc: false, + timestamp: timestamp + }); + } + } + + _currentInstance.anchorManager.redraw(id, ui, timestamp, null, clearEdits); + + if (repaintEls) { + for (var j in repaintEls) { + _currentInstance.anchorManager.redraw(repaintEls[j].id, ui, timestamp, repaintEls[j].offset, clearEdits, true); + } + } + } + }, + + // + // gets an Endpoint by uuid. + // + _getEndpoint = function (uuid) { + return endpointsByUUID[uuid]; + }, + + /** + * inits a draggable if it's not already initialised. + * TODO: somehow abstract this to the adapter, because the concept of "draggable" has no + * place on the server. + */ + + + _scopeMatch = function (e1, e2) { + var s1 = e1.scope.split(/\s/), s2 = e2.scope.split(/\s/); + for (var i = 0; i < s1.length; i++) { + for (var j = 0; j < s2.length; j++) { + if (s2[j] === s1[i]) { + return true; + } + } + } + + return false; + }, + + _mergeOverrides = function (def, values) { + var m = jsPlumb.extend({}, def); + for (var i in values) { + if (values[i]) { + m[i] = values[i]; + } + } + return m; + }, + + /* + * prepares a final params object that can be passed to _newConnection, taking into account defaults, events, etc. + */ + _prepareConnectionParams = function (params, referenceParams) { + var _p = jsPlumb.extend({ }, params); + if (referenceParams) { + jsPlumb.extend(_p, referenceParams); + } + + // hotwire endpoints passed as source or target to sourceEndpoint/targetEndpoint, respectively. + if (_p.source) { + if (_p.source.endpoint) { + _p.sourceEndpoint = _p.source; + } + else { + _p.source = _currentInstance.getElement(_p.source); + } + } + if (_p.target) { + if (_p.target.endpoint) { + _p.targetEndpoint = _p.target; + } + else { + _p.target = _currentInstance.getElement(_p.target); + } + } + + // test for endpoint uuids to connect + if (params.uuids) { + _p.sourceEndpoint = _getEndpoint(params.uuids[0]); + _p.targetEndpoint = _getEndpoint(params.uuids[1]); + } + + // now ensure that if we do have Endpoints already, they're not full. + // source: + if (_p.sourceEndpoint && _p.sourceEndpoint.isFull()) { + _ju.log(_currentInstance, "could not add connection; source endpoint is full"); + return; + } + + // target: + if (_p.targetEndpoint && _p.targetEndpoint.isFull()) { + _ju.log(_currentInstance, "could not add connection; target endpoint is full"); + return; + } + + // if source endpoint mandates connection type and nothing specified in our params, use it. + if (!_p.type && _p.sourceEndpoint) { + _p.type = _p.sourceEndpoint.connectionType; + } + + // copy in any connectorOverlays that were specified on the source endpoint. + // it doesnt copy target endpoint overlays. i'm not sure if we want it to or not. + if (_p.sourceEndpoint && _p.sourceEndpoint.connectorOverlays) { + _p.overlays = _p.overlays || []; + for (var i = 0, j = _p.sourceEndpoint.connectorOverlays.length; i < j; i++) { + _p.overlays.push(_p.sourceEndpoint.connectorOverlays[i]); + } + } + + // scope + if (_p.sourceEndpoint && _p.sourceEndpoint.scope) { + _p.scope = _p.sourceEndpoint.scope; + } + + // pointer events + if (!_p["pointer-events"] && _p.sourceEndpoint && _p.sourceEndpoint.connectorPointerEvents) { + _p["pointer-events"] = _p.sourceEndpoint.connectorPointerEvents; + } + + + var _addEndpoint = function (el, def, idx) { + var params = _mergeOverrides(def, { + anchor: _p.anchors ? _p.anchors[idx] : _p.anchor, + endpoint: _p.endpoints ? _p.endpoints[idx] : _p.endpoint, + paintStyle: _p.endpointStyles ? _p.endpointStyles[idx] : _p.endpointStyle, + hoverPaintStyle: _p.endpointHoverStyles ? _p.endpointHoverStyles[idx] : _p.endpointHoverStyle + }); + return _currentInstance.addEndpoint(el, params); + }; + + // check for makeSource/makeTarget specs. + + var _oneElementDef = function (type, idx, defs, matchType) { + if (_p[type] && !_p[type].endpoint && !_p[type + "Endpoint"] && !_p.newConnection) { + var tid = _getId(_p[type]), tep = defs[tid]; + + tep = tep ? tep[matchType] : null; + + if (tep) { + // if not enabled, return. + if (!tep.enabled) { + return false; + } + + var epDef = jsPlumb.extend({}, tep.def); + delete epDef.label; + + var newEndpoint = tep.endpoint != null && tep.endpoint._jsPlumb ? tep.endpoint : _addEndpoint(_p[type], epDef, idx); + if (newEndpoint.isFull()) { + return false; + } + _p[type + "Endpoint"] = newEndpoint; + if (!_p.scope && epDef.scope) { + _p.scope = epDef.scope; + } // provide scope if not already provided and endpoint def has one. + if (tep.uniqueEndpoint) { + if (!tep.endpoint) { + tep.endpoint = newEndpoint; + newEndpoint.setDeleteOnEmpty(false); + } + else { + newEndpoint.finalEndpoint = tep.endpoint; + } + } else { + newEndpoint.setDeleteOnEmpty(true); + } + + // + // copy in connector overlays if present on the source definition. + // + if (idx === 0 && tep.def.connectorOverlays) { + _p.overlays = _p.overlays || []; + Array.prototype.push.apply(_p.overlays, tep.def.connectorOverlays); + } + } + } + }; + + if (_oneElementDef("source", 0, this.sourceEndpointDefinitions, _p.type || "default") === false) { + return; + } + if (_oneElementDef("target", 1, this.targetEndpointDefinitions, _p.type || "default") === false) { + return; + } + + // last, ensure scopes match + if (_p.sourceEndpoint && _p.targetEndpoint) { + if (!_scopeMatch(_p.sourceEndpoint, _p.targetEndpoint)) { + _p = null; + } + } + + return _p; + }.bind(_currentInstance), + + _newConnection = function (params) { + var connectionFunc = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(); + + params._jsPlumb = _currentInstance; + params.newConnection = _newConnection; + params.newEndpoint = _newEndpoint; + params.endpointsByUUID = endpointsByUUID; + params.endpointsByElement = endpointsByElement; + params.finaliseConnection = _finaliseConnection; + params.id = "con_" + _idstamp(); + var con = new connectionFunc(params); + + // if the connection is draggable, then maybe we need to tell the target endpoint to init the + // dragging code. it won't run again if it already configured to be draggable. + if (con.isDetachable()) { + con.endpoints[0].initDraggable("_jsPlumbSource"); + con.endpoints[1].initDraggable("_jsPlumbTarget"); + } + + return con; + }, + + // + // adds the connection to the backing model, fires an event if necessary and then redraws + // + _finaliseConnection = _currentInstance.finaliseConnection = function (jpc, params, originalEvent, doInformAnchorManager) { + params = params || {}; + // add to list of connections (by scope). + if (!jpc.suspendedEndpoint) { + connections.push(jpc); + } + + jpc.pending = null; + + // turn off isTemporarySource on the source endpoint (only viable on first draw) + jpc.endpoints[0].isTemporarySource = false; + + // always inform the anchor manager + // except that if jpc has a suspended endpoint it's not true to say the + // connection is new; it has just (possibly) moved. the question is whether + // to make that call here or in the anchor manager. i think perhaps here. + if (doInformAnchorManager !== false) { + _currentInstance.anchorManager.newConnection(jpc); + } + + // force a paint + _draw(jpc.source); + + // fire an event + if (!params.doNotFireConnectionEvent && params.fireEvent !== false) { + + var eventArgs = { + connection: jpc, + source: jpc.source, target: jpc.target, + sourceId: jpc.sourceId, targetId: jpc.targetId, + sourceEndpoint: jpc.endpoints[0], targetEndpoint: jpc.endpoints[1] + }; + + _currentInstance.fire("connection", eventArgs, originalEvent); + } + }, + + /* + factory method to prepare a new endpoint. this should always be used instead of creating Endpoints + manually, since this method attaches event listeners and an id. + */ + _newEndpoint = function (params, id) { + var endpointFunc = _currentInstance.Defaults.EndpointType || jsPlumb.Endpoint; + var _p = jsPlumb.extend({}, params); + //delete _p.label; // not supported by endpoint. + _p._jsPlumb = _currentInstance; + _p.newConnection = _newConnection; + _p.newEndpoint = _newEndpoint; + _p.endpointsByUUID = endpointsByUUID; + _p.endpointsByElement = endpointsByElement; + _p.fireDetachEvent = fireDetachEvent; + _p.elementId = id || _getId(_p.source); + var ep = new endpointFunc(_p); + ep.id = "ep_" + _idstamp(); + _manage(_p.elementId, _p.source); + + if (!jsPlumb.headless) { + _currentInstance.getDragManager().endpointAdded(_p.source, id); + } + + return ep; + }, + + /* + * performs the given function operation on all the connections found + * for the given element id; this means we find all the endpoints for + * the given element, and then for each endpoint find the connectors + * connected to it. then we pass each connection in to the given + * function. + */ + _operation = function (elId, func, endpointFunc) { + var endpoints = endpointsByElement[elId]; + if (endpoints && endpoints.length) { + for (var i = 0, ii = endpoints.length; i < ii; i++) { + for (var j = 0, jj = endpoints[i].connections.length; j < jj; j++) { + var retVal = func(endpoints[i].connections[j]); + // if the function passed in returns true, we exit. + // most functions return false. + if (retVal) { + return; + } + } + if (endpointFunc) { + endpointFunc(endpoints[i]); + } + } + } + }, + + _setDraggable = function (element, draggable) { + return jsPlumb.each(element, function (el) { + if (_currentInstance.isDragSupported(el)) { + draggableStates[_currentInstance.getAttribute(el, "id")] = draggable; + _currentInstance.setElementDraggable(el, draggable); + } + }); + }, + /* + * private method to do the business of hiding/showing. + * + * @param el + * either Id of the element in question or a library specific + * object for the element. + * @param state + * String specifying a value for the css 'display' property + * ('block' or 'none'). + */ + _setVisible = function (el, state, alsoChangeEndpoints) { + state = state === "block"; + var endpointFunc = null; + if (alsoChangeEndpoints) { + endpointFunc = function (ep) { + ep.setVisible(state, true, true); + }; + } + var info = _info(el); + _operation(info.id, function (jpc) { + if (state && alsoChangeEndpoints) { + // this test is necessary because this functionality is new, and i wanted to maintain backwards compatibility. + // this block will only set a connection to be visible if the other endpoint in the connection is also visible. + var oidx = jpc.sourceId === info.id ? 1 : 0; + if (jpc.endpoints[oidx].isVisible()) { + jpc.setVisible(true); + } + } + else { // the default behaviour for show, and what always happens for hide, is to just set the visibility without getting clever. + jpc.setVisible(state); + } + }, endpointFunc); + }, + /** + * private method to do the business of toggling hiding/showing. + */ + _toggleVisible = function (elId, changeEndpoints) { + var endpointFunc = null; + if (changeEndpoints) { + endpointFunc = function (ep) { + var state = ep.isVisible(); + ep.setVisible(!state); + }; + } + _operation(elId, function (jpc) { + var state = jpc.isVisible(); + jpc.setVisible(!state); + }, endpointFunc); + }, + + // TODO comparison performance + _getCachedData = function (elId) { + var o = offsets[elId]; + if (!o) { + return _updateOffset({elId: elId}); + } + else { + return {o: o, s: sizes[elId]}; + } + }, + + /** + * gets an id for the given element, creating and setting one if + * necessary. the id is of the form + * + * jsPlumb_<instance index>_<index in instance> + * + * where "index in instance" is a monotonically increasing integer that starts at 0, + * for each instance. this method is used not only to assign ids to elements that do not + * have them but also to connections and endpoints. + */ + _getId = function (element, uuid, doNotCreateIfNotFound) { + if (_ju.isString(element)) { + return element; + } + if (element == null) { + return null; + } + var id = _currentInstance.getAttribute(element, "id"); + if (!id || id === "undefined") { + // check if fixed uuid parameter is given + if (arguments.length === 2 && arguments[1] !== undefined) { + id = uuid; + } + else if (arguments.length === 1 || (arguments.length === 3 && !arguments[2])) { + id = "jsPlumb_" + _instanceIndex + "_" + _idstamp(); + } + + if (!doNotCreateIfNotFound) { + _currentInstance.setAttribute(element, "id", id); + } + } + return id; + }; + + this.setConnectionBeingDragged = function (v) { + connectionBeingDragged = v; + }; + this.isConnectionBeingDragged = function () { + return connectionBeingDragged; + }; + + /** + * Returns a map of all the elements this jsPlumbInstance is currently managing. + * @returns {Object} Map of [id-> {el, endpoint[], connection, position}] information. + */ + this.getManagedElements = function() { + return managedElements; + }; + + this.connectorClass = "jtk-connector"; + this.connectorOutlineClass = "jtk-connector-outline"; + this.connectedClass = "jtk-connected"; + this.hoverClass = "jtk-hover"; + this.endpointClass = "jtk-endpoint"; + this.endpointConnectedClass = "jtk-endpoint-connected"; + this.endpointFullClass = "jtk-endpoint-full"; + this.endpointDropAllowedClass = "jtk-endpoint-drop-allowed"; + this.endpointDropForbiddenClass = "jtk-endpoint-drop-forbidden"; + this.overlayClass = "jtk-overlay"; + this.draggingClass = "jtk-dragging";// CONVERTED + this.elementDraggingClass = "jtk-element-dragging";// CONVERTED + this.sourceElementDraggingClass = "jtk-source-element-dragging"; // CONVERTED + this.targetElementDraggingClass = "jtk-target-element-dragging";// CONVERTED + this.endpointAnchorClassPrefix = "jtk-endpoint-anchor"; + this.hoverSourceClass = "jtk-source-hover"; + this.hoverTargetClass = "jtk-target-hover"; + this.dragSelectClass = "jtk-drag-select"; + + this.Anchors = {}; + this.Connectors = { "svg": {} }; + this.Endpoints = { "svg": {} }; + this.Overlays = { "svg": {} } ; + this.ConnectorRenderers = {}; + this.SVG = "svg"; + +// --------------------------- jsPlumbInstance public API --------------------------------------------------------- + + + this.addEndpoint = function (el, params, referenceParams) { + referenceParams = referenceParams || {}; + var p = jsPlumb.extend({}, referenceParams); + jsPlumb.extend(p, params); + p.endpoint = p.endpoint || _currentInstance.Defaults.Endpoint; + p.paintStyle = p.paintStyle || _currentInstance.Defaults.EndpointStyle; + + var results = [], + inputs = (_ju.isArray(el) || (el.length != null && !_ju.isString(el))) ? el : [ el ]; + + for (var i = 0, j = inputs.length; i < j; i++) { + p.source = _currentInstance.getElement(inputs[i]); + _ensureContainer(p.source); + + var id = _getId(p.source), e = _newEndpoint(p, id); + + // ensure element is managed. + var myOffset = _manage(id, p.source).info.o; + _ju.addToList(endpointsByElement, id, e); + + if (!_suspendDrawing) { + e.paint({ + anchorLoc: e.anchor.compute({ xy: [ myOffset.left, myOffset.top ], wh: sizes[id], element: e, timestamp: _suspendedAt }), + timestamp: _suspendedAt + }); + } + + results.push(e); + } + + return results.length === 1 ? results[0] : results; + }; + + this.addEndpoints = function (el, endpoints, referenceParams) { + var results = []; + for (var i = 0, j = endpoints.length; i < j; i++) { + var e = _currentInstance.addEndpoint(el, endpoints[i], referenceParams); + if (_ju.isArray(e)) { + Array.prototype.push.apply(results, e); + } + else { + results.push(e); + } + } + return results; + }; + + this.animate = function (el, properties, options) { + if (!this.animationSupported) { + return false; + } + + options = options || {}; + var del = _currentInstance.getElement(el), + id = _getId(del), + stepFunction = jsPlumb.animEvents.step, + completeFunction = jsPlumb.animEvents.complete; + + options[stepFunction] = _ju.wrap(options[stepFunction], function () { + _currentInstance.revalidate(id); + }); + + // onComplete repaints, just to make sure everything looks good at the end of the animation. + options[completeFunction] = _ju.wrap(options[completeFunction], function () { + _currentInstance.revalidate(id); + }); + + _currentInstance.doAnimate(del, properties, options); + }; + + /** + * checks for a listener for the given condition, executing it if found, passing in the given value. + * condition listeners would have been attached using "bind" (which is, you could argue, now overloaded, since + * firing click events etc is a bit different to what this does). i thought about adding a "bindCondition" + * or something, but decided against it, for the sake of simplicity. jsPlumb will never fire one of these + * condition events anyway. + */ + this.checkCondition = function (conditionName, args) { + var l = _currentInstance.getListener(conditionName), + r = true; + + if (l && l.length > 0) { + var values = Array.prototype.slice.call(arguments, 1); + try { + for (var i = 0, j = l.length; i < j; i++) { + r = r && l[i].apply(l[i], values); + } + } + catch (e) { + _ju.log(_currentInstance, "cannot check condition [" + conditionName + "]" + e); + } + } + return r; + }; + + this.connect = function (params, referenceParams) { + // prepare a final set of parameters to create connection with + var _p = _prepareConnectionParams(params, referenceParams), jpc; + // TODO probably a nicer return value if the connection was not made. _prepareConnectionParams + // will return null (and log something) if either endpoint was full. what would be nicer is to + // create a dedicated 'error' object. + if (_p) { + if (_p.source == null && _p.sourceEndpoint == null) { + _ju.log("Cannot establish connection - source does not exist"); + return; + } + if (_p.target == null && _p.targetEndpoint == null) { + _ju.log("Cannot establish connection - target does not exist"); + return; + } + _ensureContainer(_p.source); + // create the connection. it is not yet registered + jpc = _newConnection(_p); + // now add it the model, fire an event, and redraw + _finaliseConnection(jpc, _p); + } + return jpc; + }; + + var stTypes = [ + { el: "source", elId: "sourceId", epDefs: "sourceEndpointDefinitions" }, + { el: "target", elId: "targetId", epDefs: "targetEndpointDefinitions" } + ]; + + var _set = function (c, el, idx, doNotRepaint) { + var ep, _st = stTypes[idx], cId = c[_st.elId], cEl = c[_st.el], sid, sep, + oldEndpoint = c.endpoints[idx]; + + var evtParams = { + index: idx, + originalSourceId: idx === 0 ? cId : c.sourceId, + newSourceId: c.sourceId, + originalTargetId: idx === 1 ? cId : c.targetId, + newTargetId: c.targetId, + connection: c + }; + + if (el.constructor === jsPlumb.Endpoint) { + ep = el; + ep.addConnection(c); + el = ep.element; + } + else { + sid = _getId(el); + sep = this[_st.epDefs][sid]; + + if (sid === c[_st.elId]) { + ep = null; // dont change source/target if the element is already the one given. + } + else if (sep) { + for (var t in sep) { + if (!sep[t].enabled) { + return; + } + ep = sep[t].endpoint != null && sep[t].endpoint._jsPlumb ? sep[t].endpoint : this.addEndpoint(el, sep[t].def); + if (sep[t].uniqueEndpoint) { + sep[t].endpoint = ep; + } + ep.addConnection(c); + } + } + else { + ep = c.makeEndpoint(idx === 0, el, sid); + } + } + + if (ep != null) { + oldEndpoint.detachFromConnection(c); + c.endpoints[idx] = ep; + c[_st.el] = ep.element; + c[_st.elId] = ep.elementId; + evtParams[idx === 0 ? "newSourceId" : "newTargetId"] = ep.elementId; + + fireMoveEvent(evtParams); + + if (!doNotRepaint) { + c.repaint(); + } + } + + evtParams.element = el; + return evtParams; + + }.bind(this); + + this.setSource = function (connection, el, doNotRepaint) { + var p = _set(connection, el, 0, doNotRepaint); + this.anchorManager.sourceChanged(p.originalSourceId, p.newSourceId, connection, p.el); + }; + this.setTarget = function (connection, el, doNotRepaint) { + var p = _set(connection, el, 1, doNotRepaint); + this.anchorManager.updateOtherEndpoint(p.originalSourceId, p.originalTargetId, p.newTargetId, connection); + }; + + this.deleteEndpoint = function (object, dontUpdateHover, deleteAttachedObjects) { + var endpoint = (typeof object === "string") ? endpointsByUUID[object] : object; + if (endpoint) { + _currentInstance.deleteObject({ endpoint: endpoint, dontUpdateHover: dontUpdateHover, deleteAttachedObjects:deleteAttachedObjects }); + } + return _currentInstance; + }; + + this.deleteEveryEndpoint = function () { + var _is = _currentInstance.setSuspendDrawing(true); + for (var id in endpointsByElement) { + var endpoints = endpointsByElement[id]; + if (endpoints && endpoints.length) { + for (var i = 0, j = endpoints.length; i < j; i++) { + _currentInstance.deleteEndpoint(endpoints[i], true); + } + } + } + endpointsByElement = {}; + managedElements = {}; + endpointsByUUID = {}; + offsets = {}; + offsetTimestamps = {}; + _currentInstance.anchorManager.reset(); + var dm = _currentInstance.getDragManager(); + if (dm) { + dm.reset(); + } + if (!_is) { + _currentInstance.setSuspendDrawing(false); + } + return _currentInstance; + }; + + var fireDetachEvent = function (jpc, doFireEvent, originalEvent) { + // may have been given a connection, or in special cases, an object + var connType = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(), + argIsConnection = jpc.constructor === connType, + params = argIsConnection ? { + connection: jpc, + source: jpc.source, target: jpc.target, + sourceId: jpc.sourceId, targetId: jpc.targetId, + sourceEndpoint: jpc.endpoints[0], targetEndpoint: jpc.endpoints[1] + } : jpc; + + if (doFireEvent) { + _currentInstance.fire("connectionDetached", params, originalEvent); + } + + // always fire this. used by internal jsplumb stuff. + _currentInstance.fire("internal.connectionDetached", params, originalEvent); + + _currentInstance.anchorManager.connectionDetached(params); + }; + + var fireMoveEvent = _currentInstance.fireMoveEvent = function (params, evt) { + _currentInstance.fire("connectionMoved", params, evt); + }; + + this.unregisterEndpoint = function (endpoint) { + if (endpoint._jsPlumb.uuid) { + endpointsByUUID[endpoint._jsPlumb.uuid] = null; + } + _currentInstance.anchorManager.deleteEndpoint(endpoint); + // TODO at least replace this with a removeWithFunction call. + for (var e in endpointsByElement) { + var endpoints = endpointsByElement[e]; + if (endpoints) { + var newEndpoints = []; + for (var i = 0, j = endpoints.length; i < j; i++) { + if (endpoints[i] !== endpoint) { + newEndpoints.push(endpoints[i]); + } + } + + endpointsByElement[e] = newEndpoints; + } + if (endpointsByElement[e].length < 1) { + delete endpointsByElement[e]; + } + } + }; + + var IS_DETACH_ALLOWED = "isDetachAllowed"; + var BEFORE_DETACH = "beforeDetach"; + var CHECK_CONDITION = "checkCondition"; + + /** + * Deletes a Connection. + * @method deleteConnection + * @param connection Connection to delete + * @param {Object} [params] Optional delete parameters + * @param {Boolean} [params.doNotFireEvent=false] If true, a connection detached event will not be fired. Otherwise one will. + * @param {Boolean} [params.force=false] If true, the connection will be deleted even if a beforeDetach interceptor tries to stop the deletion. + * @returns {Boolean} True if the connection was deleted, false otherwise. + */ + this.deleteConnection = function(connection, params) { + + if (connection != null) { + params = params || {}; + + if (params.force || _ju.functionChain(true, false, [ + [ connection.endpoints[0], IS_DETACH_ALLOWED, [ connection ] ], + [ connection.endpoints[1], IS_DETACH_ALLOWED, [ connection ] ], + [ connection, IS_DETACH_ALLOWED, [ connection ] ], + [ _currentInstance, CHECK_CONDITION, [ BEFORE_DETACH, connection ] ] + ])) { + + connection.setHover(false); + fireDetachEvent(connection, !connection.pending && params.fireEvent !== false, params.originalEvent); + + connection.endpoints[0].detachFromConnection(connection); + connection.endpoints[1].detachFromConnection(connection); + _ju.removeWithFunction(connections, function (_c) { + return connection.id === _c.id; + }); + + connection.cleanup(); + connection.destroy(); + return true; + } + } + return false; + }; + + /** + * Remove all Connections from all elements, but leaves Endpoints in place ((unless a connection is set to auto delete its Endpoints). + * @method deleteEveryConnection + * @param {Object} [params] optional params object for the call + * @param {Boolean} [params.fireEvent=true] Whether or not to fire detach events + * @param {Boolean} [params.forceDetach=false] If true, this call will ignore any `beforeDetach` interceptors. + * @returns {Number} The number of connections that were deleted. + */ + this.deleteEveryConnection = function (params) { + params = params || {}; + var count = connections.length, deletedCount = 0; + _currentInstance.batch(function () { + for (var i = 0; i < count; i++) { + deletedCount += _currentInstance.deleteConnection(connections[0], params) ? 1 : 0; + } + }); + return deletedCount; + }; + + /** + * Removes all an element's Connections. + * @method deleteConnectionsForElement + * @param {Object} el Either the id of the element, or a selector for the element. + * @param {Object} [params] Optional parameters. + * @param {Boolean} [params.fireEvent=true] Whether or not to fire the detach event. + * @param {Boolean} [params.forceDetach=false] If true, this call will ignore any `beforeDetach` interceptors. + * @return {jsPlumbInstance} The current jsPlumb instance. + */ + this.deleteConnectionsForElement = function (el, params) { + params = params || {}; + el = _currentInstance.getElement(el); + var id = _getId(el), endpoints = endpointsByElement[id]; + if (endpoints && endpoints.length) { + for (var i = 0, j = endpoints.length; i < j; i++) { + endpoints[i].deleteEveryConnection(params); + } + } + return _currentInstance; + }; + + /// not public. but of course its exposed. how to change this. + this.deleteObject = function (params) { + var result = { + endpoints: {}, + connections: {}, + endpointCount: 0, + connectionCount: 0 + }, + deleteAttachedObjects = params.deleteAttachedObjects !== false; + + var unravelConnection = function (connection) { + if (connection != null && result.connections[connection.id] == null) { + if (!params.dontUpdateHover && connection._jsPlumb != null) { + connection.setHover(false); + } + result.connections[connection.id] = connection; + result.connectionCount++; + } + }; + var unravelEndpoint = function (endpoint) { + if (endpoint != null && result.endpoints[endpoint.id] == null) { + if (!params.dontUpdateHover && endpoint._jsPlumb != null) { + endpoint.setHover(false); + } + result.endpoints[endpoint.id] = endpoint; + result.endpointCount++; + + if (deleteAttachedObjects) { + for (var i = 0; i < endpoint.connections.length; i++) { + var c = endpoint.connections[i]; + unravelConnection(c); + } + } + } + }; + + if (params.connection) { + unravelConnection(params.connection); + } + else { + unravelEndpoint(params.endpoint); + } + + // loop through connections + for (var i in result.connections) { + var c = result.connections[i]; + if (c._jsPlumb) { + _ju.removeWithFunction(connections, function (_c) { + return c.id === _c.id; + }); + + fireDetachEvent(c, params.fireEvent === false ? false : !c.pending, params.originalEvent); + var doNotCleanup = params.deleteAttachedObjects == null ? null : !params.deleteAttachedObjects; + + c.endpoints[0].detachFromConnection(c, null, doNotCleanup); + c.endpoints[1].detachFromConnection(c, null, doNotCleanup); + + c.cleanup(true); + c.destroy(true); + } + } + + // loop through endpoints + for (var j in result.endpoints) { + var e = result.endpoints[j]; + if (e._jsPlumb) { + _currentInstance.unregisterEndpoint(e); + // FIRE some endpoint deleted event? + e.cleanup(true); + e.destroy(true); + } + } + + return result; + }; + + + // helpers for select/selectEndpoints + var _setOperation = function (list, func, args, selector) { + for (var i = 0, j = list.length; i < j; i++) { + list[i][func].apply(list[i], args); + } + return selector(list); + }, + _getOperation = function (list, func, args) { + var out = []; + for (var i = 0, j = list.length; i < j; i++) { + out.push([ list[i][func].apply(list[i], args), list[i] ]); + } + return out; + }, + setter = function (list, func, selector) { + return function () { + return _setOperation(list, func, arguments, selector); + }; + }, + getter = function (list, func) { + return function () { + return _getOperation(list, func, arguments); + }; + }, + prepareList = function (input, doNotGetIds) { + var r = []; + if (input) { + if (typeof input === 'string') { + if (input === "*") { + return input; + } + r.push(input); + } + else { + if (doNotGetIds) { + r = input; + } + else { + if (input.length) { + for (var i = 0, j = input.length; i < j; i++) { + r.push(_info(input[i]).id); + } + } + else { + r.push(_info(input).id); + } + } + } + } + return r; + }, + filterList = function (list, value, missingIsFalse) { + if (list === "*") { + return true; + } + return list.length > 0 ? list.indexOf(value) !== -1 : !missingIsFalse; + }; + + // get some connections, specifying source/target/scope + this.getConnections = function (options, flat) { + if (!options) { + options = {}; + } else if (options.constructor === String) { + options = { "scope": options }; + } + var scope = options.scope || _currentInstance.getDefaultScope(), + scopes = prepareList(scope, true), + sources = prepareList(options.source), + targets = prepareList(options.target), + results = (!flat && scopes.length > 1) ? {} : [], + _addOne = function (scope, obj) { + if (!flat && scopes.length > 1) { + var ss = results[scope]; + if (ss == null) { + ss = results[scope] = []; + } + ss.push(obj); + } else { + results.push(obj); + } + }; + + for (var j = 0, jj = connections.length; j < jj; j++) { + var c = connections[j], + sourceId = c.proxies && c.proxies[0] ? c.proxies[0].originalEp.elementId : c.sourceId, + targetId = c.proxies && c.proxies[1] ? c.proxies[1].originalEp.elementId : c.targetId; + + if (filterList(scopes, c.scope) && filterList(sources, sourceId) && filterList(targets, targetId)) { + _addOne(c.scope, c); + } + } + + return results; + }; + + var _curryEach = function (list, executor) { + return function (f) { + for (var i = 0, ii = list.length; i < ii; i++) { + f(list[i]); + } + return executor(list); + }; + }, + _curryGet = function (list) { + return function (idx) { + return list[idx]; + }; + }; + + var _makeCommonSelectHandler = function (list, executor) { + var out = { + length: list.length, + each: _curryEach(list, executor), + get: _curryGet(list) + }, + setters = ["setHover", "removeAllOverlays", "setLabel", "addClass", "addOverlay", "removeOverlay", + "removeOverlays", "showOverlay", "hideOverlay", "showOverlays", "hideOverlays", "setPaintStyle", + "setHoverPaintStyle", "setSuspendEvents", "setParameter", "setParameters", "setVisible", + "repaint", "addType", "toggleType", "removeType", "removeClass", "setType", "bind", "unbind" ], + + getters = ["getLabel", "getOverlay", "isHover", "getParameter", "getParameters", "getPaintStyle", + "getHoverPaintStyle", "isVisible", "hasType", "getType", "isSuspendEvents" ], + i, ii; + + for (i = 0, ii = setters.length; i < ii; i++) { + out[setters[i]] = setter(list, setters[i], executor); + } + + for (i = 0, ii = getters.length; i < ii; i++) { + out[getters[i]] = getter(list, getters[i]); + } + + return out; + }; + + var _makeConnectionSelectHandler = function (list) { + var common = _makeCommonSelectHandler(list, _makeConnectionSelectHandler); + return jsPlumb.extend(common, { + // setters + setDetachable: setter(list, "setDetachable", _makeConnectionSelectHandler), + setReattach: setter(list, "setReattach", _makeConnectionSelectHandler), + setConnector: setter(list, "setConnector", _makeConnectionSelectHandler), + delete: function () { + for (var i = 0, ii = list.length; i < ii; i++) { + _currentInstance.deleteConnection(list[i]); + } + }, + // getters + isDetachable: getter(list, "isDetachable"), + isReattach: getter(list, "isReattach") + }); + }; + + var _makeEndpointSelectHandler = function (list) { + var common = _makeCommonSelectHandler(list, _makeEndpointSelectHandler); + return jsPlumb.extend(common, { + setEnabled: setter(list, "setEnabled", _makeEndpointSelectHandler), + setAnchor: setter(list, "setAnchor", _makeEndpointSelectHandler), + isEnabled: getter(list, "isEnabled"), + deleteEveryConnection: function () { + for (var i = 0, ii = list.length; i < ii; i++) { + list[i].deleteEveryConnection(); + } + }, + "delete": function () { + for (var i = 0, ii = list.length; i < ii; i++) { + _currentInstance.deleteEndpoint(list[i]); + } + } + }); + }; + + this.select = function (params) { + params = params || {}; + params.scope = params.scope || "*"; + return _makeConnectionSelectHandler(params.connections || _currentInstance.getConnections(params, true)); + }; + + this.selectEndpoints = function (params) { + params = params || {}; + params.scope = params.scope || "*"; + var noElementFilters = !params.element && !params.source && !params.target, + elements = noElementFilters ? "*" : prepareList(params.element), + sources = noElementFilters ? "*" : prepareList(params.source), + targets = noElementFilters ? "*" : prepareList(params.target), + scopes = prepareList(params.scope, true); + + var ep = []; + + for (var el in endpointsByElement) { + var either = filterList(elements, el, true), + source = filterList(sources, el, true), + sourceMatchExact = sources !== "*", + target = filterList(targets, el, true), + targetMatchExact = targets !== "*"; + + // if they requested 'either' then just match scope. otherwise if they requested 'source' (not as a wildcard) then we have to match only endpoints that have isSource set to to true, and the same thing with isTarget. + if (either || source || target) { + inner: + for (var i = 0, ii = endpointsByElement[el].length; i < ii; i++) { + var _ep = endpointsByElement[el][i]; + if (filterList(scopes, _ep.scope, true)) { + + var noMatchSource = (sourceMatchExact && sources.length > 0 && !_ep.isSource), + noMatchTarget = (targetMatchExact && targets.length > 0 && !_ep.isTarget); + + if (noMatchSource || noMatchTarget) { + continue inner; + } + + ep.push(_ep); + } + } + } + } + + return _makeEndpointSelectHandler(ep); + }; + + // get all connections managed by the instance of jsplumb. + this.getAllConnections = function () { + return connections; + }; + this.getDefaultScope = function () { + return DEFAULT_SCOPE; + }; + // get an endpoint by uuid. + this.getEndpoint = _getEndpoint; + /** + * Gets the list of Endpoints for a given element. + * @method getEndpoints + * @param {String|Element|Selector} el The element to get endpoints for. + * @return {Endpoint[]} An array of Endpoints for the specified element. + */ + this.getEndpoints = function (el) { + return endpointsByElement[_info(el).id] || []; + }; + // gets the default endpoint type. used when subclassing. see wiki. + this.getDefaultEndpointType = function () { + return jsPlumb.Endpoint; + }; + // gets the default connection type. used when subclassing. see wiki. + this.getDefaultConnectionType = function () { + return jsPlumb.Connection; + }; + /* + * Gets an element's id, creating one if necessary. really only exposed + * for the lib-specific functionality to access; would be better to pass + * the current instance into the lib-specific code (even though this is + * a static call. i just don't want to expose it to the public API). + */ + this.getId = _getId; + this.draw = _draw; + this.info = _info; + + this.appendElement = _appendElement; + + var _hoverSuspended = false; + this.isHoverSuspended = function () { + return _hoverSuspended; + }; + this.setHoverSuspended = function (s) { + _hoverSuspended = s; + }; + + // set an element's connections to be hidden + this.hide = function (el, changeEndpoints) { + _setVisible(el, "none", changeEndpoints); + return _currentInstance; + }; + + // exposed for other objects to use to get a unique id. + this.idstamp = _idstamp; + + // ensure that, if the current container exists, it is a DOM element and not a selector. + // if it does not exist and `candidate` is supplied, the offset parent of that element will be set as the Container. + // this is used to do a better default behaviour for the case that the user has not set a container: + // addEndpoint, makeSource, makeTarget and connect all call this method with the offsetParent of the + // element in question (for connect it is the source element). So if no container is set, it is inferred + // to be the offsetParent of the first element the user tries to connect. + var _ensureContainer = function (candidate) { + if (!_container && candidate) { + var can = _currentInstance.getElement(candidate); + if (can.offsetParent) { + _currentInstance.setContainer(can.offsetParent); + } + } + }; + + var _getContainerFromDefaults = function () { + if (_currentInstance.Defaults.Container) { + _currentInstance.setContainer(_currentInstance.Defaults.Container); + } + }; + + // check if a given element is managed or not. if not, add to our map. if drawing is not suspended then + // we'll also stash its dimensions; otherwise we'll do this in a lazy way through updateOffset. + var _manage = _currentInstance.manage = function (id, element, _transient) { + if (!managedElements[id]) { + managedElements[id] = { + el: element, + endpoints: [], + connections: [] + }; + + managedElements[id].info = _updateOffset({ elId: id, timestamp: _suspendedAt }); + _currentInstance.addClass(element, "jtk-managed"); + + if (!_transient) { + _currentInstance.fire("manageElement", { id:id, info:managedElements[id].info, el:element }); + } + } + + return managedElements[id]; + }; + + var _unmanage = _currentInstance.unmanage = function(id) { + if (managedElements[id]) { + var el = managedElements[id].el; + _currentInstance.removeClass(el, "jtk-managed"); + delete managedElements[id]; + _currentInstance.fire("unmanageElement", {id:id, el:el}); + } + }; + + /** + * updates the offset and size for a given element, and stores the + * values. if 'offset' is not null we use that (it would have been + * passed in from a drag call) because it's faster; but if it is null, + * or if 'recalc' is true in order to force a recalculation, we get the current values. + * @method updateOffset + */ + var _updateOffset = function (params) { + + var timestamp = params.timestamp, recalc = params.recalc, offset = params.offset, elId = params.elId, s; + if (_suspendDrawing && !timestamp) { + timestamp = _suspendedAt; + } + if (!recalc) { + if (timestamp && timestamp === offsetTimestamps[elId]) { + return {o: params.offset || offsets[elId], s: sizes[elId]}; + } + } + if (recalc || (!offset && offsets[elId] == null)) { // if forced repaint or no offset available, we recalculate. + + // get the current size and offset, and store them + s = managedElements[elId] ? managedElements[elId].el : null; + if (s != null) { + sizes[elId] = _currentInstance.getSize(s); + offsets[elId] = _currentInstance.getOffset(s); + offsetTimestamps[elId] = timestamp; + } + } else { + offsets[elId] = offset || offsets[elId]; + if (sizes[elId] == null) { + s = managedElements[elId].el; + if (s != null) { + sizes[elId] = _currentInstance.getSize(s); + } + } + offsetTimestamps[elId] = timestamp; + } + + if (offsets[elId] && !offsets[elId].right) { + offsets[elId].right = offsets[elId].left + sizes[elId][0]; + offsets[elId].bottom = offsets[elId].top + sizes[elId][1]; + offsets[elId].width = sizes[elId][0]; + offsets[elId].height = sizes[elId][1]; + offsets[elId].centerx = offsets[elId].left + (offsets[elId].width / 2); + offsets[elId].centery = offsets[elId].top + (offsets[elId].height / 2); + } + + return {o: offsets[elId], s: sizes[elId]}; + }; + + this.updateOffset = _updateOffset; + + /** + * callback from the current library to tell us to prepare ourselves (attach + * mouse listeners etc; can't do that until the library has provided a bind method) + */ + this.init = function () { + if (!initialized) { + _getContainerFromDefaults(); + _currentInstance.anchorManager = new root.jsPlumb.AnchorManager({jsPlumbInstance: _currentInstance}); + initialized = true; + _currentInstance.fire("ready", _currentInstance); + } + }.bind(this); + + this.log = log; + this.jsPlumbUIComponent = jsPlumbUIComponent; + + /* + * Creates an anchor with the given params. + * + * + * Returns: The newly created Anchor. + * Throws: an error if a named anchor was not found. + */ + this.makeAnchor = function () { + var pp, _a = function (t, p) { + if (root.jsPlumb.Anchors[t]) { + return new root.jsPlumb.Anchors[t](p); + } + if (!_currentInstance.Defaults.DoNotThrowErrors) { + throw { msg: "jsPlumb: unknown anchor type '" + t + "'" }; + } + }; + if (arguments.length === 0) { + return null; + } + var specimen = arguments[0], elementId = arguments[1], jsPlumbInstance = arguments[2], newAnchor = null; + // if it appears to be an anchor already... + if (specimen.compute && specimen.getOrientation) { + return specimen; + } //TODO hazy here about whether it should be added or is already added somehow. + // is it the name of an anchor type? + else if (typeof specimen === "string") { + newAnchor = _a(arguments[0], {elementId: elementId, jsPlumbInstance: _currentInstance}); + } + // is it an array? it will be one of: + // an array of [spec, params] - this defines a single anchor, which may be dynamic, but has parameters. + // an array of arrays - this defines some dynamic anchors + // an array of numbers - this defines a single anchor. + else if (_ju.isArray(specimen)) { + if (_ju.isArray(specimen[0]) || _ju.isString(specimen[0])) { + // if [spec, params] format + if (specimen.length === 2 && _ju.isObject(specimen[1])) { + // if first arg is a string, its a named anchor with params + if (_ju.isString(specimen[0])) { + pp = root.jsPlumb.extend({elementId: elementId, jsPlumbInstance: _currentInstance}, specimen[1]); + newAnchor = _a(specimen[0], pp); + } + // otherwise first arg is array, second is params. we treat as a dynamic anchor, which is fine + // even if the first arg has only one entry. you could argue all anchors should be implicitly dynamic in fact. + else { + pp = root.jsPlumb.extend({elementId: elementId, jsPlumbInstance: _currentInstance, anchors: specimen[0]}, specimen[1]); + newAnchor = new root.jsPlumb.DynamicAnchor(pp); + } + } + else { + newAnchor = new jsPlumb.DynamicAnchor({anchors: specimen, selector: null, elementId: elementId, jsPlumbInstance: _currentInstance}); + } + + } + else { + var anchorParams = { + x: specimen[0], y: specimen[1], + orientation: (specimen.length >= 4) ? [ specimen[2], specimen[3] ] : [0, 0], + offsets: (specimen.length >= 6) ? [ specimen[4], specimen[5] ] : [ 0, 0 ], + elementId: elementId, + jsPlumbInstance: _currentInstance, + cssClass: specimen.length === 7 ? specimen[6] : null + }; + newAnchor = new root.jsPlumb.Anchor(anchorParams); + newAnchor.clone = function () { + return new root.jsPlumb.Anchor(anchorParams); + }; + } + } + + if (!newAnchor.id) { + newAnchor.id = "anchor_" + _idstamp(); + } + return newAnchor; + }; + + /** + * makes a list of anchors from the given list of types or coords, eg + * ["TopCenter", "RightMiddle", "BottomCenter", [0, 1, -1, -1] ] + */ + this.makeAnchors = function (types, elementId, jsPlumbInstance) { + var r = []; + for (var i = 0, ii = types.length; i < ii; i++) { + if (typeof types[i] === "string") { + r.push(root.jsPlumb.Anchors[types[i]]({elementId: elementId, jsPlumbInstance: jsPlumbInstance})); + } + else if (_ju.isArray(types[i])) { + r.push(_currentInstance.makeAnchor(types[i], elementId, jsPlumbInstance)); + } + } + return r; + }; + + /** + * Makes a dynamic anchor from the given list of anchors (which may be in shorthand notation as strings or dimension arrays, or Anchor + * objects themselves) and the given, optional, anchorSelector function (jsPlumb uses a default if this is not provided; most people will + * not need to provide this - i think). + */ + this.makeDynamicAnchor = function (anchors, anchorSelector) { + return new root.jsPlumb.DynamicAnchor({anchors: anchors, selector: anchorSelector, elementId: null, jsPlumbInstance: _currentInstance}); + }; + +// --------------------- makeSource/makeTarget ---------------------------------------------- + + this.targetEndpointDefinitions = {}; + this.sourceEndpointDefinitions = {}; + + var selectorFilter = function (evt, _el, selector, _instance, negate) { + var t = evt.target || evt.srcElement, ok = false, + sel = _instance.getSelector(_el, selector); + for (var j = 0; j < sel.length; j++) { + if (sel[j] === t) { + ok = true; + break; + } + } + return negate ? !ok : ok; + }; + + var _makeElementDropHandler = function (elInfo, p, dropOptions, isSource, isTarget) { + var proxyComponent = new jsPlumbUIComponent(p); + var _drop = p._jsPlumb.EndpointDropHandler({ + jsPlumb: _currentInstance, + enabled: function () { + return elInfo.def.enabled; + }, + isFull: function () { + var targetCount = _currentInstance.select({target: elInfo.id}).length; + return elInfo.def.maxConnections > 0 && targetCount >= elInfo.def.maxConnections; + }, + element: elInfo.el, + elementId: elInfo.id, + isSource: isSource, + isTarget: isTarget, + addClass: function (clazz) { + _currentInstance.addClass(elInfo.el, clazz); + }, + removeClass: function (clazz) { + _currentInstance.removeClass(elInfo.el, clazz); + }, + onDrop: function (jpc) { + var source = jpc.endpoints[0]; + source.anchor.unlock(); + }, + isDropAllowed: function () { + return proxyComponent.isDropAllowed.apply(proxyComponent, arguments); + }, + isRedrop:function(jpc) { + return (jpc.suspendedElement != null && jpc.suspendedEndpoint != null && jpc.suspendedEndpoint.element === elInfo.el); + }, + getEndpoint: function (jpc) { + + // make a new Endpoint for the target, or get it from the cache if uniqueEndpoint + // is set. if its a redrop the new endpoint will be immediately cleaned up. + + var newEndpoint = elInfo.def.endpoint; + + // if no cached endpoint, or there was one but it has been cleaned up + // (ie. detached), create a new one + if (newEndpoint == null || newEndpoint._jsPlumb == null) { + var eps = _currentInstance.deriveEndpointAndAnchorSpec(jpc.getType().join(" "), true); + var pp = eps.endpoints ? root.jsPlumb.extend(p, { + endpoint:elInfo.def.def.endpoint || eps.endpoints[1] + }) :p; + if (eps.anchors) { + pp = root.jsPlumb.extend(pp, { + anchor:elInfo.def.def.anchor || eps.anchors[1] + }); + } + newEndpoint = _currentInstance.addEndpoint(elInfo.el, pp); + newEndpoint._mtNew = true; + } + + if (p.uniqueEndpoint) { + elInfo.def.endpoint = newEndpoint; + } + + newEndpoint.setDeleteOnEmpty(true); + + // if connection is detachable, init the new endpoint to be draggable, to support that happening. + if (jpc.isDetachable()) { + newEndpoint.initDraggable(); + } + + // if the anchor has a 'positionFinder' set, then delegate to that function to find + // out where to locate the anchor. + if (newEndpoint.anchor.positionFinder != null) { + var dropPosition = _currentInstance.getUIPosition(arguments, _currentInstance.getZoom()), + elPosition = _currentInstance.getOffset(elInfo.el), + elSize = _currentInstance.getSize(elInfo.el), + ap = dropPosition == null ? [0,0] : newEndpoint.anchor.positionFinder(dropPosition, elPosition, elSize, newEndpoint.anchor.constructorParams); + + newEndpoint.anchor.x = ap[0]; + newEndpoint.anchor.y = ap[1]; + // now figure an orientation for it..kind of hard to know what to do actually. probably the best thing i can do is to + // support specifying an orientation in the anchor's spec. if one is not supplied then i will make the orientation + // be what will cause the most natural link to the source: it will be pointing at the source, but it needs to be + // specified in one axis only, and so how to make that choice? i think i will use whichever axis is the one in which + // the target is furthest away from the source. + } + + return newEndpoint; + }, + maybeCleanup: function (ep) { + if (ep._mtNew && ep.connections.length === 0) { + _currentInstance.deleteObject({endpoint: ep}); + } + else { + delete ep._mtNew; + } + } + }); + + // wrap drop events as needed and initialise droppable + var dropEvent = root.jsPlumb.dragEvents.drop; + dropOptions.scope = dropOptions.scope || (p.scope || _currentInstance.Defaults.Scope); + dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], _drop, true); + dropOptions.rank = p.rank || 0; + + // if target, return true from the over event. this will cause katavorio to stop setting drops to hover + // if multipleDrop is set to false. + if (isTarget) { + dropOptions[root.jsPlumb.dragEvents.over] = function () { return true; }; + } + + // vanilla jsplumb only + if (p.allowLoopback === false) { + dropOptions.canDrop = function (_drag) { + var de = _drag.getDragElement()._jsPlumbRelatedElement; + return de !== elInfo.el; + }; + } + _currentInstance.initDroppable(elInfo.el, dropOptions, "internal"); + + return _drop; + + }; + + // see API docs + this.makeTarget = function (el, params, referenceParams) { + + // put jsplumb ref into params without altering the params passed in + var p = root.jsPlumb.extend({_jsPlumb: this}, referenceParams); + root.jsPlumb.extend(p, params); + + var maxConnections = p.maxConnections || -1, + + _doOne = function (el) { + + // get the element's id and store the endpoint definition for it. jsPlumb.connect calls will look for one of these, + // and use the endpoint definition if found. + // decode the info for this element (id and element) + var elInfo = _info(el), + elid = elInfo.id, + dropOptions = root.jsPlumb.extend({}, p.dropOptions || {}), + type = p.connectionType || "default"; + + this.targetEndpointDefinitions[elid] = this.targetEndpointDefinitions[elid] || {}; + + _ensureContainer(elid); + + // if this is a group and the user has not mandated a rank, set to -1 so that Nodes takes + // precedence. + if (elInfo.el._isJsPlumbGroup && dropOptions.rank == null) { + dropOptions.rank = -1; + } + + // store the definition + var _def = { + def: root.jsPlumb.extend({}, p), + uniqueEndpoint: p.uniqueEndpoint, + maxConnections: maxConnections, + enabled: true + }; + + if (p.createEndpoint) { + _def.uniqueEndpoint = true; + _def.endpoint = _currentInstance.addEndpoint(el, _def.def); + _def.endpoint.setDeleteOnEmpty(false); + } + + elInfo.def = _def; + this.targetEndpointDefinitions[elid][type] = _def; + _makeElementDropHandler(elInfo, p, dropOptions, p.isSource === true, true); + // stash the definition on the drop + elInfo.el._katavorioDrop[elInfo.el._katavorioDrop.length - 1].targetDef = _def; + + }.bind(this); + + // make an array if only given one element + var inputs = el.length && el.constructor !== String ? el : [ el ]; + + // register each one in the list. + for (var i = 0, ii = inputs.length; i < ii; i++) { + _doOne(inputs[i]); + } + + return this; + }; + + // see api docs + this.unmakeTarget = function (el, doNotClearArrays) { + var info = _info(el); + _currentInstance.destroyDroppable(info.el, "internal"); + if (!doNotClearArrays) { + delete this.targetEndpointDefinitions[info.id]; + } + + return this; + }; + + // see api docs + this.makeSource = function (el, params, referenceParams) { + var p = root.jsPlumb.extend({_jsPlumb: this}, referenceParams); + root.jsPlumb.extend(p, params); + var type = p.connectionType || "default"; + var aae = _currentInstance.deriveEndpointAndAnchorSpec(type); + p.endpoint = p.endpoint || aae.endpoints[0]; + p.anchor = p.anchor || aae.anchors[0]; + var maxConnections = p.maxConnections || -1, + onMaxConnections = p.onMaxConnections, + _doOne = function (elInfo) { + // get the element's id and store the endpoint definition for it. jsPlumb.connect calls will look for one of these, + // and use the endpoint definition if found. + var elid = elInfo.id, + _del = this.getElement(elInfo.el); + + this.sourceEndpointDefinitions[elid] = this.sourceEndpointDefinitions[elid] || {}; + _ensureContainer(elid); + + var _def = { + def:root.jsPlumb.extend({}, p), + uniqueEndpoint: p.uniqueEndpoint, + maxConnections: maxConnections, + enabled: true + }; + + if (p.createEndpoint) { + _def.uniqueEndpoint = true; + _def.endpoint = _currentInstance.addEndpoint(el, _def.def); + _def.endpoint.setDeleteOnEmpty(false); + } + + this.sourceEndpointDefinitions[elid][type] = _def; + elInfo.def = _def; + + var stopEvent = root.jsPlumb.dragEvents.stop, + dragEvent = root.jsPlumb.dragEvents.drag, + dragOptions = root.jsPlumb.extend({ }, p.dragOptions || {}), + existingDrag = dragOptions.drag, + existingStop = dragOptions.stop, + ep = null, + endpointAddedButNoDragYet = false; + + // set scope if its not set in dragOptions but was passed in in params + dragOptions.scope = dragOptions.scope || p.scope; + + dragOptions[dragEvent] = _ju.wrap(dragOptions[dragEvent], function () { + if (existingDrag) { + existingDrag.apply(this, arguments); + } + endpointAddedButNoDragYet = false; + }); + + dragOptions[stopEvent] = _ju.wrap(dragOptions[stopEvent], function () { + + if (existingStop) { + existingStop.apply(this, arguments); + } + this.currentlyDragging = false; + if (ep._jsPlumb != null) { // if not cleaned up... + + // reset the anchor to the anchor that was initially provided. the one we were using to drag + // the connection was just a placeholder that was located at the place the user pressed the + // mouse button to initiate the drag. + var anchorDef = p.anchor || this.Defaults.Anchor, + oldAnchor = ep.anchor, + oldConnection = ep.connections[0]; + + var newAnchor = this.makeAnchor(anchorDef, elid, this), + _el = ep.element; + + // if the anchor has a 'positionFinder' set, then delegate to that function to find + // out where to locate the anchor. issue 117. + if (newAnchor.positionFinder != null) { + var elPosition = _currentInstance.getOffset(_el), + elSize = this.getSize(_el), + dropPosition = { left: elPosition.left + (oldAnchor.x * elSize[0]), top: elPosition.top + (oldAnchor.y * elSize[1]) }, + ap = newAnchor.positionFinder(dropPosition, elPosition, elSize, newAnchor.constructorParams); + + newAnchor.x = ap[0]; + newAnchor.y = ap[1]; + } + + ep.setAnchor(newAnchor, true); + ep.repaint(); + this.repaint(ep.elementId); + if (oldConnection != null) { + this.repaint(oldConnection.targetId); + } + } + }.bind(this)); + + // when the user presses the mouse, add an Endpoint, if we are enabled. + var mouseDownListener = function (e) { + // on right mouse button, abort. + if (e.which === 3 || e.button === 2) { + return; + } + + // TODO store def on element. + var def = this.sourceEndpointDefinitions[elid][type]; + + // if disabled, return. + if (!def.enabled) { + return; + } + + elid = this.getId(this.getElement(elInfo.el)); // elid might have changed since this method was called to configure the element. + + // if a filter was given, run it, and return if it says no. + if (p.filter) { + var r = _ju.isString(p.filter) ? selectorFilter(e, elInfo.el, p.filter, this, p.filterExclude) : p.filter(e, elInfo.el); + if (r === false) { + return; + } + } + + // if maxConnections reached + var sourceCount = this.select({source: elid}).length; + if (def.maxConnections >= 0 && (sourceCount >= def.maxConnections)) { + if (onMaxConnections) { + onMaxConnections({ + element: elInfo.el, + maxConnections: maxConnections + }, e); + } + return false; + } + + // find the position on the element at which the mouse was pressed; this is where the endpoint + // will be located. + var elxy = root.jsPlumb.getPositionOnElement(e, _del, _zoom); + + // we need to override the anchor in here, and force 'isSource', but we don't want to mess with + // the params passed in, because after a connection is established we're going to reset the endpoint + // to have the anchor we were given. + var tempEndpointParams = {}; + root.jsPlumb.extend(tempEndpointParams, def.def); + tempEndpointParams.isTemporarySource = true; + tempEndpointParams.anchor = [ elxy[0], elxy[1] , 0, 0]; + tempEndpointParams.dragOptions = dragOptions; + + if (def.def.scope) { + tempEndpointParams.scope = def.def.scope; + } + + ep = this.addEndpoint(elid, tempEndpointParams); + endpointAddedButNoDragYet = true; + ep.setDeleteOnEmpty(true); + + // if unique endpoint and it's already been created, push it onto the endpoint we create. at the end + // of a successful connection we'll switch to that endpoint. + // TODO this is the same code as the programmatic endpoints create on line 1050 ish + if (def.uniqueEndpoint) { + if (!def.endpoint) { + def.endpoint = ep; + ep.setDeleteOnEmpty(false); + } + else { + ep.finalEndpoint = def.endpoint; + } + } + + var _delTempEndpoint = function () { + // this mouseup event is fired only if no dragging occurred, by jquery and yui, but for mootools + // it is fired even if dragging has occurred, in which case we would blow away a perfectly + // legitimate endpoint, were it not for this check. the flag is set after adding an + // endpoint and cleared in a drag listener we set in the dragOptions above. + _currentInstance.off(ep.canvas, "mouseup", _delTempEndpoint); + _currentInstance.off(elInfo.el, "mouseup", _delTempEndpoint); + if (endpointAddedButNoDragYet) { + endpointAddedButNoDragYet = false; + _currentInstance.deleteEndpoint(ep); + } + }; + + _currentInstance.on(ep.canvas, "mouseup", _delTempEndpoint); + _currentInstance.on(elInfo.el, "mouseup", _delTempEndpoint); + + // optionally check for attributes to extract from the source element + var payload = {}; + if (def.def.extract) { + for (var att in def.def.extract) { + var v = (e.srcElement || e.target).getAttribute(att); + if (v) { + payload[def.def.extract[att]] = v; + } + } + } + + // and then trigger its mousedown event, which will kick off a drag, which will start dragging + // a new connection from this endpoint. + _currentInstance.trigger(ep.canvas, "mousedown", e, payload); + + _ju.consume(e); + + }.bind(this); + + this.on(elInfo.el, "mousedown", mouseDownListener); + _def.trigger = mouseDownListener; + + // if a filter was provided, set it as a dragFilter on the element, + // to prevent the element drag function from kicking in when we want to + // drag a new connection + if (p.filter && (_ju.isString(p.filter) || _ju.isFunction(p.filter))) { + _currentInstance.setDragFilter(elInfo.el, p.filter); + } + + var dropOptions = root.jsPlumb.extend({}, p.dropOptions || {}); + + _makeElementDropHandler(elInfo, p, dropOptions, true, p.isTarget === true); + + }.bind(this); + + var inputs = el.length && el.constructor !== String ? el : [ el ]; + for (var i = 0, ii = inputs.length; i < ii; i++) { + _doOne(_info(inputs[i])); + } + + return this; + }; + + // see api docs + this.unmakeSource = function (el, connectionType, doNotClearArrays) { + var info = _info(el); + _currentInstance.destroyDroppable(info.el, "internal"); + var eldefs = this.sourceEndpointDefinitions[info.id]; + if (eldefs) { + for (var def in eldefs) { + if (connectionType == null || connectionType === def) { + var mouseDownListener = eldefs[def].trigger; + if (mouseDownListener) { + _currentInstance.off(info.el, "mousedown", mouseDownListener); + } + if (!doNotClearArrays) { + delete this.sourceEndpointDefinitions[info.id][def]; + } + } + } + } + + return this; + }; + + // see api docs + this.unmakeEverySource = function () { + for (var i in this.sourceEndpointDefinitions) { + _currentInstance.unmakeSource(i, null, true); + } + + this.sourceEndpointDefinitions = {}; + return this; + }; + + var _getScope = function (el, types, connectionType) { + types = _ju.isArray(types) ? types : [ types ]; + var id = _getId(el); + connectionType = connectionType || "default"; + for (var i = 0; i < types.length; i++) { + var eldefs = this[types[i]][id]; + if (eldefs && eldefs[connectionType]) { + return eldefs[connectionType].def.scope || this.Defaults.Scope; + } + } + }.bind(this); + + var _setScope = function (el, scope, types, connectionType) { + types = _ju.isArray(types) ? types : [ types ]; + var id = _getId(el); + connectionType = connectionType || "default"; + for (var i = 0; i < types.length; i++) { + var eldefs = this[types[i]][id]; + if (eldefs && eldefs[connectionType]) { + eldefs[connectionType].def.scope = scope; + } + } + + }.bind(this); + + this.getScope = function (el, scope) { + return _getScope(el, [ "sourceEndpointDefinitions", "targetEndpointDefinitions" ]); + }; + this.getSourceScope = function (el) { + return _getScope(el, "sourceEndpointDefinitions"); + }; + this.getTargetScope = function (el) { + return _getScope(el, "targetEndpointDefinitions"); + }; + this.setScope = function (el, scope, connectionType) { + this.setSourceScope(el, scope, connectionType); + this.setTargetScope(el, scope, connectionType); + }; + this.setSourceScope = function (el, scope, connectionType) { + _setScope(el, scope, "sourceEndpointDefinitions", connectionType); + // we get the source scope during the mousedown event, but we also want to set this. + this.setDragScope(el, scope); + }; + this.setTargetScope = function (el, scope, connectionType) { + _setScope(el, scope, "targetEndpointDefinitions", connectionType); + this.setDropScope(el, scope); + }; + + // see api docs + this.unmakeEveryTarget = function () { + for (var i in this.targetEndpointDefinitions) { + _currentInstance.unmakeTarget(i, true); + } + + this.targetEndpointDefinitions = {}; + return this; + }; + + // does the work of setting a source enabled or disabled. + var _setEnabled = function (type, el, state, toggle, connectionType) { + var a = type === "source" ? this.sourceEndpointDefinitions : this.targetEndpointDefinitions, + originalState, info, newState; + + connectionType = connectionType || "default"; + + // a selector or an array + if (el.length && !_ju.isString(el)) { + originalState = []; + for (var i = 0, ii = el.length; i < ii; i++) { + info = _info(el[i]); + if (a[info.id] && a[info.id][connectionType]) { + originalState[i] = a[info.id][connectionType].enabled; + newState = toggle ? !originalState[i] : state; + a[info.id][connectionType].enabled = newState; + _currentInstance[newState ? "removeClass" : "addClass"](info.el, "jtk-" + type + "-disabled"); + } + } + } + // otherwise a DOM element or a String ID. + else { + info = _info(el); + var id = info.id; + if (a[id] && a[id][connectionType]) { + originalState = a[id][connectionType].enabled; + newState = toggle ? !originalState : state; + a[id][connectionType].enabled = newState; + _currentInstance[newState ? "removeClass" : "addClass"](info.el, "jtk-" + type + "-disabled"); + } + } + return originalState; + }.bind(this); + + var _first = function (el, fn) { + if (_ju.isString(el) || !el.length) { + return fn.apply(this, [ el ]); + } + else if (el.length) { + return fn.apply(this, [ el[0] ]); + } + + }.bind(this); + + this.toggleSourceEnabled = function (el, connectionType) { + _setEnabled("source", el, null, true, connectionType); + return this.isSourceEnabled(el, connectionType); + }; + + this.setSourceEnabled = function (el, state, connectionType) { + return _setEnabled("source", el, state, null, connectionType); + }; + this.isSource = function (el, connectionType) { + connectionType = connectionType || "default"; + return _first(el, function (_el) { + var eldefs = this.sourceEndpointDefinitions[_info(_el).id]; + return eldefs != null && eldefs[connectionType] != null; + }.bind(this)); + }; + this.isSourceEnabled = function (el, connectionType) { + connectionType = connectionType || "default"; + return _first(el, function (_el) { + var sep = this.sourceEndpointDefinitions[_info(_el).id]; + return sep && sep[connectionType] && sep[connectionType].enabled === true; + }.bind(this)); + }; + + this.toggleTargetEnabled = function (el, connectionType) { + _setEnabled("target", el, null, true, connectionType); + return this.isTargetEnabled(el, connectionType); + }; + + this.isTarget = function (el, connectionType) { + connectionType = connectionType || "default"; + return _first(el, function (_el) { + var eldefs = this.targetEndpointDefinitions[_info(_el).id]; + return eldefs != null && eldefs[connectionType] != null; + }.bind(this)); + }; + this.isTargetEnabled = function (el, connectionType) { + connectionType = connectionType || "default"; + return _first(el, function (_el) { + var tep = this.targetEndpointDefinitions[_info(_el).id]; + return tep && tep[connectionType] && tep[connectionType].enabled === true; + }.bind(this)); + }; + this.setTargetEnabled = function (el, state, connectionType) { + return _setEnabled("target", el, state, null, connectionType); + }; + +// --------------------- end makeSource/makeTarget ---------------------------------------------- + + this.ready = function (fn) { + _currentInstance.bind("ready", fn); + }; + + var _elEach = function(el, fn) { + // support both lists... + if (typeof el === 'object' && el.length) { + for (var i = 0, ii = el.length; i < ii; i++) { + fn(el[i]); + } + } + else {// ...and single strings or elements. + fn(el); + } + + return _currentInstance; + }; + + // repaint some element's endpoints and connections + this.repaint = function (el, ui, timestamp) { + return _elEach(el, function(_el) { + _draw(_el, ui, timestamp); + }); + }; + + this.revalidate = function (el, timestamp, isIdAlready) { + return _elEach(el, function(_el) { + var elId = isIdAlready ? _el : _currentInstance.getId(_el); + _currentInstance.updateOffset({ elId: elId, recalc: true, timestamp:timestamp }); + var dm = _currentInstance.getDragManager(); + if (dm) { + dm.updateOffsets(elId); + } + _currentInstance.repaint(_el); + }); + }; + + // repaint every endpoint and connection. + this.repaintEverything = function () { + // TODO this timestamp causes continuous anchors to not repaint properly. + // fix this. do not just take out the timestamp. it runs a lot faster with + // the timestamp included. + var timestamp = _timestamp(), elId; + + for (elId in endpointsByElement) { + _currentInstance.updateOffset({ elId: elId, recalc: true, timestamp: timestamp }); + } + + for (elId in endpointsByElement) { + _draw(elId, null, timestamp); + } + + return this; + }; + + this.removeAllEndpoints = function (el, recurse, affectedElements) { + affectedElements = affectedElements || []; + var _one = function (_el) { + var info = _info(_el), + ebe = endpointsByElement[info.id], + i, ii; + + if (ebe) { + affectedElements.push(info); + for (i = 0, ii = ebe.length; i < ii; i++) { + _currentInstance.deleteEndpoint(ebe[i], false); + } + } + delete endpointsByElement[info.id]; + + if (recurse) { + if (info.el && info.el.nodeType !== 3 && info.el.nodeType !== 8) { + for (i = 0, ii = info.el.childNodes.length; i < ii; i++) { + _one(info.el.childNodes[i]); + } + } + } + + }; + _one(el); + return this; + }; + + var _doRemove = function(info, affectedElements) { + _currentInstance.removeAllEndpoints(info.id, true, affectedElements); + var dm = _currentInstance.getDragManager(); + var _one = function(_info) { + + if (dm) { + dm.elementRemoved(_info.id); + } + _currentInstance.anchorManager.clearFor(_info.id); + _currentInstance.anchorManager.removeFloatingConnection(_info.id); + + if (_currentInstance.isSource(_info.el)) { + _currentInstance.unmakeSource(_info.el); + } + if (_currentInstance.isTarget(_info.el)) { + _currentInstance.unmakeTarget(_info.el); + } + _currentInstance.destroyDraggable(_info.el); + _currentInstance.destroyDroppable(_info.el); + + + delete _currentInstance.floatingConnections[_info.id]; + delete managedElements[_info.id]; + delete offsets[_info.id]; + if (_info.el) { + _currentInstance.removeElement(_info.el); + _info.el._jsPlumb = null; + } + }; + + // remove all affected child elements + for (var ae = 1; ae < affectedElements.length; ae++) { + _one(affectedElements[ae]); + } + // and always remove the requested one from the dom. + _one(info); + }; + + /** + * Remove the given element, including cleaning up all endpoints registered for it. + * This is exposed in the public API but also used internally by jsPlumb when removing the + * element associated with a connection drag. + */ + this.remove = function (el, doNotRepaint) { + var info = _info(el), affectedElements = []; + if (info.text && info.el.parentNode) { + info.el.parentNode.removeChild(info.el); + } + else if (info.id) { + _currentInstance.batch(function () { + _doRemove(info, affectedElements); + }, doNotRepaint === true); + } + return _currentInstance; + }; + + this.empty = function (el, doNotRepaint) { + var affectedElements = []; + var _one = function(el, dontRemoveFocus) { + var info = _info(el); + if (info.text) { + info.el.parentNode.removeChild(info.el); + } + else if (info.el) { + while(info.el.childNodes.length > 0) { + _one(info.el.childNodes[0]); + } + if (!dontRemoveFocus) { + _doRemove(info, affectedElements); + } + } + }; + + _currentInstance.batch(function() { + _one(el, true); + }, doNotRepaint === false); + + return _currentInstance; + }; + + this.reset = function (doNotUnbindInstanceEventListeners) { + _currentInstance.silently(function() { + _hoverSuspended = false; + _currentInstance.removeAllGroups(); + _currentInstance.removeGroupManager(); + _currentInstance.deleteEveryEndpoint(); + if (!doNotUnbindInstanceEventListeners) { + _currentInstance.unbind(); + } + this.targetEndpointDefinitions = {}; + this.sourceEndpointDefinitions = {}; + connections.length = 0; + if (this.doReset) { + this.doReset(); + } + }.bind(this)); + }; + + var _clearObject = function (obj) { + if (obj.canvas && obj.canvas.parentNode) { + obj.canvas.parentNode.removeChild(obj.canvas); + } + obj.cleanup(); + obj.destroy(); + }; + + this.clear = function () { + _currentInstance.select().each(_clearObject); + _currentInstance.selectEndpoints().each(_clearObject); + + endpointsByElement = {}; + endpointsByUUID = {}; + }; + + this.setDefaultScope = function (scope) { + DEFAULT_SCOPE = scope; + return _currentInstance; + }; + + this.deriveEndpointAndAnchorSpec = function(type, dontPrependDefault) { + var bits = ((dontPrependDefault ? "" : "default ") + type).split(/[\s]/), eps = null, ep = null, a = null, as = null; + for (var i = 0; i < bits.length; i++) { + var _t = _currentInstance.getType(bits[i], "connection"); + if (_t) { + if (_t.endpoints) { + eps = _t.endpoints; + } + if (_t.endpoint) { + ep = _t.endpoint; + } + if (_t.anchors) { + as = _t.anchors; + } + if (_t.anchor) { + a = _t.anchor; + } + } + } + return { endpoints: eps ? eps : [ ep, ep ], anchors: as ? as : [a, a ]}; + }; + + // sets the id of some element, changing whatever we need to to keep track. + this.setId = function (el, newId, doNotSetAttribute) { + // + var id; + + if (_ju.isString(el)) { + id = el; + } + else { + el = this.getElement(el); + id = this.getId(el); + } + + var sConns = this.getConnections({source: id, scope: '*'}, true), + tConns = this.getConnections({target: id, scope: '*'}, true); + + newId = "" + newId; + + if (!doNotSetAttribute) { + el = this.getElement(id); + this.setAttribute(el, "id", newId); + } + else { + el = this.getElement(newId); + } + + endpointsByElement[newId] = endpointsByElement[id] || []; + for (var i = 0, ii = endpointsByElement[newId].length; i < ii; i++) { + endpointsByElement[newId][i].setElementId(newId); + endpointsByElement[newId][i].setReferenceElement(el); + } + delete endpointsByElement[id]; + + this.sourceEndpointDefinitions[newId] = this.sourceEndpointDefinitions[id]; + delete this.sourceEndpointDefinitions[id]; + this.targetEndpointDefinitions[newId] = this.targetEndpointDefinitions[id]; + delete this.targetEndpointDefinitions[id]; + + this.anchorManager.changeId(id, newId); + var dm = this.getDragManager(); + if (dm) { + dm.changeId(id, newId); + } + managedElements[newId] = managedElements[id]; + delete managedElements[id]; + + var _conns = function (list, epIdx, type) { + for (var i = 0, ii = list.length; i < ii; i++) { + list[i].endpoints[epIdx].setElementId(newId); + list[i].endpoints[epIdx].setReferenceElement(el); + list[i][type + "Id"] = newId; + list[i][type] = el; + } + }; + _conns(sConns, 0, "source"); + _conns(tConns, 1, "target"); + + this.repaint(newId); + }; + + this.setDebugLog = function (debugLog) { + log = debugLog; + }; + + this.setSuspendDrawing = function (val, repaintAfterwards) { + var curVal = _suspendDrawing; + _suspendDrawing = val; + if (val) { + _suspendedAt = new Date().getTime(); + } else { + _suspendedAt = null; + } + if (repaintAfterwards) { + this.repaintEverything(); + } + return curVal; + }; + + // returns whether or not drawing is currently suspended. + this.isSuspendDrawing = function () { + return _suspendDrawing; + }; + + // return timestamp for when drawing was suspended. + this.getSuspendedAt = function () { + return _suspendedAt; + }; + + this.batch = function (fn, doNotRepaintAfterwards) { + var _wasSuspended = this.isSuspendDrawing(); + if (!_wasSuspended) { + this.setSuspendDrawing(true); + } + try { + fn(); + } + catch (e) { + _ju.log("Function run while suspended failed", e); + } + if (!_wasSuspended) { + this.setSuspendDrawing(false, !doNotRepaintAfterwards); + } + }; + + this.doWhileSuspended = this.batch; + + this.getCachedData = _getCachedData; + this.timestamp = _timestamp; + this.show = function (el, changeEndpoints) { + _setVisible(el, "block", changeEndpoints); + return _currentInstance; + }; + + // TODO: update this method to return the current state. + this.toggleVisible = _toggleVisible; + this.addListener = this.bind; + + var floatingConnections = []; + this.registerFloatingConnection = function(info, conn, ep) { + floatingConnections[info.id] = conn; + // only register for the target endpoint; we will not be dragging the source at any time + // before this connection is either discarded or made into a permanent connection. + _ju.addToList(endpointsByElement, info.id, ep); + }; + this.getFloatingConnectionFor = function(id) { + return floatingConnections[id]; + }; + + this.listManager = new root.jsPlumbListManager(this); + }; + + _ju.extend(root.jsPlumbInstance, _ju.EventGenerator, { + setAttribute: function (el, a, v) { + this.setAttribute(el, a, v); + }, + getAttribute: function (el, a) { + return this.getAttribute(root.jsPlumb.getElement(el), a); + }, + convertToFullOverlaySpec: function(spec) { + if (_ju.isString(spec)) { + spec = [ spec, { } ]; + } + spec[1].id = spec[1].id || _ju.uuid(); + return spec; + }, + registerConnectionType: function (id, type) { + this._connectionTypes[id] = root.jsPlumb.extend({}, type); + if (type.overlays) { + var to = {}; + for (var i = 0; i < type.overlays.length; i++) { + // if a string, convert to object representation so that we can store the typeid on it. + // also assign an id. + var fo = this.convertToFullOverlaySpec(type.overlays[i]); + to[fo[1].id] = fo; + } + this._connectionTypes[id].overlays = to; + } + }, + registerConnectionTypes: function (types) { + for (var i in types) { + this.registerConnectionType(i, types[i]); + } + }, + registerEndpointType: function (id, type) { + this._endpointTypes[id] = root.jsPlumb.extend({}, type); + if (type.overlays) { + var to = {}; + for (var i = 0; i < type.overlays.length; i++) { + // if a string, convert to object representation so that we can store the typeid on it. + // also assign an id. + var fo = this.convertToFullOverlaySpec(type.overlays[i]); + to[fo[1].id] = fo; + } + this._endpointTypes[id].overlays = to; + } + }, + registerEndpointTypes: function (types) { + for (var i in types) { + this.registerEndpointType(i, types[i]); + } + }, + getType: function (id, typeDescriptor) { + return typeDescriptor === "connection" ? this._connectionTypes[id] : this._endpointTypes[id]; + }, + setIdChanged: function (oldId, newId) { + this.setId(oldId, newId, true); + }, + // set parent: change the parent for some node and update all the registrations we need to. + setParent: function (el, newParent) { + var _dom = this.getElement(el), + _id = this.getId(_dom), + _pdom = this.getElement(newParent), + _pid = this.getId(_pdom), + dm = this.getDragManager(); + + _dom.parentNode.removeChild(_dom); + _pdom.appendChild(_dom); + if (dm) { + dm.setParent(_dom, _id, _pdom, _pid); + } + }, + extend: function (o1, o2, names) { + var i; + if (names) { + for (i = 0; i < names.length; i++) { + o1[names[i]] = o2[names[i]]; + } + } + else { + for (i in o2) { + o1[i] = o2[i]; + } + } + + return o1; + }, + floatingConnections: {}, + getFloatingAnchorIndex: function (jpc) { + return jpc.endpoints[0].isFloating() ? 0 : jpc.endpoints[1].isFloating() ? 1 : -1; + }, + proxyConnection :function(connection, index, proxyEl, proxyElId, endpointGenerator, anchorGenerator) { + var proxyEp, + originalElementId = connection.endpoints[index].elementId, + originalEndpoint = connection.endpoints[index]; + + connection.proxies = connection.proxies || []; + if(connection.proxies[index]) { + proxyEp = connection.proxies[index].ep; + }else { + proxyEp = this.addEndpoint(proxyEl, { + endpoint:endpointGenerator(connection, index), + anchor:anchorGenerator(connection, index), + parameters:{ + isProxyEndpoint:true + } + }); + } + proxyEp.setDeleteOnEmpty(true); + + // for this index, stash proxy info: the new EP, the original EP. + connection.proxies[index] = { ep:proxyEp, originalEp: originalEndpoint }; + + // and advise the anchor manager + if (index === 0) { + // TODO why are there two differently named methods? Why is there not one method that says "some end of this + // connection changed (you give the index), and here's the new element and element id." + this.anchorManager.sourceChanged(originalElementId, proxyElId, connection, proxyEl); + } + else { + this.anchorManager.updateOtherEndpoint(connection.endpoints[0].elementId, originalElementId, proxyElId, connection); + connection.target = proxyEl; + connection.targetId = proxyElId; + } + + // detach the original EP from the connection. + originalEndpoint.detachFromConnection(connection, null, true); + + // set the proxy as the new ep + proxyEp.connections = [ connection ]; + connection.endpoints[index] = proxyEp; + + originalEndpoint.setVisible(false); + + connection.setVisible(true); + + this.revalidate(proxyEl); + }, + unproxyConnection : function(connection, index, proxyElId) { + // if connection cleaned up, no proxies, or none for this end of the connection, abort. + if (connection._jsPlumb == null || connection.proxies == null || connection.proxies[index] == null) { + return; + } + + var originalElement = connection.proxies[index].originalEp.element, + originalElementId = connection.proxies[index].originalEp.elementId; + + connection.endpoints[index] = connection.proxies[index].originalEp; + // and advise the anchor manager + if (index === 0) { + // TODO why are there two differently named methods? Why is there not one method that says "some end of this + // connection changed (you give the index), and here's the new element and element id." + this.anchorManager.sourceChanged(proxyElId, originalElementId, connection, originalElement); + } + else { + this.anchorManager.updateOtherEndpoint(connection.endpoints[0].elementId, proxyElId, originalElementId, connection); + connection.target = originalElement; + connection.targetId = originalElementId; + } + + // detach the proxy EP from the connection (which will cause it to be removed as we no longer need it) + connection.proxies[index].ep.detachFromConnection(connection, null); + + connection.proxies[index].originalEp.addConnection(connection); + if(connection.isVisible()) { + connection.proxies[index].originalEp.setVisible(true); + } + + // cleanup + delete connection.proxies[index]; + } + }); + +// --------------------- static instance + module registration ------------------------------------------- + +// create static instance and assign to window if window exists. + var jsPlumb = new jsPlumbInstance(); + // register on 'root' (lets us run on server or browser) + root.jsPlumb = jsPlumb; + // add 'getInstance' method to static instance + jsPlumb.getInstance = function (_defaults, overrideFns) { + var j = new jsPlumbInstance(_defaults); + if (overrideFns) { + for (var ovf in overrideFns) { + j[ovf] = overrideFns[ovf]; + } + } + j.init(); + return j; + }; + jsPlumb.each = function (spec, fn) { + if (spec == null) { + return; + } + if (typeof spec === "string") { + fn(jsPlumb.getElement(spec)); + } + else if (spec.length != null) { + for (var i = 0; i < spec.length; i++) { + fn(jsPlumb.getElement(spec[i])); + } + } + else { + fn(spec); + } // assume it's an element. + }; + + // CommonJS + if (typeof exports !== 'undefined') { + exports.jsPlumb = jsPlumb; + } + +// --------------------- end static instance + AMD registration ------------------------------------------- + +}).call(typeof window !== 'undefined' ? window : this); + +/* + * 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +;(function() { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + + // ------------------------------ BEGIN OverlayCapablejsPlumbUIComponent -------------------------------------------- + + var _internalLabelOverlayId = "__label", + // this is a shortcut helper method to let people add a label as + // overlay. + _makeLabelOverlay = function (component, params) { + + var _params = { + cssClass: params.cssClass, + labelStyle: component.labelStyle, + id: _internalLabelOverlayId, + component: component, + _jsPlumb: component._jsPlumb.instance // TODO not necessary, since the instance can be accessed through the component. + }, + mergedParams = _jp.extend(_params, params); + + return new _jp.Overlays[component._jsPlumb.instance.getRenderMode()].Label(mergedParams); + }, + _processOverlay = function (component, o) { + var _newOverlay = null; + if (_ju.isArray(o)) { // this is for the shorthand ["Arrow", { width:50 }] syntax + // there's also a three arg version: + // ["Arrow", { width:50 }, {location:0.7}] + // which merges the 3rd arg into the 2nd. + var type = o[0], + // make a copy of the object so as not to mess up anyone else's reference... + p = _jp.extend({component: component, _jsPlumb: component._jsPlumb.instance}, o[1]); + if (o.length === 3) { + _jp.extend(p, o[2]); + } + _newOverlay = new _jp.Overlays[component._jsPlumb.instance.getRenderMode()][type](p); + } else if (o.constructor === String) { + _newOverlay = new _jp.Overlays[component._jsPlumb.instance.getRenderMode()][o]({component: component, _jsPlumb: component._jsPlumb.instance}); + } else { + _newOverlay = o; + } + + _newOverlay.id = _newOverlay.id || _ju.uuid(); + component.cacheTypeItem("overlay", _newOverlay, _newOverlay.id); + component._jsPlumb.overlays[_newOverlay.id] = _newOverlay; + + return _newOverlay; + }; + + _jp.OverlayCapableJsPlumbUIComponent = function (params) { + + root.jsPlumbUIComponent.apply(this, arguments); + this._jsPlumb.overlays = {}; + this._jsPlumb.overlayPositions = {}; + + if (params.label) { + this.getDefaultType().overlays[_internalLabelOverlayId] = ["Label", { + label: params.label, + location: params.labelLocation || this.defaultLabelLocation || 0.5, + labelStyle: params.labelStyle || this._jsPlumb.instance.Defaults.LabelStyle, + id:_internalLabelOverlayId + }]; + }else if(params.id){ + // 鏂板 label 涓虹┖鏃惰缃殑cssClass鏃犳晥闂锛屽彲浠ヤ娇鐢� emptyLabelStyle: {cssClass: 'emptyFlowLabel'} 杩涜璁剧疆 by_ 钀岀骇灏忚彍楦� + this.getDefaultType().overlays[_internalLabelOverlayId] = ["Label", { + label: params.label, + location: params.labelLocation || this.defaultLabelLocation || 0.5, + labelStyle: params.emptyLabelStyle || this._jsPlumb.instance.Defaults.emptyLabelStyle, + id:_internalLabelOverlayId + }]; + } + + this.setListenerComponent = function (c) { + if (this._jsPlumb) { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i].setListenerComponent(c); + } + } + }; + }; + + _jp.OverlayCapableJsPlumbUIComponent.applyType = function (component, t) { + if (t.overlays) { + // loop through the ones in the type. if already present on the component, + // dont remove or re-add. + var keep = {}, i; + + for (i in t.overlays) { + + var existing = component._jsPlumb.overlays[t.overlays[i][1].id]; + if (existing) { + // maybe update from data, if there were parameterised values for instance. + existing.updateFrom(t.overlays[i][1]); + keep[t.overlays[i][1].id] = true; + } + else { + var c = component.getCachedTypeItem("overlay", t.overlays[i][1].id); + if (c != null) { + c.reattach(component._jsPlumb.instance, component); + c.setVisible(true); + // maybe update from data, if there were parameterised values for instance. + c.updateFrom(t.overlays[i][1]); + component._jsPlumb.overlays[c.id] = c; + } + else { + c = component.addOverlay(t.overlays[i], true); + } + keep[c.id] = true; + } + } + + // now loop through the full overlays and remove those that we dont want to keep + for (i in component._jsPlumb.overlays) { + if (keep[component._jsPlumb.overlays[i].id] == null) { + component.removeOverlay(component._jsPlumb.overlays[i].id, true); // remove overlay but dont clean it up. + // that would remove event listeners etc; overlays are never discarded by the types stuff, they are + // just detached/reattached. + } + } + } + }; + + _ju.extend(_jp.OverlayCapableJsPlumbUIComponent, root.jsPlumbUIComponent, { + + setHover: function (hover, ignoreAttachedElements) { + if (this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i][hover ? "addClass" : "removeClass"](this._jsPlumb.instance.hoverClass); + } + } + }, + addOverlay: function (overlay, doNotRepaint) { + var o = _processOverlay(this, overlay); + + if (this.getData && o.type === "Label" && _ju.isArray(overlay)) { + // + // component data might contain label location - look for it here. + var d = this.getData(), p = overlay[1]; + if (d) { + var locationAttribute = p.labelLocationAttribute || "labelLocation"; + var loc = d ? d[locationAttribute] : null; + + if (loc) { + o.loc = loc; + } + } + } + + if (!doNotRepaint) { + this.repaint(); + } + return o; + }, + getOverlay: function (id) { + return this._jsPlumb.overlays[id]; + }, + getOverlays: function () { + return this._jsPlumb.overlays; + }, + hideOverlay: function (id) { + var o = this.getOverlay(id); + if (o) { + o.hide(); + } + }, + hideOverlays: function () { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i].hide(); + } + }, + showOverlay: function (id) { + var o = this.getOverlay(id); + if (o) { + o.show(); + } + }, + showOverlays: function () { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i].show(); + } + }, + removeAllOverlays: function (doNotRepaint) { + for (var i in this._jsPlumb.overlays) { + if (this._jsPlumb.overlays[i].cleanup) { + this._jsPlumb.overlays[i].cleanup(); + } + } + + this._jsPlumb.overlays = {}; + this._jsPlumb.overlayPositions = null; + this._jsPlumb.overlayPlacements= {}; + if (!doNotRepaint) { + this.repaint(); + } + }, + removeOverlay: function (overlayId, dontCleanup) { + var o = this._jsPlumb.overlays[overlayId]; + if (o) { + o.setVisible(false); + if (!dontCleanup && o.cleanup) { + o.cleanup(); + } + delete this._jsPlumb.overlays[overlayId]; + if (this._jsPlumb.overlayPositions) { + delete this._jsPlumb.overlayPositions[overlayId]; + } + + if (this._jsPlumb.overlayPlacements) { + delete this._jsPlumb.overlayPlacements[overlayId]; + } + } + }, + removeOverlays: function () { + for (var i = 0, j = arguments.length; i < j; i++) { + this.removeOverlay(arguments[i]); + } + }, + moveParent: function (newParent) { + if (this.bgCanvas) { + this.bgCanvas.parentNode.removeChild(this.bgCanvas); + newParent.appendChild(this.bgCanvas); + } + + if (this.canvas && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + newParent.appendChild(this.canvas); + + for (var i in this._jsPlumb.overlays) { + if (this._jsPlumb.overlays[i].isAppendedAtTopLevel) { + var el = this._jsPlumb.overlays[i].getElement(); + el.parentNode.removeChild(el); + newParent.appendChild(el); + } + } + } + }, + getLabel: function () { + var lo = this.getOverlay(_internalLabelOverlayId); + return lo != null ? lo.getLabel() : null; + }, + getLabelOverlay: function () { + return this.getOverlay(_internalLabelOverlayId); + }, + setLabel: function (l) { + var lo = this.getOverlay(_internalLabelOverlayId); + if (!lo) { + var params = l.constructor === String || l.constructor === Function ? { label: l } : l; + lo = _makeLabelOverlay(this, params); + this._jsPlumb.overlays[_internalLabelOverlayId] = lo; + } + else { + if (l.constructor === String || l.constructor === Function) { + lo.setLabel(l); + } + else { + // 淇璁剧疆label涓虹┖鏃跺師鏉ョ殑鍊兼棤娉曟浛鎹紝by_钀岀骇灏忚彍楦� 2020骞�05鏈�08鏃�21:26:15 + if (!l.label) { + l.label = '' + } + lo.setLabel(l.label); + if (l.location) { + lo.setLocation(l.location); + } + } + } + + if (!this._jsPlumb.instance.isSuspendDrawing()) { + this.repaint(); + } + }, + cleanup: function (force) { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i].cleanup(force); + this._jsPlumb.overlays[i].destroy(force); + } + if (force) { + this._jsPlumb.overlays = {}; + this._jsPlumb.overlayPositions = null; + } + }, + setVisible: function (v) { + this[v ? "showOverlays" : "hideOverlays"](); + }, + setAbsoluteOverlayPosition: function (overlay, xy) { + this._jsPlumb.overlayPositions[overlay.id] = xy; + }, + getAbsoluteOverlayPosition: function (overlay) { + return this._jsPlumb.overlayPositions ? this._jsPlumb.overlayPositions[overlay.id] : null; + }, + _clazzManip:function(action, clazz, dontUpdateOverlays) { + if (!dontUpdateOverlays) { + for (var i in this._jsPlumb.overlays) { + this._jsPlumb.overlays[i][action + "Class"](clazz); + } + } + }, + addClass:function(clazz, dontUpdateOverlays) { + this._clazzManip("add", clazz, dontUpdateOverlays); + }, + removeClass:function(clazz, dontUpdateOverlays) { + this._clazzManip("remove", clazz, dontUpdateOverlays); + } + }); + +// ------------------------------ END OverlayCapablejsPlumbUIComponent -------------------------------------------- + +}).call(typeof window !== 'undefined' ? window : this); + +/* + * This file contains the code for Endpoints. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +;(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + + // create the drag handler for a connection + var _makeConnectionDragHandler = function (endpoint, placeholder, _jsPlumb) { + var stopped = false; + return { + drag: function () { + if (stopped) { + stopped = false; + return true; + } + + if (placeholder.element) { + var _ui = _jsPlumb.getUIPosition(arguments, _jsPlumb.getZoom()); + if (_ui != null) { + _jsPlumb.setPosition(placeholder.element, _ui); + } + _jsPlumb.repaint(placeholder.element, _ui); + // always repaint the source endpoint, because only continuous/dynamic anchors cause the endpoint + // to be repainted, so static anchors need to be told (or the endpoint gets dragged around) + endpoint.paint({anchorPoint:endpoint.anchor.getCurrentLocation({element:endpoint})}); + } + }, + stopDrag: function () { + stopped = true; + } + }; + }; + + // creates a placeholder div for dragging purposes, adds it, and pre-computes its offset. + var _makeDraggablePlaceholder = function (placeholder, _jsPlumb, ipco, ips) { + var n = _jsPlumb.createElement("div", { position : "absolute" }); + _jsPlumb.appendElement(n); + var id = _jsPlumb.getId(n); + _jsPlumb.setPosition(n, ipco); + n.style.width = ips[0] + "px"; + n.style.height = ips[1] + "px"; + _jsPlumb.manage(id, n, true); // TRANSIENT MANAGE + // create and assign an id, and initialize the offset. + placeholder.id = id; + placeholder.element = n; + }; + + // create a floating endpoint (for drag connections) + var _makeFloatingEndpoint = function (paintStyle, referenceAnchor, endpoint, referenceCanvas, sourceElement, _jsPlumb, _newEndpoint, scope) { + var floatingAnchor = new _jp.FloatingAnchor({ reference: referenceAnchor, referenceCanvas: referenceCanvas, jsPlumbInstance: _jsPlumb }); + //setting the scope here should not be the way to fix that mootools issue. it should be fixed by not + // adding the floating endpoint as a droppable. that makes more sense anyway! + // TRANSIENT MANAGE + return _newEndpoint({ + paintStyle: paintStyle, + endpoint: endpoint, + anchor: floatingAnchor, + source: sourceElement, + scope: scope + }); + }; + + var typeParameters = [ "connectorStyle", "connectorHoverStyle", "connectorOverlays", + "connector", "connectionType", "connectorClass", "connectorHoverClass" ]; + + // a helper function that tries to find a connection to the given element, and returns it if so. if elementWithPrecedence is null, + // or no connection to it is found, we return the first connection in our list. + var findConnectionToUseForDynamicAnchor = function (ep, elementWithPrecedence) { + var idx = 0; + if (elementWithPrecedence != null) { + for (var i = 0; i < ep.connections.length; i++) { + if (ep.connections[i].sourceId === elementWithPrecedence || ep.connections[i].targetId === elementWithPrecedence) { + idx = i; + break; + } + } + } + + return ep.connections[idx]; + }; + + _jp.Endpoint = function (params) { + var _jsPlumb = params._jsPlumb, + _newConnection = params.newConnection, + _newEndpoint = params.newEndpoint; + + this.idPrefix = "_jsplumb_e_"; + this.defaultLabelLocation = [ 0.5, 0.5 ]; + this.defaultOverlayKeys = ["Overlays", "EndpointOverlays"]; + _jp.OverlayCapableJsPlumbUIComponent.apply(this, arguments); + +// TYPE + + this.appendToDefaultType({ + connectionType:params.connectionType, + maxConnections: params.maxConnections == null ? this._jsPlumb.instance.Defaults.MaxConnections : params.maxConnections, // maximum number of connections this endpoint can be the source of., + paintStyle: params.endpointStyle || params.paintStyle || params.style || this._jsPlumb.instance.Defaults.EndpointStyle || _jp.Defaults.EndpointStyle, + hoverPaintStyle: params.endpointHoverStyle || params.hoverPaintStyle || this._jsPlumb.instance.Defaults.EndpointHoverStyle || _jp.Defaults.EndpointHoverStyle, + connectorStyle: params.connectorStyle, + connectorHoverStyle: params.connectorHoverStyle, + connectorClass: params.connectorClass, + connectorHoverClass: params.connectorHoverClass, + connectorOverlays: params.connectorOverlays, + connector: params.connector, + connectorTooltip: params.connectorTooltip + }); + +// END TYPE + + this._jsPlumb.enabled = !(params.enabled === false); + this._jsPlumb.visible = true; + this.element = _jp.getElement(params.source); + this._jsPlumb.uuid = params.uuid; + this._jsPlumb.floatingEndpoint = null; + var inPlaceCopy = null; + if (this._jsPlumb.uuid) { + params.endpointsByUUID[this._jsPlumb.uuid] = this; + } + this.elementId = params.elementId; + this.dragProxy = params.dragProxy; + + this._jsPlumb.connectionCost = params.connectionCost; + this._jsPlumb.connectionsDirected = params.connectionsDirected; + this._jsPlumb.currentAnchorClass = ""; + this._jsPlumb.events = {}; + + var deleteOnEmpty = params.deleteOnEmpty === true; + this.setDeleteOnEmpty = function(d) { + deleteOnEmpty = d; + }; + + var _updateAnchorClass = function () { + // stash old, get new + var oldAnchorClass = _jsPlumb.endpointAnchorClassPrefix + "-" + this._jsPlumb.currentAnchorClass; + this._jsPlumb.currentAnchorClass = this.anchor.getCssClass(); + var anchorClass = _jsPlumb.endpointAnchorClassPrefix + (this._jsPlumb.currentAnchorClass ? "-" + this._jsPlumb.currentAnchorClass : ""); + + this.removeClass(oldAnchorClass); + this.addClass(anchorClass); + // add and remove at the same time to reduce the number of reflows. + _jp.updateClasses(this.element, anchorClass, oldAnchorClass); + }.bind(this); + + this.prepareAnchor = function(anchorParams) { + var a = this._jsPlumb.instance.makeAnchor(anchorParams, this.elementId, _jsPlumb); + a.bind("anchorChanged", function (currentAnchor) { + this.fire("anchorChanged", {endpoint: this, anchor: currentAnchor}); + _updateAnchorClass(); + }.bind(this)); + return a; + }; + + this.setPreparedAnchor = function(anchor, doNotRepaint) { + this._jsPlumb.instance.continuousAnchorFactory.clear(this.elementId); + this.anchor = anchor; + _updateAnchorClass(); + + if (!doNotRepaint) { + this._jsPlumb.instance.repaint(this.elementId); + } + + return this; + }; + + this.setAnchor = function (anchorParams, doNotRepaint) { + var a = this.prepareAnchor(anchorParams); + this.setPreparedAnchor(a, doNotRepaint); + return this; + }; + + var internalHover = function (state) { + if (this.connections.length > 0) { + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].setHover(state, false); + } + } + else { + this.setHover(state); + } + }.bind(this); + + this.bind("mouseover", function () { + internalHover(true); + }); + this.bind("mouseout", function () { + internalHover(false); + }); + + // ANCHOR MANAGER + if (!params._transient) { // in place copies, for example, are transient. they will never need to be retrieved during a paint cycle, because they dont move, and then they are deleted. + this._jsPlumb.instance.anchorManager.add(this, this.elementId); + } + + this.prepareEndpoint = function(ep, typeId) { + var _e = function (t, p) { + var rm = _jsPlumb.getRenderMode(); + if (_jp.Endpoints[rm][t]) { + return new _jp.Endpoints[rm][t](p); + } + if (!_jsPlumb.Defaults.DoNotThrowErrors) { + throw { msg: "jsPlumb: unknown endpoint type '" + t + "'" }; + } + }; + + var endpointArgs = { + _jsPlumb: this._jsPlumb.instance, + cssClass: params.cssClass, + container: params.container, + tooltip: params.tooltip, + connectorTooltip: params.connectorTooltip, + endpoint: this + }; + + var endpoint; + + if (_ju.isString(ep)) { + endpoint = _e(ep, endpointArgs); + } + else if (_ju.isArray(ep)) { + endpointArgs = _ju.merge(ep[1], endpointArgs); + endpoint = _e(ep[0], endpointArgs); + } + else { + endpoint = ep.clone(); + } + + // assign a clone function using a copy of endpointArgs. this is used when a drag starts: the endpoint that was dragged is cloned, + // and the clone is left in its place while the original one goes off on a magical journey. + // the copy is to get around a closure problem, in which endpointArgs ends up getting shared by + // the whole world. + //var argsForClone = jsPlumb.extend({}, endpointArgs); + endpoint.clone = function () { + // TODO this, and the code above, can be refactored to be more dry. + if (_ju.isString(ep)) { + return _e(ep, endpointArgs); + } + else if (_ju.isArray(ep)) { + endpointArgs = _ju.merge(ep[1], endpointArgs); + return _e(ep[0], endpointArgs); + } + }.bind(this); + + endpoint.typeId = typeId; + return endpoint; + }; + + this.setEndpoint = function(ep, doNotRepaint) { + var _ep = this.prepareEndpoint(ep); + this.setPreparedEndpoint(_ep, true); + }; + + this.setPreparedEndpoint = function (ep, doNotRepaint) { + if (this.endpoint != null) { + this.endpoint.cleanup(); + this.endpoint.destroy(); + } + this.endpoint = ep; + this.type = this.endpoint.type; + this.canvas = this.endpoint.canvas; + }; + + _jp.extend(this, params, typeParameters); + + this.isSource = params.isSource || false; + this.isTemporarySource = params.isTemporarySource || false; + this.isTarget = params.isTarget || false; + + this.connections = params.connections || []; + this.connectorPointerEvents = params["connector-pointer-events"]; + + this.scope = params.scope || _jsPlumb.getDefaultScope(); + this.timestamp = null; + this.reattachConnections = params.reattach || _jsPlumb.Defaults.ReattachConnections; + this.connectionsDetachable = _jsPlumb.Defaults.ConnectionsDetachable; + if (params.connectionsDetachable === false || params.detachable === false) { + this.connectionsDetachable = false; + } + this.dragAllowedWhenFull = params.dragAllowedWhenFull !== false; + + if (params.onMaxConnections) { + this.bind("maxConnections", params.onMaxConnections); + } + + // + // add a connection. not part of public API. + // + this.addConnection = function (connection) { + this.connections.push(connection); + this[(this.connections.length > 0 ? "add" : "remove") + "Class"](_jsPlumb.endpointConnectedClass); + this[(this.isFull() ? "add" : "remove") + "Class"](_jsPlumb.endpointFullClass); + }; + + this.detachFromConnection = function (connection, idx, doNotCleanup) { + idx = idx == null ? this.connections.indexOf(connection) : idx; + if (idx >= 0) { + this.connections.splice(idx, 1); + this[(this.connections.length > 0 ? "add" : "remove") + "Class"](_jsPlumb.endpointConnectedClass); + this[(this.isFull() ? "add" : "remove") + "Class"](_jsPlumb.endpointFullClass); + } + + if (!doNotCleanup && deleteOnEmpty && this.connections.length === 0) { + _jsPlumb.deleteObject({ + endpoint: this, + fireEvent: false, + deleteAttachedObjects: doNotCleanup !== true + }); + } + }; + + this.deleteEveryConnection = function(params) { + var c = this.connections.length; + for (var i = 0; i < c; i++) { + _jsPlumb.deleteConnection(this.connections[0], params); + } + }; + + this.detachFrom = function (targetEndpoint, fireEvent, originalEvent) { + var c = []; + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].endpoints[1] === targetEndpoint || this.connections[i].endpoints[0] === targetEndpoint) { + c.push(this.connections[i]); + } + } + for (var j = 0, count = c.length; j < count; j++) { + _jsPlumb.deleteConnection(c[0]); + } + return this; + }; + + this.getElement = function () { + return this.element; + }; + + this.setElement = function (el) { + var parentId = this._jsPlumb.instance.getId(el), + curId = this.elementId; + // remove the endpoint from the list for the current endpoint's element + _ju.removeWithFunction(params.endpointsByElement[this.elementId], function (e) { + return e.id === this.id; + }.bind(this)); + this.element = _jp.getElement(el); + this.elementId = _jsPlumb.getId(this.element); + _jsPlumb.anchorManager.rehomeEndpoint(this, curId, this.element); + _jsPlumb.dragManager.endpointAdded(this.element); + _ju.addToList(params.endpointsByElement, parentId, this); + return this; + }; + + /** + * private but must be exposed. + */ + this.makeInPlaceCopy = function () { + var loc = this.anchor.getCurrentLocation({element: this}), + o = this.anchor.getOrientation(this), + acc = this.anchor.getCssClass(), + inPlaceAnchor = { + bind: function () { + }, + compute: function () { + return [ loc[0], loc[1] ]; + }, + getCurrentLocation: function () { + return [ loc[0], loc[1] ]; + }, + getOrientation: function () { + return o; + }, + getCssClass: function () { + return acc; + } + }; + + return _newEndpoint({ + dropOptions: params.dropOptions, + anchor: inPlaceAnchor, + source: this.element, + paintStyle: this.getPaintStyle(), + endpoint: params.hideOnDrag ? "Blank" : this.endpoint, + _transient: true, + scope: this.scope, + reference:this + }); + }; + + /** + * returns a connection from the pool; used when dragging starts. just gets the head of the array if it can. + */ + this.connectorSelector = function () { + return this.connections[0]; + }; + + this.setStyle = this.setPaintStyle; + + this.paint = function (params) { + params = params || {}; + var timestamp = params.timestamp, recalc = !(params.recalc === false); + if (!timestamp || this.timestamp !== timestamp) { + + var info = _jsPlumb.updateOffset({ elId: this.elementId, timestamp: timestamp }); + + var xy = params.offset ? params.offset.o : info.o; + if (xy != null) { + var ap = params.anchorPoint, connectorPaintStyle = params.connectorPaintStyle; + if (ap == null) { + var wh = params.dimensions || info.s, + anchorParams = { xy: [ xy.left, xy.top ], wh: wh, element: this, timestamp: timestamp }; + if (recalc && this.anchor.isDynamic && this.connections.length > 0) { + var c = findConnectionToUseForDynamicAnchor(this, params.elementWithPrecedence), + oIdx = c.endpoints[0] === this ? 1 : 0, + oId = oIdx === 0 ? c.sourceId : c.targetId, + oInfo = _jsPlumb.getCachedData(oId), + oOffset = oInfo.o, oWH = oInfo.s; + + anchorParams.index = oIdx === 0 ? 1 : 0; + anchorParams.connection = c; + anchorParams.txy = [ oOffset.left, oOffset.top ]; + anchorParams.twh = oWH; + anchorParams.tElement = c.endpoints[oIdx]; + } else if (this.connections.length > 0) { + anchorParams.connection = this.connections[0]; + } + ap = this.anchor.compute(anchorParams); + } + + this.endpoint.compute(ap, this.anchor.getOrientation(this), this._jsPlumb.paintStyleInUse, connectorPaintStyle || this.paintStyleInUse); + this.endpoint.paint(this._jsPlumb.paintStyleInUse, this.anchor); + this.timestamp = timestamp; + + // paint overlays + for (var i in this._jsPlumb.overlays) { + if (this._jsPlumb.overlays.hasOwnProperty(i)) { + var o = this._jsPlumb.overlays[i]; + if (o.isVisible()) { + this._jsPlumb.overlayPlacements[i] = o.draw(this.endpoint, this._jsPlumb.paintStyleInUse); + o.paint(this._jsPlumb.overlayPlacements[i]); + } + } + } + } + } + }; + + this.getTypeDescriptor = function () { + return "endpoint"; + }; + this.isVisible = function () { + return this._jsPlumb.visible; + }; + + this.repaint = this.paint; + + var draggingInitialised = false; + this.initDraggable = function () { + + // is this a connection source? we make it draggable and have the + // drag listener maintain a connection with a floating endpoint. + if (!draggingInitialised && _jp.isDragSupported(this.element)) { + var placeholderInfo = { id: null, element: null }, + jpc = null, + existingJpc = false, + existingJpcParams = null, + _dragHandler = _makeConnectionDragHandler(this, placeholderInfo, _jsPlumb), + dragOptions = params.dragOptions || {}, + defaultOpts = {}, + startEvent = _jp.dragEvents.start, + stopEvent = _jp.dragEvents.stop, + dragEvent = _jp.dragEvents.drag, + beforeStartEvent = _jp.dragEvents.beforeStart, + payload; + + // respond to beforeStart from katavorio; this will have, optionally, a payload of attribute values + // that were placed there by the makeSource mousedown listener. + var beforeStart = function(beforeStartParams) { + payload = beforeStartParams.e.payload || {}; + }; + + var start = function (startParams) { + +// ------------- first, get a connection to drag. this may be null, in which case we are dragging a new one. + + jpc = this.connectorSelector(); + +// -------------------------------- now a bunch of tests about whether or not to proceed ------------------------- + + var _continue = true; + // if not enabled, return + if (!this.isEnabled()) { + _continue = false; + } + // if no connection and we're not a source - or temporarily a source, as is the case with makeSource - return. + if (jpc == null && !this.isSource && !this.isTemporarySource) { + _continue = false; + } + // otherwise if we're full and not allowed to drag, also return false. + if (this.isSource && this.isFull() && !(jpc != null && this.dragAllowedWhenFull)) { + _continue = false; + } + // if the connection was setup as not detachable or one of its endpoints + // was setup as connectionsDetachable = false, or Defaults.ConnectionsDetachable + // is set to false... + if (jpc != null && !jpc.isDetachable(this)) { + // .. and the endpoint is full + if (this.isFull()) { + _continue = false; + } else { + // otherwise, if not full, set the connection to null, and we will now proceed + // to drag a new connection. + jpc = null; + } + } + + var beforeDrag = _jsPlumb.checkCondition(jpc == null ? "beforeDrag" : "beforeStartDetach", { + endpoint:this, + source:this.element, + sourceId:this.elementId, + connection:jpc + }); + if (beforeDrag === false) { + _continue = false; + } + // else we might have been given some data. we'll pass it in to a new connection as 'data'. + // here we also merge in the optional payload we were given on mousedown. + else if (typeof beforeDrag === "object") { + _jp.extend(beforeDrag, payload || {}); + } + else { + // or if no beforeDrag data, maybe use the payload on its own. + beforeDrag = payload || {}; + } + + if (_continue === false) { + // this is for mootools and yui. returning false from this causes jquery to stop drag. + // the events are wrapped in both mootools and yui anyway, but i don't think returning + // false from the start callback would stop a drag. + if (_jsPlumb.stopDrag) { + _jsPlumb.stopDrag(this.canvas); + } + _dragHandler.stopDrag(); + return false; + } + +// --------------------------------------------------------------------------------------------------------------------- + + // ok to proceed. + + // clear hover for all connections for this endpoint before continuing. + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].setHover(false); + } + + this.addClass("endpointDrag"); + _jsPlumb.setConnectionBeingDragged(true); + + // if we're not full but there was a connection, make it null. we'll create a new one. + if (jpc && !this.isFull() && this.isSource) { + jpc = null; + } + + _jsPlumb.updateOffset({ elId: this.elementId }); + +// ---------------- make the element we will drag around, and position it ----------------------------- + + var ipco = this._jsPlumb.instance.getOffset(this.canvas), + canvasElement = this.canvas, + ips = this._jsPlumb.instance.getSize(this.canvas); + + _makeDraggablePlaceholder(placeholderInfo, _jsPlumb, ipco, ips); + + // store the id of the dragging div and the source element. the drop function will pick these up. + _jsPlumb.setAttributes(this.canvas, { + "dragId": placeholderInfo.id, + "elId": this.elementId + }); + +// ------------------- create an endpoint that will be our floating endpoint ------------------------------------ + + var endpointToFloat = this.dragProxy || this.endpoint; + if (this.dragProxy == null && this.connectionType != null) { + var aae = this._jsPlumb.instance.deriveEndpointAndAnchorSpec(this.connectionType); + if (aae.endpoints[1]) { + endpointToFloat = aae.endpoints[1]; + } + } + var centerAnchor = this._jsPlumb.instance.makeAnchor("Center"); + centerAnchor.isFloating = true; + this._jsPlumb.floatingEndpoint = _makeFloatingEndpoint(this.getPaintStyle(), centerAnchor, endpointToFloat, this.canvas, placeholderInfo.element, _jsPlumb, _newEndpoint, this.scope); + var _savedAnchor = this._jsPlumb.floatingEndpoint.anchor; + + + if (jpc == null) { + + this.setHover(false, false); + // create a connection. one end is this endpoint, the other is a floating endpoint. + jpc = _newConnection({ + sourceEndpoint: this, + targetEndpoint: this._jsPlumb.floatingEndpoint, + source: this.element, // for makeSource with parent option. ensure source element is represented correctly. + target: placeholderInfo.element, + anchors: [ this.anchor, this._jsPlumb.floatingEndpoint.anchor ], + paintStyle: params.connectorStyle, // this can be null. Connection will use the default. + hoverPaintStyle: params.connectorHoverStyle, + connector: params.connector, // this can also be null. Connection will use the default. + overlays: params.connectorOverlays, + type: this.connectionType, + cssClass: this.connectorClass, + hoverClass: this.connectorHoverClass, + scope:params.scope, + data:beforeDrag + }); + jpc.pending = true; + jpc.addClass(_jsPlumb.draggingClass); + this._jsPlumb.floatingEndpoint.addClass(_jsPlumb.draggingClass); + this._jsPlumb.floatingEndpoint.anchor = _savedAnchor; + // fire an event that informs that a connection is being dragged + _jsPlumb.fire("connectionDrag", jpc); + + // register the new connection on the drag manager. This connection, at this point, is 'pending', + // and has as its target a temporary element (the 'placeholder'). If the connection subsequently + // becomes established, the anchor manager is informed that the target of the connection has + // changed. + + _jsPlumb.anchorManager.newConnection(jpc); + + } else { + existingJpc = true; + jpc.setHover(false); + // new anchor idx + var anchorIdx = jpc.endpoints[0].id === this.id ? 0 : 1; + this.detachFromConnection(jpc, null, true); // detach from the connection while dragging is occurring. but dont cleanup automatically. + + // store the original scope (issue 57) + var dragScope = _jsPlumb.getDragScope(canvasElement); + _jsPlumb.setAttribute(this.canvas, "originalScope", dragScope); + + // fire an event that informs that a connection is being dragged. we do this before + // replacing the original target with the floating element info. + _jsPlumb.fire("connectionDrag", jpc); + + // now we replace ourselves with the temporary div we created above: + if (anchorIdx === 0) { + existingJpcParams = [ jpc.source, jpc.sourceId, canvasElement, dragScope ]; + _jsPlumb.anchorManager.sourceChanged(jpc.endpoints[anchorIdx].elementId, placeholderInfo.id, jpc, placeholderInfo.element); + + } else { + existingJpcParams = [ jpc.target, jpc.targetId, canvasElement, dragScope ]; + jpc.target = placeholderInfo.element; + jpc.targetId = placeholderInfo.id; + + _jsPlumb.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.endpoints[anchorIdx].elementId, jpc.targetId, jpc); + } + + // store the original endpoint and assign the new floating endpoint for the drag. + jpc.suspendedEndpoint = jpc.endpoints[anchorIdx]; + + // PROVIDE THE SUSPENDED ELEMENT, BE IT A SOURCE OR TARGET (ISSUE 39) + jpc.suspendedElement = jpc.endpoints[anchorIdx].getElement(); + jpc.suspendedElementId = jpc.endpoints[anchorIdx].elementId; + jpc.suspendedElementType = anchorIdx === 0 ? "source" : "target"; + + jpc.suspendedEndpoint.setHover(false); + this._jsPlumb.floatingEndpoint.referenceEndpoint = jpc.suspendedEndpoint; + jpc.endpoints[anchorIdx] = this._jsPlumb.floatingEndpoint; + + jpc.addClass(_jsPlumb.draggingClass); + this._jsPlumb.floatingEndpoint.addClass(_jsPlumb.draggingClass); + } + + _jsPlumb.registerFloatingConnection(placeholderInfo, jpc, this._jsPlumb.floatingEndpoint); + + // // register it and register connection on it. + // _jsPlumb.floatingConnections[placeholderInfo.id] = jpc; + // + // // only register for the target endpoint; we will not be dragging the source at any time + // // before this connection is either discarded or made into a permanent connection. + // _ju.addToList(params.endpointsByElement, placeholderInfo.id, this._jsPlumb.floatingEndpoint); + + + // tell jsplumb about it + _jsPlumb.currentlyDragging = true; + }.bind(this); + + var stop = function () { + _jsPlumb.setConnectionBeingDragged(false); + + if (jpc && jpc.endpoints != null) { + // get the actual drop event (decode from library args to stop function) + var originalEvent = _jsPlumb.getDropEvent(arguments); + // unlock the other endpoint (if it is dynamic, it would have been locked at drag start) + var idx = _jsPlumb.getFloatingAnchorIndex(jpc); + jpc.endpoints[idx === 0 ? 1 : 0].anchor.unlock(); + // TODO: Dont want to know about css classes inside jsplumb, ideally. + jpc.removeClass(_jsPlumb.draggingClass); + + // if we have the floating endpoint then the connection has not been dropped + // on another endpoint. If it is a new connection we throw it away. If it is an + // existing connection we check to see if we should reattach it, throwing it away + // if not. + if (this._jsPlumb && (jpc.deleteConnectionNow || jpc.endpoints[idx] === this._jsPlumb.floatingEndpoint)) { + // 6a. if the connection was an existing one... + if (existingJpc && jpc.suspendedEndpoint) { + // fix for issue35, thanks Sylvain Gizard: when firing the detach event make sure the + // floating endpoint has been replaced. + if (idx === 0) { + jpc.floatingElement = jpc.source; + jpc.floatingId = jpc.sourceId; + jpc.floatingEndpoint = jpc.endpoints[0]; + jpc.floatingIndex = 0; + jpc.source = existingJpcParams[0]; + jpc.sourceId = existingJpcParams[1]; + } else { + // keep a copy of the floating element; the anchor manager will want to clean up. + jpc.floatingElement = jpc.target; + jpc.floatingId = jpc.targetId; + jpc.floatingEndpoint = jpc.endpoints[1]; + jpc.floatingIndex = 1; + jpc.target = existingJpcParams[0]; + jpc.targetId = existingJpcParams[1]; + } + + var fe = this._jsPlumb.floatingEndpoint; // store for later removal. + // restore the original scope (issue 57) + _jsPlumb.setDragScope(existingJpcParams[2], existingJpcParams[3]); + jpc.endpoints[idx] = jpc.suspendedEndpoint; + // if the connection should be reattached, or the other endpoint refuses detach, then + // reset the connection to its original state + if (jpc.isReattach() || jpc._forceReattach || jpc._forceDetach || !_jsPlumb.deleteConnection(jpc, {originalEvent: originalEvent})) { + + jpc.setHover(false); + jpc._forceDetach = null; + jpc._forceReattach = null; + this._jsPlumb.floatingEndpoint.detachFromConnection(jpc); + jpc.suspendedEndpoint.addConnection(jpc); + + // TODO this code is duplicated in lots of places...and there is nothing external + // in the code; it all refers to the connection itself. we could add a + // `checkSanity(connection)` method to anchorManager that did this. + if (idx === 1) { + _jsPlumb.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.floatingId, jpc.targetId, jpc); + } + else { + _jsPlumb.anchorManager.sourceChanged(jpc.floatingId, jpc.sourceId, jpc, jpc.source); + } + + _jsPlumb.repaint(existingJpcParams[1]); + } + else { + _jsPlumb.deleteObject({endpoint: fe}); + } + } + } + + // makeTargets sets this flag, to tell us we have been replaced and should delete this object. + if (this.deleteAfterDragStop) { + _jsPlumb.deleteObject({endpoint: this}); + } + else { + if (this._jsPlumb) { + this.paint({recalc: false}); + } + } + + // although the connection is no longer valid, there are use cases where this is useful. + _jsPlumb.fire("connectionDragStop", jpc, originalEvent); + // fire this event to give people more fine-grained control (connectionDragStop fires a lot) + if (jpc.pending) { + _jsPlumb.fire("connectionAborted", jpc, originalEvent); + } + // tell jsplumb that dragging is finished. + _jsPlumb.currentlyDragging = false; + jpc.suspendedElement = null; + jpc.suspendedEndpoint = null; + jpc = null; + } + + // if no endpoints, jpc already cleaned up. but still we want to ensure we're reset properly. + // remove the element associated with the floating endpoint + // (and its associated floating endpoint and visual artefacts) + if (placeholderInfo && placeholderInfo.element) { + _jsPlumb.remove(placeholderInfo.element, false, false); + } + // remove the inplace copy + if (inPlaceCopy) { + _jsPlumb.deleteObject({endpoint: inPlaceCopy}); + } + + if (this._jsPlumb) { + // make our canvas visible (TODO: hand off to library; we should not know about DOM) + this.canvas.style.visibility = "visible"; + // unlock our anchor + this.anchor.unlock(); + // clear floating anchor. + this._jsPlumb.floatingEndpoint = null; + } + + }.bind(this); + + dragOptions = _jp.extend(defaultOpts, dragOptions); + dragOptions.scope = this.scope || dragOptions.scope; + dragOptions[beforeStartEvent] = _ju.wrap(dragOptions[beforeStartEvent], beforeStart, false); + dragOptions[startEvent] = _ju.wrap(dragOptions[startEvent], start, false); + // extracted drag handler function so can be used by makeSource + dragOptions[dragEvent] = _ju.wrap(dragOptions[dragEvent], _dragHandler.drag); + dragOptions[stopEvent] = _ju.wrap(dragOptions[stopEvent], stop); + dragOptions.multipleDrop = false; + + dragOptions.canDrag = function () { + return this.isSource || this.isTemporarySource || (this.connections.length > 0 && this.connectionsDetachable !== false); + }.bind(this); + + _jsPlumb.initDraggable(this.canvas, dragOptions, "internal"); + + this.canvas._jsPlumbRelatedElement = this.element; + + draggingInitialised = true; + } + }; + + var ep = params.endpoint || this._jsPlumb.instance.Defaults.Endpoint || _jp.Defaults.Endpoint; + this.setEndpoint(ep, true); + var anchorParamsToUse = params.anchor ? params.anchor : params.anchors ? params.anchors : (_jsPlumb.Defaults.Anchor || "Top"); + this.setAnchor(anchorParamsToUse, true); + + // finally, set type if it was provided + var type = [ "default", (params.type || "")].join(" "); + this.addType(type, params.data, true); + this.canvas = this.endpoint.canvas; + this.canvas._jsPlumb = this; + + this.initDraggable(); + + // pulled this out into a function so we can reuse it for the inPlaceCopy canvas; you can now drop detached connections + // back onto the endpoint you detached it from. + var _initDropTarget = function (canvas, isTransient, endpoint, referenceEndpoint) { + + if (_jp.isDropSupported(this.element)) { + var dropOptions = params.dropOptions || _jsPlumb.Defaults.DropOptions || _jp.Defaults.DropOptions; + dropOptions = _jp.extend({}, dropOptions); + dropOptions.scope = dropOptions.scope || this.scope; + var dropEvent = _jp.dragEvents.drop, + overEvent = _jp.dragEvents.over, + outEvent = _jp.dragEvents.out, + _ep = this, + drop = _jsPlumb.EndpointDropHandler({ + getEndpoint: function () { + return _ep; + }, + jsPlumb: _jsPlumb, + enabled: function () { + return endpoint != null ? endpoint.isEnabled() : true; + }, + isFull: function () { + return endpoint.isFull(); + }, + element: this.element, + elementId: this.elementId, + isSource: this.isSource, + isTarget: this.isTarget, + addClass: function (clazz) { + _ep.addClass(clazz); + }, + removeClass: function (clazz) { + _ep.removeClass(clazz); + }, + isDropAllowed: function () { + return _ep.isDropAllowed.apply(_ep, arguments); + }, + reference:referenceEndpoint, + isRedrop:function(jpc, dhParams) { + return jpc.suspendedEndpoint && dhParams.reference && (jpc.suspendedEndpoint.id === dhParams.reference.id); + } + }); + + dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], drop, true); + dropOptions[overEvent] = _ju.wrap(dropOptions[overEvent], function () { + var draggable = _jp.getDragObject(arguments), + id = _jsPlumb.getAttribute(_jp.getElement(draggable), "dragId"), + _jpc = _jsPlumb.getFloatingConnectionFor(id);//_jsPlumb.floatingConnections[id]; + + if (_jpc != null) { + var idx = _jsPlumb.getFloatingAnchorIndex(_jpc); + // here we should fire the 'over' event if we are a target and this is a new connection, + // or we are the same as the floating endpoint. + var _cont = (this.isTarget && idx !== 0) || (_jpc.suspendedEndpoint && this.referenceEndpoint && this.referenceEndpoint.id === _jpc.suspendedEndpoint.id); + if (_cont) { + var bb = _jsPlumb.checkCondition("checkDropAllowed", { + sourceEndpoint: _jpc.endpoints[idx], + targetEndpoint: this, + connection: _jpc + }); + this[(bb ? "add" : "remove") + "Class"](_jsPlumb.endpointDropAllowedClass); + this[(bb ? "remove" : "add") + "Class"](_jsPlumb.endpointDropForbiddenClass); + _jpc.endpoints[idx].anchor.over(this.anchor, this); + } + } + }.bind(this)); + + dropOptions[outEvent] = _ju.wrap(dropOptions[outEvent], function () { + var draggable = _jp.getDragObject(arguments), + id = draggable == null ? null : _jsPlumb.getAttribute(_jp.getElement(draggable), "dragId"), + _jpc = id ? _jsPlumb.getFloatingConnectionFor(id) : null; + + if (_jpc != null) { + var idx = _jsPlumb.getFloatingAnchorIndex(_jpc); + var _cont = (this.isTarget && idx !== 0) || (_jpc.suspendedEndpoint && this.referenceEndpoint && this.referenceEndpoint.id === _jpc.suspendedEndpoint.id); + if (_cont) { + this.removeClass(_jsPlumb.endpointDropAllowedClass); + this.removeClass(_jsPlumb.endpointDropForbiddenClass); + _jpc.endpoints[idx].anchor.out(); + } + } + }.bind(this)); + + _jsPlumb.initDroppable(canvas, dropOptions, "internal", isTransient); + } + }.bind(this); + + // Initialise the endpoint's canvas as a drop target. The drop handler will take care of the logic of whether + // something can actually be dropped. + if (!this.anchor.isFloating) { + _initDropTarget(this.canvas, !(params._transient || this.anchor.isFloating), this, params.reference); + } + + return this; + }; + + _ju.extend(_jp.Endpoint, _jp.OverlayCapableJsPlumbUIComponent, { + + setVisible: function (v, doNotChangeConnections, doNotNotifyOtherEndpoint) { + this._jsPlumb.visible = v; + if (this.canvas) { + this.canvas.style.display = v ? "block" : "none"; + } + this[v ? "showOverlays" : "hideOverlays"](); + if (!doNotChangeConnections) { + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].setVisible(v); + if (!doNotNotifyOtherEndpoint) { + var oIdx = this === this.connections[i].endpoints[0] ? 1 : 0; + // only change the other endpoint if this is its only connection. + if (this.connections[i].endpoints[oIdx].connections.length === 1) { + this.connections[i].endpoints[oIdx].setVisible(v, true, true); + } + } + } + } + }, + getAttachedElements: function () { + return this.connections; + }, + applyType: function (t, doNotRepaint) { + this.setPaintStyle(t.endpointStyle || t.paintStyle, doNotRepaint); + this.setHoverPaintStyle(t.endpointHoverStyle || t.hoverPaintStyle, doNotRepaint); + if (t.maxConnections != null) { + this._jsPlumb.maxConnections = t.maxConnections; + } + if (t.scope) { + this.scope = t.scope; + } + _jp.extend(this, t, typeParameters); + if (t.cssClass != null && this.canvas) { + this._jsPlumb.instance.addClass(this.canvas, t.cssClass); + } + _jp.OverlayCapableJsPlumbUIComponent.applyType(this, t); + }, + isEnabled: function () { + return this._jsPlumb.enabled; + }, + setEnabled: function (e) { + this._jsPlumb.enabled = e; + }, + cleanup: function () { + var anchorClass = this._jsPlumb.instance.endpointAnchorClassPrefix + (this._jsPlumb.currentAnchorClass ? "-" + this._jsPlumb.currentAnchorClass : ""); + _jp.removeClass(this.element, anchorClass); + this.anchor = null; + this.endpoint.cleanup(true); + this.endpoint.destroy(); + this.endpoint = null; + // drag/drop + this._jsPlumb.instance.destroyDraggable(this.canvas, "internal"); + this._jsPlumb.instance.destroyDroppable(this.canvas, "internal"); + }, + setHover: function (h) { + if (this.endpoint && this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) { + this.endpoint.setHover(h); + } + }, + isFull: function () { + return this._jsPlumb.maxConnections === 0 ? true : !(this.isFloating() || this._jsPlumb.maxConnections < 0 || this.connections.length < this._jsPlumb.maxConnections); + }, + /** + * private but needs to be exposed. + */ + isFloating: function () { + return this.anchor != null && this.anchor.isFloating; + }, + isConnectedTo: function (endpoint) { + var found = false; + if (endpoint) { + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].endpoints[1] === endpoint || this.connections[i].endpoints[0] === endpoint) { + found = true; + break; + } + } + } + return found; + }, + getConnectionCost: function () { + return this._jsPlumb.connectionCost; + }, + setConnectionCost: function (c) { + this._jsPlumb.connectionCost = c; + }, + areConnectionsDirected: function () { + return this._jsPlumb.connectionsDirected; + }, + setConnectionsDirected: function (b) { + this._jsPlumb.connectionsDirected = b; + }, + setElementId: function (_elId) { + this.elementId = _elId; + this.anchor.elementId = _elId; + }, + setReferenceElement: function (_el) { + this.element = _jp.getElement(_el); + }, + setDragAllowedWhenFull: function (allowed) { + this.dragAllowedWhenFull = allowed; + }, + equals: function (endpoint) { + return this.anchor.equals(endpoint.anchor); + }, + getUuid: function () { + return this._jsPlumb.uuid; + }, + computeAnchor: function (params) { + return this.anchor.compute(params); + } + }); + + root.jsPlumbInstance.prototype.EndpointDropHandler = function (dhParams) { + return function (e) { + + var _jsPlumb = dhParams.jsPlumb; + + // remove the classes that are added dynamically. drop is neither forbidden nor allowed now that + // the drop is finishing. + dhParams.removeClass(_jsPlumb.endpointDropAllowedClass); + dhParams.removeClass(_jsPlumb.endpointDropForbiddenClass); + + var originalEvent = _jsPlumb.getDropEvent(arguments), + draggable = _jsPlumb.getDragObject(arguments), + id = _jsPlumb.getAttribute(draggable, "dragId"), + elId = _jsPlumb.getAttribute(draggable, "elId"), + scope = _jsPlumb.getAttribute(draggable, "originalScope"), + jpc = _jsPlumb.getFloatingConnectionFor(id); + + // if no active connection, bail. + if (jpc == null) { + return; + } + + // calculate if this is an existing connection. + var existingConnection = jpc.suspendedEndpoint != null; + + // if suspended endpoint exists but has been cleaned up, bail. This means it's an existing connection + // that has been detached and will shortly be discarded. + if (existingConnection && jpc.suspendedEndpoint._jsPlumb == null) { + return; + } + + // get the drop endpoint. for a normal connection this is just the one that would replace the currently + // floating endpoint. for a makeTarget this is a new endpoint that is created on drop. But we leave that to + // the handler to figure out. + var _ep = dhParams.getEndpoint(jpc); + + // If we're not given an endpoint to use, bail. + if (_ep == null) { + return; + } + + // if this is a drop back where the connection came from, mark it force reattach and + // return; the stop handler will reattach. without firing an event. + if (dhParams.isRedrop(jpc, dhParams)) { + jpc._forceReattach = true; + jpc.setHover(false); + if (dhParams.maybeCleanup) { + dhParams.maybeCleanup(_ep); + } + return; + } + + // ensure we dont bother trying to drop sources on non-source eps, and same for target. + var idx = _jsPlumb.getFloatingAnchorIndex(jpc); + if ((idx === 0 && !dhParams.isSource)|| (idx === 1 && !dhParams.isTarget)){ + if (dhParams.maybeCleanup) { + dhParams.maybeCleanup(_ep); + } + return; + } + + if (dhParams.onDrop) { + dhParams.onDrop(jpc); + } + + // restore the original scope if necessary (issue 57) + if (scope) { + _jsPlumb.setDragScope(draggable, scope); + } + + // if the target of the drop is full, fire an event (we abort below) + // makeTarget: keep. + var isFull = dhParams.isFull(e); + if (isFull) { + _ep.fire("maxConnections", { + endpoint: this, + connection: jpc, + maxConnections: _ep._jsPlumb.maxConnections + }, originalEvent); + } + // + // if endpoint enabled, not full, and matches the index of the floating endpoint... + if (!isFull && dhParams.enabled()) { + var _doContinue = true; + + // before testing for beforeDrop, reset the connection's source/target to be the actual DOM elements + // involved (that is, stash any temporary stuff used for dragging. but we need to keep it around in + // order that the anchor manager can clean things up properly). + if (idx === 0) { + jpc.floatingElement = jpc.source; + jpc.floatingId = jpc.sourceId; + jpc.floatingEndpoint = jpc.endpoints[0]; + jpc.floatingIndex = 0; + jpc.source = dhParams.element; + jpc.sourceId = dhParams.elementId; + } else { + jpc.floatingElement = jpc.target; + jpc.floatingId = jpc.targetId; + jpc.floatingEndpoint = jpc.endpoints[1]; + jpc.floatingIndex = 1; + jpc.target = dhParams.element; + jpc.targetId = dhParams.elementId; + } + + // if this is an existing connection and detach is not allowed we won't continue. The connection's + // endpoints have been reinstated; everything is back to how it was. + if (existingConnection && jpc.suspendedEndpoint.id !== _ep.id) { + if (!jpc.isDetachAllowed(jpc) || !jpc.endpoints[idx].isDetachAllowed(jpc) || !jpc.suspendedEndpoint.isDetachAllowed(jpc) || !_jsPlumb.checkCondition("beforeDetach", jpc)) { + _doContinue = false; + } + } + +// ------------ wrap the execution path in a function so we can support asynchronous beforeDrop + + var continueFunction = function (optionalData) { + // remove this jpc from the current endpoint, which is a floating endpoint that we will + // subsequently discard. + jpc.endpoints[idx].detachFromConnection(jpc); + + // if there's a suspended endpoint, detach it from the connection. + if (jpc.suspendedEndpoint) { + jpc.suspendedEndpoint.detachFromConnection(jpc); + } + + jpc.endpoints[idx] = _ep; + _ep.addConnection(jpc); + + // copy our parameters in to the connection: + var params = _ep.getParameters(); + for (var aParam in params) { + jpc.setParameter(aParam, params[aParam]); + } + + if (!existingConnection) { + // if not an existing connection and + if (params.draggable) { + _jsPlumb.initDraggable(this.element, dhParams.dragOptions, "internal", _jsPlumb); + } + } + else { + var suspendedElementId = jpc.suspendedEndpoint.elementId; + _jsPlumb.fireMoveEvent({ + index: idx, + originalSourceId: idx === 0 ? suspendedElementId : jpc.sourceId, + newSourceId: idx === 0 ? _ep.elementId : jpc.sourceId, + originalTargetId: idx === 1 ? suspendedElementId : jpc.targetId, + newTargetId: idx === 1 ? _ep.elementId : jpc.targetId, + originalSourceEndpoint: idx === 0 ? jpc.suspendedEndpoint : jpc.endpoints[0], + newSourceEndpoint: idx === 0 ? _ep : jpc.endpoints[0], + originalTargetEndpoint: idx === 1 ? jpc.suspendedEndpoint : jpc.endpoints[1], + newTargetEndpoint: idx === 1 ? _ep : jpc.endpoints[1], + connection: jpc + }, originalEvent); + } + + if (idx === 1) { + _jsPlumb.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.floatingId, jpc.targetId, jpc); + } + else { + _jsPlumb.anchorManager.sourceChanged(jpc.floatingId, jpc.sourceId, jpc, jpc.source); + } + + // when makeSource has uniqueEndpoint:true, we want to create connections with new endpoints + // that are subsequently deleted. So makeSource sets `finalEndpoint`, which is the Endpoint to + // which the connection should be attached. The `detachFromConnection` call below results in the + // temporary endpoint being cleaned up. + if (jpc.endpoints[0].finalEndpoint) { + var _toDelete = jpc.endpoints[0]; + _toDelete.detachFromConnection(jpc); + jpc.endpoints[0] = jpc.endpoints[0].finalEndpoint; + jpc.endpoints[0].addConnection(jpc); + } + + // if optionalData was given, merge it onto the connection's data. + if (_ju.isObject(optionalData)) { + jpc.mergeData(optionalData); + } + // finalise will inform the anchor manager and also add to + // connectionsByScope if necessary. + _jsPlumb.finaliseConnection(jpc, null, originalEvent, false); + jpc.setHover(false); + + // SP continuous anchor flush + _jsPlumb.revalidate(jpc.endpoints[0].element); + + }.bind(this); + + var dontContinueFunction = function () { + // otherwise just put it back on the endpoint it was on before the drag. + if (jpc.suspendedEndpoint) { + jpc.endpoints[idx] = jpc.suspendedEndpoint; + jpc.setHover(false); + jpc._forceDetach = true; + if (idx === 0) { + jpc.source = jpc.suspendedEndpoint.element; + jpc.sourceId = jpc.suspendedEndpoint.elementId; + } else { + jpc.target = jpc.suspendedEndpoint.element; + jpc.targetId = jpc.suspendedEndpoint.elementId; + } + jpc.suspendedEndpoint.addConnection(jpc); + + // TODO checkSanity + if (idx === 1) { + _jsPlumb.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.floatingId, jpc.targetId, jpc); + } + else { + _jsPlumb.anchorManager.sourceChanged(jpc.floatingId, jpc.sourceId, jpc, jpc.source); + } + + _jsPlumb.repaint(jpc.sourceId); + jpc._forceDetach = false; + } + }; + +// -------------------------------------- + // now check beforeDrop. this will be available only on Endpoints that are setup to + // have a beforeDrop condition (although, secretly, under the hood all Endpoints and + // the Connection have them, because they are on jsPlumbUIComponent. shhh!), because + // it only makes sense to have it on a target endpoint. + _doContinue = _doContinue && dhParams.isDropAllowed(jpc.sourceId, jpc.targetId, jpc.scope, jpc, _ep);// && jpc.pending; + + if (_doContinue) { + continueFunction(_doContinue); + return true; + } + else { + dontContinueFunction(); + } + } + + if (dhParams.maybeCleanup) { + dhParams.maybeCleanup(_ep); + } + + _jsPlumb.currentlyDragging = false; + }; + }; +}).call(typeof window !== 'undefined' ? window : this); + +/* + * This file contains the code for Connections. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, + _jp = root.jsPlumb, + _ju = root.jsPlumbUtil; + + var makeConnector = function (_jsPlumb, renderMode, connectorName, connectorArgs, forComponent) { + // first make sure we have a cache for the specified renderer + _jp.Connectors[renderMode] = _jp.Connectors[renderMode] || {}; + + // now see if the one we want exists; if not we will try to make it + if (_jp.Connectors[renderMode][connectorName] == null) { + + if (_jp.Connectors[connectorName] == null) { + if (!_jsPlumb.Defaults.DoNotThrowErrors) { + throw new TypeError("jsPlumb: unknown connector type '" + connectorName + "'"); + } else { + return null; + } + } + + _jp.Connectors[renderMode][connectorName] = function() { + _jp.Connectors[connectorName].apply(this, arguments); + _jp.ConnectorRenderers[renderMode].apply(this, arguments); + }; + + _ju.extend(_jp.Connectors[renderMode][connectorName], [ _jp.Connectors[connectorName], _jp.ConnectorRenderers[renderMode]]); + + } + + return new _jp.Connectors[renderMode][connectorName](connectorArgs, forComponent); + }, + _makeAnchor = function (anchorParams, elementId, _jsPlumb) { + return (anchorParams) ? _jsPlumb.makeAnchor(anchorParams, elementId, _jsPlumb) : null; + }, + _updateConnectedClass = function (conn, element, _jsPlumb, remove) { + if (element != null) { + element._jsPlumbConnections = element._jsPlumbConnections || {}; + if (remove) { + delete element._jsPlumbConnections[conn.id]; + } + else { + element._jsPlumbConnections[conn.id] = true; + } + + if (_ju.isEmpty(element._jsPlumbConnections)) { + _jsPlumb.removeClass(element, _jsPlumb.connectedClass); + } + else { + _jsPlumb.addClass(element, _jsPlumb.connectedClass); + } + } + }; + + _jp.Connection = function (params) { + var _newEndpoint = params.newEndpoint; + + this.id = params.id; + this.connector = null; + this.idPrefix = "_jsplumb_c_"; + this.defaultLabelLocation = 0.5; + this.defaultOverlayKeys = ["Overlays", "ConnectionOverlays"]; + // if a new connection is the result of moving some existing connection, params.previousConnection + // will have that Connection in it. listeners for the jsPlumbConnection event can look for that + // member and take action if they need to. + this.previousConnection = params.previousConnection; + this.source = _jp.getElement(params.source); + this.target = _jp.getElement(params.target); + + + _jp.OverlayCapableJsPlumbUIComponent.apply(this, arguments); + + // sourceEndpoint and targetEndpoint override source/target, if they are present. but + // source is not overridden if the Endpoint has declared it is not the final target of a connection; + // instead we use the source that the Endpoint declares will be the final source element. + if (params.sourceEndpoint) { + this.source = params.sourceEndpoint.getElement(); + this.sourceId = params.sourceEndpoint.elementId; + } else { + this.sourceId = this._jsPlumb.instance.getId(this.source); + } + + if (params.targetEndpoint) { + this.target = params.targetEndpoint.getElement(); + this.targetId = params.targetEndpoint.elementId; + } else { + this.targetId = this._jsPlumb.instance.getId(this.target); + } + + + this.scope = params.scope; // scope may have been passed in to the connect call. if it wasn't, we will pull it from the source endpoint, after having initialised the endpoints. + this.endpoints = []; + this.endpointStyles = []; + + var _jsPlumb = this._jsPlumb.instance; + + _jsPlumb.manage(this.sourceId, this.source); + _jsPlumb.manage(this.targetId, this.target); + + this._jsPlumb.visible = true; + + this._jsPlumb.params = { + cssClass: params.cssClass, + container: params.container, + "pointer-events": params["pointer-events"], + editorParams: params.editorParams, + overlays: params.overlays + }; + this._jsPlumb.lastPaintedAt = null; + + // listen to mouseover and mouseout events passed from the container delegate. + this.bind("mouseover", function () { + this.setHover(true); + }.bind(this)); + this.bind("mouseout", function () { + this.setHover(false); + }.bind(this)); + + +// INITIALISATION CODE + + this.makeEndpoint = function (isSource, el, elId, ep, definition) { + elId = elId || this._jsPlumb.instance.getId(el); + return this.prepareEndpoint(_jsPlumb, _newEndpoint, this, ep, isSource ? 0 : 1, params, el, elId, definition); + }; + + // if type given, get the endpoint definitions mapping to that type from the jsplumb instance, and use those. + // we apply types at the end of this constructor but endpoints are only honoured in a type definition at + // create time. + if (params.type) { + params.endpoints = params.endpoints || this._jsPlumb.instance.deriveEndpointAndAnchorSpec(params.type).endpoints; + } + + var eS = this.makeEndpoint(true, this.source, this.sourceId, params.sourceEndpoint), + eT = this.makeEndpoint(false, this.target, this.targetId, params.targetEndpoint); + + if (eS) { + _ju.addToList(params.endpointsByElement, this.sourceId, eS); + } + if (eT) { + _ju.addToList(params.endpointsByElement, this.targetId, eT); + } + // if scope not set, set it to be the scope for the source endpoint. + if (!this.scope) { + this.scope = this.endpoints[0].scope; + } + + // if explicitly told to (or not to) delete endpoints when empty, override endpoint's preferences + if (params.deleteEndpointsOnEmpty != null) { + this.endpoints[0].setDeleteOnEmpty(params.deleteEndpointsOnEmpty); + this.endpoints[1].setDeleteOnEmpty(params.deleteEndpointsOnEmpty); + } + +// -------------------------- DEFAULT TYPE --------------------------------------------- + + // DETACHABLE + var _detachable = _jsPlumb.Defaults.ConnectionsDetachable; + if (params.detachable === false) { + _detachable = false; + } + if (this.endpoints[0].connectionsDetachable === false) { + _detachable = false; + } + if (this.endpoints[1].connectionsDetachable === false) { + _detachable = false; + } + // REATTACH + var _reattach = params.reattach || this.endpoints[0].reattachConnections || this.endpoints[1].reattachConnections || _jsPlumb.Defaults.ReattachConnections; + + this.appendToDefaultType({ + detachable: _detachable, + reattach: _reattach, + paintStyle:this.endpoints[0].connectorStyle || this.endpoints[1].connectorStyle || params.paintStyle || _jsPlumb.Defaults.PaintStyle || _jp.Defaults.PaintStyle, + hoverPaintStyle:this.endpoints[0].connectorHoverStyle || this.endpoints[1].connectorHoverStyle || params.hoverPaintStyle || _jsPlumb.Defaults.HoverPaintStyle || _jp.Defaults.HoverPaintStyle + }); + + var _suspendedAt = _jsPlumb.getSuspendedAt(); + if (!_jsPlumb.isSuspendDrawing()) { + // paint the endpoints + var myInfo = _jsPlumb.getCachedData(this.sourceId), + myOffset = myInfo.o, myWH = myInfo.s, + otherInfo = _jsPlumb.getCachedData(this.targetId), + otherOffset = otherInfo.o, + otherWH = otherInfo.s, + initialTimestamp = _suspendedAt || _jsPlumb.timestamp(), + anchorLoc = this.endpoints[0].anchor.compute({ + xy: [ myOffset.left, myOffset.top ], wh: myWH, element: this.endpoints[0], + elementId: this.endpoints[0].elementId, + txy: [ otherOffset.left, otherOffset.top ], twh: otherWH, tElement: this.endpoints[1], + timestamp: initialTimestamp + }); + + this.endpoints[0].paint({ anchorLoc: anchorLoc, timestamp: initialTimestamp }); + + anchorLoc = this.endpoints[1].anchor.compute({ + xy: [ otherOffset.left, otherOffset.top ], wh: otherWH, element: this.endpoints[1], + elementId: this.endpoints[1].elementId, + txy: [ myOffset.left, myOffset.top ], twh: myWH, tElement: this.endpoints[0], + timestamp: initialTimestamp + }); + this.endpoints[1].paint({ anchorLoc: anchorLoc, timestamp: initialTimestamp }); + } + + this.getTypeDescriptor = function () { + return "connection"; + }; + this.getAttachedElements = function () { + return this.endpoints; + }; + + this.isDetachable = function (ep) { + return this._jsPlumb.detachable === false ? false : ep != null ? ep.connectionsDetachable === true : this._jsPlumb.detachable === true; + }; + this.setDetachable = function (detachable) { + this._jsPlumb.detachable = detachable === true; + }; + this.isReattach = function () { + return this._jsPlumb.reattach === true || this.endpoints[0].reattachConnections === true || this.endpoints[1].reattachConnections === true; + }; + this.setReattach = function (reattach) { + this._jsPlumb.reattach = reattach === true; + }; + +// END INITIALISATION CODE + + +// COST + DIRECTIONALITY + // if cost not supplied, try to inherit from source endpoint + this._jsPlumb.cost = params.cost || this.endpoints[0].getConnectionCost(); + this._jsPlumb.directed = params.directed; + // inherit directed flag if set no source endpoint + if (params.directed == null) { + this._jsPlumb.directed = this.endpoints[0].areConnectionsDirected(); + } +// END COST + DIRECTIONALITY + +// PARAMETERS + // merge all the parameters objects into the connection. parameters set + // on the connection take precedence; then source endpoint params, then + // finally target endpoint params. + var _p = _jp.extend({}, this.endpoints[1].getParameters()); + _jp.extend(_p, this.endpoints[0].getParameters()); + _jp.extend(_p, this.getParameters()); + this.setParameters(_p); +// END PARAMETERS + +// PAINTING + + this.setConnector(this.endpoints[0].connector || this.endpoints[1].connector || params.connector || _jsPlumb.Defaults.Connector || _jp.Defaults.Connector, true); + var data = params.data == null || !_ju.isObject(params.data) ? {} : params.data; + this.getData = function() { return data; }; + this.setData = function(d) { data = d || {}; }; + this.mergeData = function(d) { data = _jp.extend(data, d); }; + + // the very last thing we do is apply types, if there are any. + var _types = [ "default", this.endpoints[0].connectionType, this.endpoints[1].connectionType, params.type ].join(" "); + if (/[^\s]/.test(_types)) { + this.addType(_types, params.data, true); + } + + this.updateConnectedClass(); + +// END PAINTING + }; + + _ju.extend(_jp.Connection, _jp.OverlayCapableJsPlumbUIComponent, { + applyType: function (t, doNotRepaint, typeMap) { + + var _connector = null; + if (t.connector != null) { + _connector = this.getCachedTypeItem("connector", typeMap.connector); + if (_connector == null) { + _connector = this.prepareConnector(t.connector, typeMap.connector); + this.cacheTypeItem("connector", _connector, typeMap.connector); + } + this.setPreparedConnector(_connector); + } + + // none of these things result in the creation of objects so can be ignored. + if (t.detachable != null) { + this.setDetachable(t.detachable); + } + if (t.reattach != null) { + this.setReattach(t.reattach); + } + if (t.scope) { + this.scope = t.scope; + } + + if (t.cssClass != null && this.canvas) { + this._jsPlumb.instance.addClass(this.canvas, t.cssClass); + } + + var _anchors = null; + // this also results in the creation of objects. + if (t.anchor) { + // note that even if the param was anchor, we store `anchors`. + _anchors = this.getCachedTypeItem("anchors", typeMap.anchor); + if (_anchors == null) { + _anchors = [ this._jsPlumb.instance.makeAnchor(t.anchor), this._jsPlumb.instance.makeAnchor(t.anchor) ]; + this.cacheTypeItem("anchors", _anchors, typeMap.anchor); + } + } + else if (t.anchors) { + _anchors = this.getCachedTypeItem("anchors", typeMap.anchors); + if (_anchors == null) { + _anchors = [ + this._jsPlumb.instance.makeAnchor(t.anchors[0]), + this._jsPlumb.instance.makeAnchor(t.anchors[1]) + ]; + this.cacheTypeItem("anchors", _anchors, typeMap.anchors); + } + } + if (_anchors != null) { + this.endpoints[0].anchor = _anchors[0]; + this.endpoints[1].anchor = _anchors[1]; + if (this.endpoints[1].anchor.isDynamic) { + this._jsPlumb.instance.repaint(this.endpoints[1].elementId); + } + } + + _jp.OverlayCapableJsPlumbUIComponent.applyType(this, t); + }, + addClass: function (c, informEndpoints) { + if (informEndpoints) { + this.endpoints[0].addClass(c); + this.endpoints[1].addClass(c); + if (this.suspendedEndpoint) { + this.suspendedEndpoint.addClass(c); + } + } + if (this.connector) { + this.connector.addClass(c); + } + }, + removeClass: function (c, informEndpoints) { + if (informEndpoints) { + this.endpoints[0].removeClass(c); + this.endpoints[1].removeClass(c); + if (this.suspendedEndpoint) { + this.suspendedEndpoint.removeClass(c); + } + } + if (this.connector) { + this.connector.removeClass(c); + } + }, + isVisible: function () { + return this._jsPlumb.visible; + }, + setVisible: function (v) { + this._jsPlumb.visible = v; + if (this.connector) { + this.connector.setVisible(v); + } + this.repaint(); + }, + cleanup: function () { + this.updateConnectedClass(true); + this.endpoints = null; + this.source = null; + this.target = null; + if (this.connector != null) { + this.connector.cleanup(true); + this.connector.destroy(true); + } + this.connector = null; + }, + updateConnectedClass:function(remove) { + if (this._jsPlumb) { + _updateConnectedClass(this, this.source, this._jsPlumb.instance, remove); + _updateConnectedClass(this, this.target, this._jsPlumb.instance, remove); + } + }, + setHover: function (state) { + if (this.connector && this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) { + this.connector.setHover(state); + root.jsPlumb[state ? "addClass" : "removeClass"](this.source, this._jsPlumb.instance.hoverSourceClass); + root.jsPlumb[state ? "addClass" : "removeClass"](this.target, this._jsPlumb.instance.hoverTargetClass); + } + }, + getUuids:function() { + return [ this.endpoints[0].getUuid(), this.endpoints[1].getUuid() ]; + }, + getCost: function () { + return this._jsPlumb ? this._jsPlumb.cost : -Infinity; + }, + setCost: function (c) { + this._jsPlumb.cost = c; + }, + isDirected: function () { + return this._jsPlumb.directed; + }, + getConnector: function () { + return this.connector; + }, + prepareConnector:function(connectorSpec, typeId) { + var connectorArgs = { + _jsPlumb: this._jsPlumb.instance, + cssClass: this._jsPlumb.params.cssClass, + container: this._jsPlumb.params.container, + "pointer-events": this._jsPlumb.params["pointer-events"] + }, + renderMode = this._jsPlumb.instance.getRenderMode(), + connector; + + if (_ju.isString(connectorSpec)) { + connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec, connectorArgs, this); + } // lets you use a string as shorthand. + else if (_ju.isArray(connectorSpec)) { + if (connectorSpec.length === 1) { + connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], connectorArgs, this); + } + else { + connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], _ju.merge(connectorSpec[1], connectorArgs), this); + } + } + if (typeId != null) { + connector.typeId = typeId; + } + return connector; + }, + setPreparedConnector: function(connector, doNotRepaint, doNotChangeListenerComponent, typeId) { + + if (this.connector !== connector) { + + var previous, previousClasses = ""; + // the connector will not be cleaned up if it was set as part of a type, because `typeId` will be set on it + // and we havent passed in `true` for "force" here. + if (this.connector != null) { + previous = this.connector; + previousClasses = previous.getClass(); + this.connector.cleanup(); + this.connector.destroy(); + } + + this.connector = connector; + if (typeId) { + this.cacheTypeItem("connector", connector, typeId); + } + + this.canvas = this.connector.canvas; + this.bgCanvas = this.connector.bgCanvas; + + this.connector.reattach(this._jsPlumb.instance); + + // put classes from prior connector onto the canvas + this.addClass(previousClasses); + + // new: instead of binding listeners per connector, we now just have one delegate on the container. + // so for that handler we set the connection as the '_jsPlumb' member of the canvas element, and + // bgCanvas, if it exists, which it does right now in the VML renderer, so it won't from v 2.0.0 onwards. + if (this.canvas) { + this.canvas._jsPlumb = this; + } + if (this.bgCanvas) { + this.bgCanvas._jsPlumb = this; + } + + if (previous != null) { + var o = this.getOverlays(); + for (var i = 0; i < o.length; i++) { + if (o[i].transfer) { + o[i].transfer(this.connector); + } + } + } + + if (!doNotChangeListenerComponent) { + this.setListenerComponent(this.connector); + } + if (!doNotRepaint) { + this.repaint(); + } + } + }, + setConnector: function (connectorSpec, doNotRepaint, doNotChangeListenerComponent, typeId) { + var connector = this.prepareConnector(connectorSpec, typeId); + this.setPreparedConnector(connector, doNotRepaint, doNotChangeListenerComponent, typeId); + }, + paint: function (params) { + + if (!this._jsPlumb.instance.isSuspendDrawing() && this._jsPlumb.visible) { + params = params || {}; + var timestamp = params.timestamp, + // if the moving object is not the source we must transpose the two references. + swap = false, + tId = swap ? this.sourceId : this.targetId, sId = swap ? this.targetId : this.sourceId, + tIdx = swap ? 0 : 1, sIdx = swap ? 1 : 0; + + if (timestamp == null || timestamp !== this._jsPlumb.lastPaintedAt) { + var sourceInfo = this._jsPlumb.instance.updateOffset({elId:sId}).o, + targetInfo = this._jsPlumb.instance.updateOffset({elId:tId}).o, + sE = this.endpoints[sIdx], tE = this.endpoints[tIdx]; + + var sAnchorP = sE.anchor.getCurrentLocation({xy: [sourceInfo.left, sourceInfo.top], wh: [sourceInfo.width, sourceInfo.height], element: sE, timestamp: timestamp}), + tAnchorP = tE.anchor.getCurrentLocation({xy: [targetInfo.left, targetInfo.top], wh: [targetInfo.width, targetInfo.height], element: tE, timestamp: timestamp}); + + this.connector.resetBounds(); + + this.connector.compute({ + sourcePos: sAnchorP, + targetPos: tAnchorP, + sourceOrientation:sE.anchor.getOrientation(sE), + targetOrientation:tE.anchor.getOrientation(tE), + sourceEndpoint: this.endpoints[sIdx], + targetEndpoint: this.endpoints[tIdx], + "stroke-width": this._jsPlumb.paintStyleInUse.strokeWidth, + sourceInfo: sourceInfo, + targetInfo: targetInfo + }); + + var overlayExtents = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; + + // compute overlays. we do this first so we can get their placements, and adjust the + // container if needs be (if an overlay would be clipped) + for (var i in this._jsPlumb.overlays) { + if (this._jsPlumb.overlays.hasOwnProperty(i)) { + var o = this._jsPlumb.overlays[i]; + if (o.isVisible()) { + this._jsPlumb.overlayPlacements[i] = o.draw(this.connector, this._jsPlumb.paintStyleInUse, this.getAbsoluteOverlayPosition(o)); + overlayExtents.minX = Math.min(overlayExtents.minX, this._jsPlumb.overlayPlacements[i].minX); + overlayExtents.maxX = Math.max(overlayExtents.maxX, this._jsPlumb.overlayPlacements[i].maxX); + overlayExtents.minY = Math.min(overlayExtents.minY, this._jsPlumb.overlayPlacements[i].minY); + overlayExtents.maxY = Math.max(overlayExtents.maxY, this._jsPlumb.overlayPlacements[i].maxY); + } + } + } + + var lineWidth = parseFloat(this._jsPlumb.paintStyleInUse.strokeWidth || 1) / 2, + outlineWidth = parseFloat(this._jsPlumb.paintStyleInUse.strokeWidth || 0), + extents = { + xmin: Math.min(this.connector.bounds.minX - (lineWidth + outlineWidth), overlayExtents.minX), + ymin: Math.min(this.connector.bounds.minY - (lineWidth + outlineWidth), overlayExtents.minY), + xmax: Math.max(this.connector.bounds.maxX + (lineWidth + outlineWidth), overlayExtents.maxX), + ymax: Math.max(this.connector.bounds.maxY + (lineWidth + outlineWidth), overlayExtents.maxY) + }; + // paint the connector. + this.connector.paintExtents = extents; + this.connector.paint(this._jsPlumb.paintStyleInUse, null, extents); + // and then the overlays + for (var j in this._jsPlumb.overlays) { + if (this._jsPlumb.overlays.hasOwnProperty(j)) { + var p = this._jsPlumb.overlays[j]; + if (p.isVisible()) { + p.paint(this._jsPlumb.overlayPlacements[j], extents); + } + } + } + } + this._jsPlumb.lastPaintedAt = timestamp; + } + }, + repaint: function (params) { + var p = jsPlumb.extend(params || {}, {}); + p.elId = this.sourceId; + this.paint(p); + }, + prepareEndpoint: function (_jsPlumb, _newEndpoint, conn, existing, index, params, element, elementId, definition) { + var e; + if (existing) { + conn.endpoints[index] = existing; + existing.addConnection(conn); + } else { + if (!params.endpoints) { + params.endpoints = [ null, null ]; + } + var ep = definition || params.endpoints[index] || params.endpoint || _jsPlumb.Defaults.Endpoints[index] || _jp.Defaults.Endpoints[index] || _jsPlumb.Defaults.Endpoint || _jp.Defaults.Endpoint; + if (!params.endpointStyles) { + params.endpointStyles = [ null, null ]; + } + if (!params.endpointHoverStyles) { + params.endpointHoverStyles = [ null, null ]; + } + var es = params.endpointStyles[index] || params.endpointStyle || _jsPlumb.Defaults.EndpointStyles[index] || _jp.Defaults.EndpointStyles[index] || _jsPlumb.Defaults.EndpointStyle || _jp.Defaults.EndpointStyle; + // Endpoints derive their fill from the connector's stroke, if no fill was specified. + if (es.fill == null && params.paintStyle != null) { + es.fill = params.paintStyle.stroke; + } + + if (es.outlineStroke == null && params.paintStyle != null) { + es.outlineStroke = params.paintStyle.outlineStroke; + } + if (es.outlineWidth == null && params.paintStyle != null) { + es.outlineWidth = params.paintStyle.outlineWidth; + } + + var ehs = params.endpointHoverStyles[index] || params.endpointHoverStyle || _jsPlumb.Defaults.EndpointHoverStyles[index] || _jp.Defaults.EndpointHoverStyles[index] || _jsPlumb.Defaults.EndpointHoverStyle || _jp.Defaults.EndpointHoverStyle; + // endpoint hover fill style is derived from connector's hover stroke style + if (params.hoverPaintStyle != null) { + if (ehs == null) { + ehs = {}; + } + if (ehs.fill == null) { + ehs.fill = params.hoverPaintStyle.stroke; + } + } + var a = params.anchors ? params.anchors[index] : + params.anchor ? params.anchor : + _makeAnchor(_jsPlumb.Defaults.Anchors[index], elementId, _jsPlumb) || + _makeAnchor(_jp.Defaults.Anchors[index], elementId, _jsPlumb) || + _makeAnchor(_jsPlumb.Defaults.Anchor, elementId, _jsPlumb) || + _makeAnchor(_jp.Defaults.Anchor, elementId, _jsPlumb), + u = params.uuids ? params.uuids[index] : null; + + e = _newEndpoint({ + paintStyle: es, hoverPaintStyle: ehs, endpoint: ep, connections: [ conn ], + uuid: u, anchor: a, source: element, scope: params.scope, + reattach: params.reattach || _jsPlumb.Defaults.ReattachConnections, + detachable: params.detachable || _jsPlumb.Defaults.ConnectionsDetachable + }); + if (existing == null) { + e.setDeleteOnEmpty(true); + } + conn.endpoints[index] = e; + + if (params.drawEndpoints === false) { + e.setVisible(false, true, true); + } + + } + return e; + }, + replaceEndpoint:function(idx, endpointDef) { + + var current = this.endpoints[idx], + elId = current.elementId, + ebe = this._jsPlumb.instance.getEndpoints(elId), + _idx = ebe.indexOf(current), + _new = this.makeEndpoint(idx === 0, current.element, elId, null, endpointDef); + + this.endpoints[idx] = _new; + + ebe.splice(_idx, 1, _new); + this._jsPlumb.instance.deleteObject({endpoint:current, deleteAttachedObjects:false}); + this._jsPlumb.instance.fire("endpointReplaced", {previous:current, current:_new}); + + this._jsPlumb.instance.anchorManager.updateOtherEndpoint(this.endpoints[0].elementId, this.endpoints[1].elementId, this.endpoints[1].elementId, this); + + } + + }); // END Connection class +}).call(typeof window !== 'undefined' ? window : this); + +/* + * This file contains the code for creating and manipulating anchors. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + + var root = this, + _ju = root.jsPlumbUtil, + _jp = root.jsPlumb; + + // + // manages anchors for all elements. + // + _jp.AnchorManager = function (params) { + var _amEndpoints = {}, + continuousAnchorLocations = {}, + continuousAnchorOrientations = {}, + connectionsByElementId = {}, + self = this, + anchorLists = {}, + jsPlumbInstance = params.jsPlumbInstance, + floatingConnections = {}, + // used by placeAnchors function + placeAnchorsOnLine = function (desc, elementDimensions, elementPosition, connections, horizontal, otherMultiplier, reverse) { + var a = [], step = elementDimensions[horizontal ? 0 : 1] / (connections.length + 1); + + for (var i = 0; i < connections.length; i++) { + var val = (i + 1) * step, other = otherMultiplier * elementDimensions[horizontal ? 1 : 0]; + if (reverse) { + val = elementDimensions[horizontal ? 0 : 1] - val; + } + + var dx = (horizontal ? val : other), x = elementPosition[0] + dx, xp = dx / elementDimensions[0], + dy = (horizontal ? other : val), y = elementPosition[1] + dy, yp = dy / elementDimensions[1]; + + a.push([ x, y, xp, yp, connections[i][1], connections[i][2] ]); + } + + return a; + }, + // used by edgeSortFunctions + currySort = function (reverseAngles) { + return function (a, b) { + var r = true; + if (reverseAngles) { + r = a[0][0] < b[0][0]; + } + else { + r = a[0][0] > b[0][0]; + } + return r === false ? -1 : 1; + }; + }, + // used by edgeSortFunctions + leftSort = function (a, b) { + // first get adjusted values + var p1 = a[0][0] < 0 ? -Math.PI - a[0][0] : Math.PI - a[0][0], + p2 = b[0][0] < 0 ? -Math.PI - b[0][0] : Math.PI - b[0][0]; + if (p1 > p2) { + return 1; + } + else { + return -1; + } + }, + // used by placeAnchors + edgeSortFunctions = { + "top": function (a, b) { + return a[0] > b[0] ? 1 : -1; + }, + "right": currySort(true), + "bottom": currySort(true), + "left": leftSort + }, + // used by placeAnchors + _sortHelper = function (_array, _fn) { + return _array.sort(_fn); + }, + // used by AnchorManager.redraw + placeAnchors = function (elementId, _anchorLists) { + var cd = jsPlumbInstance.getCachedData(elementId), sS = cd.s, sO = cd.o, + placeSomeAnchors = function (desc, elementDimensions, elementPosition, unsortedConnections, isHorizontal, otherMultiplier, orientation) { + if (unsortedConnections.length > 0) { + var sc = _sortHelper(unsortedConnections, edgeSortFunctions[desc]), // puts them in order based on the target element's pos on screen + reverse = desc === "right" || desc === "top", + anchors = placeAnchorsOnLine(desc, elementDimensions, + elementPosition, sc, + isHorizontal, otherMultiplier, reverse); + + // takes a computed anchor position and adjusts it for parent offset and scroll, then stores it. + var _setAnchorLocation = function (endpoint, anchorPos) { + continuousAnchorLocations[endpoint.id] = [ anchorPos[0], anchorPos[1], anchorPos[2], anchorPos[3] ]; + continuousAnchorOrientations[endpoint.id] = orientation; + }; + + for (var i = 0; i < anchors.length; i++) { + var c = anchors[i][4], weAreSource = c.endpoints[0].elementId === elementId, weAreTarget = c.endpoints[1].elementId === elementId; + if (weAreSource) { + _setAnchorLocation(c.endpoints[0], anchors[i]); + } + if (weAreTarget) { + _setAnchorLocation(c.endpoints[1], anchors[i]); + } + } + } + }; + + placeSomeAnchors("bottom", sS, [sO.left, sO.top], _anchorLists.bottom, true, 1, [0, 1]); + placeSomeAnchors("top", sS, [sO.left, sO.top], _anchorLists.top, true, 0, [0, -1]); + placeSomeAnchors("left", sS, [sO.left, sO.top], _anchorLists.left, false, 0, [-1, 0]); + placeSomeAnchors("right", sS, [sO.left, sO.top], _anchorLists.right, false, 1, [1, 0]); + }; + + this.reset = function () { + _amEndpoints = {}; + connectionsByElementId = {}; + anchorLists = {}; + }; + this.addFloatingConnection = function (key, conn) { + floatingConnections[key] = conn; + }; + this.removeFloatingConnection = function (key) { + delete floatingConnections[key]; + }; + this.newConnection = function (conn) { + var sourceId = conn.sourceId, targetId = conn.targetId, + ep = conn.endpoints, + doRegisterTarget = true, + registerConnection = function (otherIndex, otherEndpoint, otherAnchor, elId, c) { + if ((sourceId === targetId) && otherAnchor.isContinuous) { + // remove the target endpoint's canvas. we dont need it. + conn._jsPlumb.instance.removeElement(ep[1].canvas); + doRegisterTarget = false; + } + _ju.addToList(connectionsByElementId, elId, [c, otherEndpoint, otherAnchor.constructor === _jp.DynamicAnchor]); + }; + + registerConnection(0, ep[0], ep[0].anchor, targetId, conn); + if (doRegisterTarget) { + registerConnection(1, ep[1], ep[1].anchor, sourceId, conn); + } + }; + var removeEndpointFromAnchorLists = function (endpoint) { + (function (list, eId) { + if (list) { // transient anchors dont get entries in this list. + var f = function (e) { + return e[4] === eId; + }; + _ju.removeWithFunction(list.top, f); + _ju.removeWithFunction(list.left, f); + _ju.removeWithFunction(list.bottom, f); + _ju.removeWithFunction(list.right, f); + } + })(anchorLists[endpoint.elementId], endpoint.id); + }; + this.connectionDetached = function (connInfo, doNotRedraw) { + var connection = connInfo.connection || connInfo, + sourceId = connInfo.sourceId, + targetId = connInfo.targetId, + ep = connection.endpoints, + removeConnection = function (otherIndex, otherEndpoint, otherAnchor, elId, c) { + _ju.removeWithFunction(connectionsByElementId[elId], function (_c) { + return _c[0].id === c.id; + }); + }; + + removeConnection(1, ep[1], ep[1].anchor, sourceId, connection); + removeConnection(0, ep[0], ep[0].anchor, targetId, connection); + if (connection.floatingId) { + removeConnection(connection.floatingIndex, connection.floatingEndpoint, connection.floatingEndpoint.anchor, connection.floatingId, connection); + removeEndpointFromAnchorLists(connection.floatingEndpoint); + } + + // remove from anchorLists + removeEndpointFromAnchorLists(connection.endpoints[0]); + removeEndpointFromAnchorLists(connection.endpoints[1]); + + if (!doNotRedraw) { + self.redraw(connection.sourceId); + if (connection.targetId !== connection.sourceId) { + self.redraw(connection.targetId); + } + } + }; + this.add = function (endpoint, elementId) { + _ju.addToList(_amEndpoints, elementId, endpoint); + }; + this.changeId = function (oldId, newId) { + connectionsByElementId[newId] = connectionsByElementId[oldId]; + _amEndpoints[newId] = _amEndpoints[oldId]; + delete connectionsByElementId[oldId]; + delete _amEndpoints[oldId]; + }; + this.getConnectionsFor = function (elementId) { + return connectionsByElementId[elementId] || []; + }; + this.getEndpointsFor = function (elementId) { + return _amEndpoints[elementId] || []; + }; + this.deleteEndpoint = function (endpoint) { + _ju.removeWithFunction(_amEndpoints[endpoint.elementId], function (e) { + return e.id === endpoint.id; + }); + removeEndpointFromAnchorLists(endpoint); + }; + this.clearFor = function (elementId) { + delete _amEndpoints[elementId]; + _amEndpoints[elementId] = []; + }; + // updates the given anchor list by either updating an existing anchor's info, or adding it. this function + // also removes the anchor from its previous list, if the edge it is on has changed. + // all connections found along the way (those that are connected to one of the faces this function + // operates on) are added to the connsToPaint list, as are their endpoints. in this way we know to repaint + // them wthout having to calculate anything else about them. + var _updateAnchorList = function (lists, theta, order, conn, aBoolean, otherElId, idx, reverse, edgeId, elId, connsToPaint, endpointsToPaint) { + // first try to find the exact match, but keep track of the first index of a matching element id along the way.s + var exactIdx = -1, + firstMatchingElIdx = -1, + endpoint = conn.endpoints[idx], + endpointId = endpoint.id, + oIdx = [1, 0][idx], + values = [ + [ theta, order ], + conn, + aBoolean, + otherElId, + endpointId + ], + listToAddTo = lists[edgeId], + listToRemoveFrom = endpoint._continuousAnchorEdge ? lists[endpoint._continuousAnchorEdge] : null, + i, + candidate; + + if (listToRemoveFrom) { + var rIdx = _ju.findWithFunction(listToRemoveFrom, function (e) { + return e[4] === endpointId; + }); + if (rIdx !== -1) { + listToRemoveFrom.splice(rIdx, 1); + // get all connections from this list + for (i = 0; i < listToRemoveFrom.length; i++) { + candidate = listToRemoveFrom[i][1]; + _ju.addWithFunction(connsToPaint, candidate, function (c) { + return c.id === candidate.id; + }); + _ju.addWithFunction(endpointsToPaint, listToRemoveFrom[i][1].endpoints[idx], function (e) { + return e.id === candidate.endpoints[idx].id; + }); + _ju.addWithFunction(endpointsToPaint, listToRemoveFrom[i][1].endpoints[oIdx], function (e) { + return e.id === candidate.endpoints[oIdx].id; + }); + } + } + } + + for (i = 0; i < listToAddTo.length; i++) { + candidate = listToAddTo[i][1]; + if (params.idx === 1 && listToAddTo[i][3] === otherElId && firstMatchingElIdx === -1) { + firstMatchingElIdx = i; + } + _ju.addWithFunction(connsToPaint, candidate, function (c) { + return c.id === candidate.id; + }); + _ju.addWithFunction(endpointsToPaint, listToAddTo[i][1].endpoints[idx], function (e) { + return e.id === candidate.endpoints[idx].id; + }); + _ju.addWithFunction(endpointsToPaint, listToAddTo[i][1].endpoints[oIdx], function (e) { + return e.id === candidate.endpoints[oIdx].id; + }); + } + if (exactIdx !== -1) { + listToAddTo[exactIdx] = values; + } + else { + var insertIdx = reverse ? firstMatchingElIdx !== -1 ? firstMatchingElIdx : 0 : listToAddTo.length; // of course we will get this from having looked through the array shortly. + listToAddTo.splice(insertIdx, 0, values); + } + + // store this for next time. + endpoint._continuousAnchorEdge = edgeId; + }; + + // + // find the entry in an endpoint's list for this connection and update its target endpoint + // with the current target in the connection. + // This method and sourceChanged need to be folder into one. + // + this.updateOtherEndpoint = function (sourceElId, oldTargetId, newTargetId, connection) { + var sIndex = _ju.findWithFunction(connectionsByElementId[sourceElId], function (i) { + return i[0].id === connection.id; + }), + tIndex = _ju.findWithFunction(connectionsByElementId[oldTargetId], function (i) { + return i[0].id === connection.id; + }); + + // update or add data for source + if (sIndex !== -1) { + connectionsByElementId[sourceElId][sIndex][0] = connection; + connectionsByElementId[sourceElId][sIndex][1] = connection.endpoints[1]; + connectionsByElementId[sourceElId][sIndex][2] = connection.endpoints[1].anchor.constructor === _jp.DynamicAnchor; + } + + // remove entry for previous target (if there) + if (tIndex > -1) { + connectionsByElementId[oldTargetId].splice(tIndex, 1); + // add entry for new target + _ju.addToList(connectionsByElementId, newTargetId, [connection, connection.endpoints[0], connection.endpoints[0].anchor.constructor === _jp.DynamicAnchor]); + } + + connection.updateConnectedClass(); + }; + + // + // notification that the connection given has changed source from the originalId to the newId. + // This involves: + // 1. removing the connection from the list of connections stored for the originalId + // 2. updating the source information for the target of the connection + // 3. re-registering the connection in connectionsByElementId with the newId + // + this.sourceChanged = function (originalId, newId, connection, newElement) { + if (originalId !== newId) { + + connection.sourceId = newId; + connection.source = newElement; + + // remove the entry that points from the old source to the target + _ju.removeWithFunction(connectionsByElementId[originalId], function (info) { + return info[0].id === connection.id; + }); + // find entry for target and update it + var tIdx = _ju.findWithFunction(connectionsByElementId[connection.targetId], function (i) { + return i[0].id === connection.id; + }); + if (tIdx > -1) { + connectionsByElementId[connection.targetId][tIdx][0] = connection; + connectionsByElementId[connection.targetId][tIdx][1] = connection.endpoints[0]; + connectionsByElementId[connection.targetId][tIdx][2] = connection.endpoints[0].anchor.constructor === _jp.DynamicAnchor; + } + // add entry for new source + _ju.addToList(connectionsByElementId, newId, [connection, connection.endpoints[1], connection.endpoints[1].anchor.constructor === _jp.DynamicAnchor]); + + // TODO SP not final on this yet. when a user drags an existing connection and it turns into a self + // loop, then this code hides the target endpoint (by removing it from the DOM) But I think this should + // occur only if the anchor is Continuous + if (connection.endpoints[1].anchor.isContinuous) { + if (connection.source === connection.target) { + connection._jsPlumb.instance.removeElement(connection.endpoints[1].canvas); + } + else { + if (connection.endpoints[1].canvas.parentNode == null) { + connection._jsPlumb.instance.appendElement(connection.endpoints[1].canvas); + } + } + } + + connection.updateConnectedClass(); + } + }; + + // + // moves the given endpoint from `currentId` to `element`. + // This involves: + // + // 1. changing the key in _amEndpoints under which the endpoint is stored + // 2. changing the source or target values in all of the endpoint's connections + // 3. changing the array in connectionsByElementId in which the endpoint's connections + // are stored (done by either sourceChanged or updateOtherEndpoint) + // + this.rehomeEndpoint = function (ep, currentId, element) { + var eps = _amEndpoints[currentId] || [], + elementId = jsPlumbInstance.getId(element); + + if (elementId !== currentId) { + var idx = eps.indexOf(ep); + if (idx > -1) { + var _ep = eps.splice(idx, 1)[0]; + self.add(_ep, elementId); + } + } + + for (var i = 0; i < ep.connections.length; i++) { + if (ep.connections[i].sourceId === currentId) { + self.sourceChanged(currentId, ep.elementId, ep.connections[i], ep.element); + } + else if (ep.connections[i].targetId === currentId) { + ep.connections[i].targetId = ep.elementId; + ep.connections[i].target = ep.element; + self.updateOtherEndpoint(ep.connections[i].sourceId, currentId, ep.elementId, ep.connections[i]); + } + } + }; + + this.redraw = function (elementId, ui, timestamp, offsetToUI, clearEdits, doNotRecalcEndpoint) { + + if (!jsPlumbInstance.isSuspendDrawing()) { + // get all the endpoints for this element + var ep = _amEndpoints[elementId] || [], + endpointConnections = connectionsByElementId[elementId] || [], + connectionsToPaint = [], + endpointsToPaint = [], + anchorsToUpdate = []; + + timestamp = timestamp || jsPlumbInstance.timestamp(); + // offsetToUI are values that would have been calculated in the dragManager when registering + // an endpoint for an element that had a parent (somewhere in the hierarchy) that had been + // registered as draggable. + offsetToUI = offsetToUI || {left: 0, top: 0}; + if (ui) { + ui = { + left: ui.left + offsetToUI.left, + top: ui.top + offsetToUI.top + }; + } + + // valid for one paint cycle. + var myOffset = jsPlumbInstance.updateOffset({ elId: elementId, offset: ui, recalc: false, timestamp: timestamp }), + orientationCache = {}; + + // actually, first we should compute the orientation of this element to all other elements to which + // this element is connected with a continuous anchor (whether both ends of the connection have + // a continuous anchor or just one) + + for (var i = 0; i < endpointConnections.length; i++) { + var conn = endpointConnections[i][0], + sourceId = conn.sourceId, + targetId = conn.targetId, + sourceContinuous = conn.endpoints[0].anchor.isContinuous, + targetContinuous = conn.endpoints[1].anchor.isContinuous; + + if (sourceContinuous || targetContinuous) { + var oKey = sourceId + "_" + targetId, + o = orientationCache[oKey], + oIdx = conn.sourceId === elementId ? 1 : 0; + + if (sourceContinuous && !anchorLists[sourceId]) { + anchorLists[sourceId] = { top: [], right: [], bottom: [], left: [] }; + } + if (targetContinuous && !anchorLists[targetId]) { + anchorLists[targetId] = { top: [], right: [], bottom: [], left: [] }; + } + + if (elementId !== targetId) { + jsPlumbInstance.updateOffset({ elId: targetId, timestamp: timestamp }); + } + if (elementId !== sourceId) { + jsPlumbInstance.updateOffset({ elId: sourceId, timestamp: timestamp }); + } + + var td = jsPlumbInstance.getCachedData(targetId), + sd = jsPlumbInstance.getCachedData(sourceId); + + if (targetId === sourceId && (sourceContinuous || targetContinuous)) { + // here we may want to improve this by somehow determining the face we'd like + // to put the connector on. ideally, when drawing, the face should be calculated + // by determining which face is closest to the point at which the mouse button + // was released. for now, we're putting it on the top face. + _updateAnchorList( anchorLists[sourceId], -Math.PI / 2, 0, conn, false, targetId, 0, false, "top", sourceId, connectionsToPaint, endpointsToPaint); + _updateAnchorList( anchorLists[targetId], -Math.PI / 2, 0, conn, false, sourceId, 1, false, "top", targetId, connectionsToPaint, endpointsToPaint); + } + else { + if (!o) { + o = this.calculateOrientation(sourceId, targetId, sd.o, td.o, conn.endpoints[0].anchor, conn.endpoints[1].anchor, conn); + orientationCache[oKey] = o; + // this would be a performance enhancement, but the computed angles need to be clamped to + //the (-PI/2 -> PI/2) range in order for the sorting to work properly. + /* orientationCache[oKey2] = { + orientation:o.orientation, + a:[o.a[1], o.a[0]], + theta:o.theta + Math.PI, + theta2:o.theta2 + Math.PI + };*/ + } + if (sourceContinuous) { + _updateAnchorList(anchorLists[sourceId], o.theta, 0, conn, false, targetId, 0, false, o.a[0], sourceId, connectionsToPaint, endpointsToPaint); + } + if (targetContinuous) { + _updateAnchorList(anchorLists[targetId], o.theta2, -1, conn, true, sourceId, 1, true, o.a[1], targetId, connectionsToPaint, endpointsToPaint); + } + } + + if (sourceContinuous) { + _ju.addWithFunction(anchorsToUpdate, sourceId, function (a) { + return a === sourceId; + }); + } + if (targetContinuous) { + _ju.addWithFunction(anchorsToUpdate, targetId, function (a) { + return a === targetId; + }); + } + _ju.addWithFunction(connectionsToPaint, conn, function (c) { + return c.id === conn.id; + }); + if ((sourceContinuous && oIdx === 0) || (targetContinuous && oIdx === 1)) { + _ju.addWithFunction(endpointsToPaint, conn.endpoints[oIdx], function (e) { + return e.id === conn.endpoints[oIdx].id; + }); + } + } + } + + // place Endpoints whose anchors are continuous but have no Connections + for (i = 0; i < ep.length; i++) { + if (ep[i].connections.length === 0 && ep[i].anchor.isContinuous) { + if (!anchorLists[elementId]) { + anchorLists[elementId] = { top: [], right: [], bottom: [], left: [] }; + } + _updateAnchorList(anchorLists[elementId], -Math.PI / 2, 0, {endpoints: [ep[i], ep[i]], paint: function () { + }}, false, elementId, 0, false, ep[i].anchor.getDefaultFace(), elementId, connectionsToPaint, endpointsToPaint); + _ju.addWithFunction(anchorsToUpdate, elementId, function (a) { + return a === elementId; + }); + } + } + + // now place all the continuous anchors we need to; + for (i = 0; i < anchorsToUpdate.length; i++) { + placeAnchors(anchorsToUpdate[i], anchorLists[anchorsToUpdate[i]]); + } + + // now that continuous anchors have been placed, paint all the endpoints for this element + for (i = 0; i < ep.length; i++) { + ep[i].paint({ timestamp: timestamp, offset: myOffset, dimensions: myOffset.s, recalc: doNotRecalcEndpoint !== true }); + } + + // ... and any other endpoints we came across as a result of the continuous anchors. + for (i = 0; i < endpointsToPaint.length; i++) { + var cd = jsPlumbInstance.getCachedData(endpointsToPaint[i].elementId); + //endpointsToPaint[i].paint({ timestamp: timestamp, offset: cd, dimensions: cd.s }); + endpointsToPaint[i].paint({ timestamp: null, offset: cd, dimensions: cd.s }); + } + + // paint all the standard and "dynamic connections", which are connections whose other anchor is + // static and therefore does need to be recomputed; we make sure that happens only one time. + + // TODO we could have compiled a list of these in the first pass through connections; might save some time. + for (i = 0; i < endpointConnections.length; i++) { + var otherEndpoint = endpointConnections[i][1]; + if (otherEndpoint.anchor.constructor === _jp.DynamicAnchor) { + otherEndpoint.paint({ elementWithPrecedence: elementId, timestamp: timestamp }); + _ju.addWithFunction(connectionsToPaint, endpointConnections[i][0], function (c) { + return c.id === endpointConnections[i][0].id; + }); + // all the connections for the other endpoint now need to be repainted + for (var k = 0; k < otherEndpoint.connections.length; k++) { + if (otherEndpoint.connections[k] !== endpointConnections[i][0]) { + _ju.addWithFunction(connectionsToPaint, otherEndpoint.connections[k], function (c) { + return c.id === otherEndpoint.connections[k].id; + }); + } + } + } else { + _ju.addWithFunction(connectionsToPaint, endpointConnections[i][0], function (c) { + return c.id === endpointConnections[i][0].id; + }); + } + } + + // paint current floating connection for this element, if there is one. + var fc = floatingConnections[elementId]; + if (fc) { + fc.paint({timestamp: timestamp, recalc: false, elId: elementId}); + } + + // paint all the connections + for (i = 0; i < connectionsToPaint.length; i++) { + connectionsToPaint[i].paint({elId: elementId, timestamp: null, recalc: false, clearEdits: clearEdits}); + } + } + }; + + var ContinuousAnchor = function (anchorParams) { + _ju.EventGenerator.apply(this); + this.type = "Continuous"; + this.isDynamic = true; + this.isContinuous = true; + var faces = anchorParams.faces || ["top", "right", "bottom", "left"], + clockwise = !(anchorParams.clockwise === false), + availableFaces = { }, + opposites = { "top": "bottom", "right": "left", "left": "right", "bottom": "top" }, + clockwiseOptions = { "top": "right", "right": "bottom", "left": "top", "bottom": "left" }, + antiClockwiseOptions = { "top": "left", "right": "top", "left": "bottom", "bottom": "right" }, + secondBest = clockwise ? clockwiseOptions : antiClockwiseOptions, + lastChoice = clockwise ? antiClockwiseOptions : clockwiseOptions, + cssClass = anchorParams.cssClass || "", + _currentFace = null, _lockedFace = null, X_AXIS_FACES = ["left", "right"], Y_AXIS_FACES = ["top", "bottom"], + _lockedAxis = null; + + for (var i = 0; i < faces.length; i++) { + availableFaces[faces[i]] = true; + } + + this.getDefaultFace = function () { + return faces.length === 0 ? "top" : faces[0]; + }; + + this.isRelocatable = function() { return true; }; + this.isSnapOnRelocate = function() { return true; }; + + // if the given edge is supported, returns it. otherwise looks for a substitute that _is_ + // supported. if none supported we also return the request edge. + this.verifyEdge = function (edge) { + if (availableFaces[edge]) { + return edge; + } + else if (availableFaces[opposites[edge]]) { + return opposites[edge]; + } + else if (availableFaces[secondBest[edge]]) { + return secondBest[edge]; + } + else if (availableFaces[lastChoice[edge]]) { + return lastChoice[edge]; + } + return edge; // we have to give them something. + }; + + this.isEdgeSupported = function (edge) { + return _lockedAxis == null ? + + (_lockedFace == null ? availableFaces[edge] === true : _lockedFace === edge) + + : _lockedAxis.indexOf(edge) !== -1; + }; + + this.setCurrentFace = function(face, overrideLock) { + _currentFace = face; + // if currently locked, and the user wants to override, do that. + if (overrideLock && _lockedFace != null) { + _lockedFace = _currentFace; + } + }; + + this.getCurrentFace = function() { return _currentFace; }; + this.getSupportedFaces = function() { + var af = []; + for (var k in availableFaces) { + if (availableFaces[k]) { + af.push(k); + } + } + return af; + }; + + this.lock = function() { + _lockedFace = _currentFace; + }; + this.unlock = function() { + _lockedFace = null; + }; + this.isLocked = function() { + return _lockedFace != null; + }; + + this.lockCurrentAxis = function() { + if (_currentFace != null) { + _lockedAxis = (_currentFace === "left" || _currentFace === "right") ? X_AXIS_FACES : Y_AXIS_FACES; + } + }; + + this.unlockCurrentAxis = function() { + _lockedAxis = null; + }; + + this.compute = function (params) { + return continuousAnchorLocations[params.element.id] || [0, 0]; + }; + this.getCurrentLocation = function (params) { + return continuousAnchorLocations[params.element.id] || [0, 0]; + }; + this.getOrientation = function (endpoint) { + return continuousAnchorOrientations[endpoint.id] || [0, 0]; + }; + this.getCssClass = function () { + return cssClass; + }; + }; + + // continuous anchors + jsPlumbInstance.continuousAnchorFactory = { + get: function (params) { + return new ContinuousAnchor(params); + }, + clear: function (elementId) { + delete continuousAnchorLocations[elementId]; + } + }; + }; + + _jp.AnchorManager.prototype.calculateOrientation = function (sourceId, targetId, sd, td, sourceAnchor, targetAnchor) { + + var Orientation = { HORIZONTAL: "horizontal", VERTICAL: "vertical", DIAGONAL: "diagonal", IDENTITY: "identity" }, + axes = ["left", "top", "right", "bottom"]; + + if (sourceId === targetId) { + return { + orientation: Orientation.IDENTITY, + a: ["top", "top"] + }; + } + + var theta = Math.atan2((td.centery - sd.centery), (td.centerx - sd.centerx)), + theta2 = Math.atan2((sd.centery - td.centery), (sd.centerx - td.centerx)); + +// -------------------------------------------------------------------------------------- + + // improved face calculation. get midpoints of each face for source and target, then put in an array with all combinations of + // source/target faces. sort this array by distance between midpoints. the entry at index 0 is our preferred option. we can + // go through the array one by one until we find an entry in which each requested face is supported. + var candidates = [], midpoints = { }; + (function (types, dim) { + for (var i = 0; i < types.length; i++) { + midpoints[types[i]] = { + "left": [ dim[i].left, dim[i].centery ], + "right": [ dim[i].right, dim[i].centery ], + "top": [ dim[i].centerx, dim[i].top ], + "bottom": [ dim[i].centerx , dim[i].bottom] + }; + } + })([ "source", "target" ], [ sd, td ]); + + for (var sf = 0; sf < axes.length; sf++) { + for (var tf = 0; tf < axes.length; tf++) { + candidates.push({ + source: axes[sf], + target: axes[tf], + dist: Biltong.lineLength(midpoints.source[axes[sf]], midpoints.target[axes[tf]]) + }); + } + } + + candidates.sort(function (a, b) { + return a.dist < b.dist ? -1 : a.dist > b.dist ? 1 : 0; + }); + + // now go through this list and try to get an entry that satisfies both (there will be one, unless one of the anchors + // declares no available faces) + var sourceEdge = candidates[0].source, targetEdge = candidates[0].target; + for (var i = 0; i < candidates.length; i++) { + + if (!sourceAnchor.isContinuous || sourceAnchor.isEdgeSupported(candidates[i].source)) { + sourceEdge = candidates[i].source; + } + else { + sourceEdge = null; + } + + if (!targetAnchor.isContinuous || targetAnchor.isEdgeSupported(candidates[i].target)) { + targetEdge = candidates[i].target; + } + else { + targetEdge = null; + } + + if (sourceEdge != null && targetEdge != null) { + break; + } + } + + if (sourceAnchor.isContinuous) { + sourceAnchor.setCurrentFace(sourceEdge); + } + + if (targetAnchor.isContinuous) { + targetAnchor.setCurrentFace(targetEdge); + } + +// -------------------------------------------------------------------------------------- + + return { + a: [ sourceEdge, targetEdge ], + theta: theta, + theta2: theta2 + }; + }; + + /** + * Anchors model a position on some element at which an Endpoint may be located. They began as a first class citizen of jsPlumb, ie. a user + * was required to create these themselves, but over time this has been replaced by the concept of referring to them either by name (eg. "TopMiddle"), + * or by an array describing their coordinates (eg. [ 0, 0.5, 0, -1 ], which is the same as "TopMiddle"). jsPlumb now handles all of the + * creation of Anchors without user intervention. + */ + _jp.Anchor = function (params) { + this.x = params.x || 0; + this.y = params.y || 0; + this.elementId = params.elementId; + this.cssClass = params.cssClass || ""; + this.userDefinedLocation = null; + this.orientation = params.orientation || [ 0, 0 ]; + this.lastReturnValue = null; + this.offsets = params.offsets || [ 0, 0 ]; + this.timestamp = null; + + var relocatable = params.relocatable !== false; + this.isRelocatable = function() { return relocatable; }; + this.setRelocatable = function(_relocatable) { relocatable = _relocatable; }; + var snapOnRelocate = params.snapOnRelocate !== false; + this.isSnapOnRelocate = function() { return snapOnRelocate; }; + + var locked = false; + this.lock = function() { locked = true; }; + this.unlock = function() { locked = false; }; + this.isLocked = function() { return locked; }; + + _ju.EventGenerator.apply(this); + + this.compute = function (params) { + + var xy = params.xy, wh = params.wh, timestamp = params.timestamp; + + if (params.clearUserDefinedLocation) { + this.userDefinedLocation = null; + } + + if (timestamp && timestamp === this.timestamp) { + return this.lastReturnValue; + } + + if (this.userDefinedLocation != null) { + this.lastReturnValue = this.userDefinedLocation; + } + else { + this.lastReturnValue = [ xy[0] + (this.x * wh[0]) + this.offsets[0], xy[1] + (this.y * wh[1]) + this.offsets[1], this.x, this.y ]; + } + + this.timestamp = timestamp; + return this.lastReturnValue; + }; + + this.getCurrentLocation = function (params) { + params = params || {}; + return (this.lastReturnValue == null || (params.timestamp != null && this.timestamp !== params.timestamp)) ? this.compute(params) : this.lastReturnValue; + }; + + this.setPosition = function(x, y, ox, oy, overrideLock) { + if (!locked || overrideLock) { + this.x = x; + this.y = y; + this.orientation = [ ox, oy ]; + this.lastReturnValue = null; + } + }; + }; + _ju.extend(_jp.Anchor, _ju.EventGenerator, { + equals: function (anchor) { + if (!anchor) { + return false; + } + var ao = anchor.getOrientation(), + o = this.getOrientation(); + return this.x === anchor.x && this.y === anchor.y && this.offsets[0] === anchor.offsets[0] && this.offsets[1] === anchor.offsets[1] && o[0] === ao[0] && o[1] === ao[1]; + }, + getUserDefinedLocation: function () { + return this.userDefinedLocation; + }, + setUserDefinedLocation: function (l) { + this.userDefinedLocation = l; + }, + clearUserDefinedLocation: function () { + this.userDefinedLocation = null; + }, + getOrientation: function () { + return this.orientation; + }, + getCssClass: function () { + return this.cssClass; + } + }); + + /** + * An Anchor that floats. its orientation is computed dynamically from + * its position relative to the anchor it is floating relative to. It is used when creating + * a connection through drag and drop. + * + * TODO FloatingAnchor could totally be refactored to extend Anchor just slightly. + */ + _jp.FloatingAnchor = function (params) { + + _jp.Anchor.apply(this, arguments); + + // this is the anchor that this floating anchor is referenced to for + // purposes of calculating the orientation. + var ref = params.reference, + // the canvas this refers to. + refCanvas = params.referenceCanvas, + size = _jp.getSize(refCanvas), + // these are used to store the current relative position of our + // anchor wrt the reference anchor. they only indicate + // direction, so have a value of 1 or -1 (or, very rarely, 0). these + // values are written by the compute method, and read + // by the getOrientation method. + xDir = 0, yDir = 0, + // temporary member used to store an orientation when the floating + // anchor is hovering over another anchor. + orientation = null, + _lastResult = null; + + // clear from parent. we want floating anchor orientation to always be computed. + this.orientation = null; + + // set these to 0 each; they are used by certain types of connectors in the loopback case, + // when the connector is trying to clear the element it is on. but for floating anchor it's not + // very important. + this.x = 0; + this.y = 0; + + this.isFloating = true; + + this.compute = function (params) { + var xy = params.xy, + result = [ xy[0] + (size[0] / 2), xy[1] + (size[1] / 2) ]; // return origin of the element. we may wish to improve this so that any object can be the drag proxy. + _lastResult = result; + return result; + }; + + this.getOrientation = function (_endpoint) { + if (orientation) { + return orientation; + } + else { + var o = ref.getOrientation(_endpoint); + // here we take into account the orientation of the other + // anchor: if it declares zero for some direction, we declare zero too. this might not be the most awesome. perhaps we can come + // up with a better way. it's just so that the line we draw looks like it makes sense. maybe this wont make sense. + return [ Math.abs(o[0]) * xDir * -1, + Math.abs(o[1]) * yDir * -1 ]; + } + }; + + /** + * notification the endpoint associated with this anchor is hovering + * over another anchor; we want to assume that anchor's orientation + * for the duration of the hover. + */ + this.over = function (anchor, endpoint) { + orientation = anchor.getOrientation(endpoint); + }; + + /** + * notification the endpoint associated with this anchor is no + * longer hovering over another anchor; we should resume calculating + * orientation as we normally do. + */ + this.out = function () { + orientation = null; + }; + + this.getCurrentLocation = function (params) { + return _lastResult == null ? this.compute(params) : _lastResult; + }; + }; + _ju.extend(_jp.FloatingAnchor, _jp.Anchor); + + var _convertAnchor = function (anchor, jsPlumbInstance, elementId) { + return anchor.constructor === _jp.Anchor ? anchor : jsPlumbInstance.makeAnchor(anchor, elementId, jsPlumbInstance); + }; + + /* + * A DynamicAnchor is an Anchor that contains a list of other Anchors, which it cycles + * through at compute time to find the one that is located closest to + * the center of the target element, and returns that Anchor's compute + * method result. this causes endpoints to follow each other with + * respect to the orientation of their target elements, which is a useful + * feature for some applications. + * + */ + _jp.DynamicAnchor = function (params) { + _jp.Anchor.apply(this, arguments); + + this.isDynamic = true; + this.anchors = []; + this.elementId = params.elementId; + this.jsPlumbInstance = params.jsPlumbInstance; + + for (var i = 0; i < params.anchors.length; i++) { + this.anchors[i] = _convertAnchor(params.anchors[i], this.jsPlumbInstance, this.elementId); + } + + this.getAnchors = function () { + return this.anchors; + }; + + var _curAnchor = this.anchors.length > 0 ? this.anchors[0] : null, + _lastAnchor = _curAnchor, + self = this, + + // helper method to calculate the distance between the centers of the two elements. + _distance = function (anchor, cx, cy, xy, wh) { + var ax = xy[0] + (anchor.x * wh[0]), ay = xy[1] + (anchor.y * wh[1]), + acx = xy[0] + (wh[0] / 2), acy = xy[1] + (wh[1] / 2); + return (Math.sqrt(Math.pow(cx - ax, 2) + Math.pow(cy - ay, 2)) + + Math.sqrt(Math.pow(acx - ax, 2) + Math.pow(acy - ay, 2))); + }, + // default method uses distance between element centers. you can provide your own method in the dynamic anchor + // constructor (and also to jsPlumb.makeDynamicAnchor). the arguments to it are four arrays: + // xy - xy loc of the anchor's element + // wh - anchor's element's dimensions + // txy - xy loc of the element of the other anchor in the connection + // twh - dimensions of the element of the other anchor in the connection. + // anchors - the list of selectable anchors + _anchorSelector = params.selector || function (xy, wh, txy, twh, anchors) { + var cx = txy[0] + (twh[0] / 2), cy = txy[1] + (twh[1] / 2); + var minIdx = -1, minDist = Infinity; + for (var i = 0; i < anchors.length; i++) { + var d = _distance(anchors[i], cx, cy, xy, wh); + if (d < minDist) { + minIdx = i + 0; + minDist = d; + } + } + return anchors[minIdx]; + }; + + this.compute = function (params) { + var xy = params.xy, wh = params.wh, txy = params.txy, twh = params.twh; + + this.timestamp = params.timestamp; + + var udl = self.getUserDefinedLocation(); + if (udl != null) { + return udl; + } + + // if anchor is locked or an opposite element was not given, we + // maintain our state. anchor will be locked + // if it is the source of a drag and drop. + if (this.isLocked() || txy == null || twh == null) { + return _curAnchor.compute(params); + } + else { + params.timestamp = null; // otherwise clear this, i think. we want the anchor to compute. + } + + _curAnchor = _anchorSelector(xy, wh, txy, twh, this.anchors); + this.x = _curAnchor.x; + this.y = _curAnchor.y; + + if (_curAnchor !== _lastAnchor) { + this.fire("anchorChanged", _curAnchor); + } + + _lastAnchor = _curAnchor; + + return _curAnchor.compute(params); + }; + + this.getCurrentLocation = function (params) { + return this.getUserDefinedLocation() || (_curAnchor != null ? _curAnchor.getCurrentLocation(params) : null); + }; + + this.getOrientation = function (_endpoint) { + return _curAnchor != null ? _curAnchor.getOrientation(_endpoint) : [ 0, 0 ]; + }; + this.over = function (anchor, endpoint) { + if (_curAnchor != null) { + _curAnchor.over(anchor, endpoint); + } + }; + this.out = function () { + if (_curAnchor != null) { + _curAnchor.out(); + } + }; + + this.setAnchor = function(a) { + _curAnchor = a; + }; + + this.getCssClass = function () { + return (_curAnchor && _curAnchor.getCssClass()) || ""; + }; + + /** + * Attempt to match an anchor with the given coordinates and then set it. + * @param coords + * @returns true if matching anchor found, false otherwise. + */ + this.setAnchorCoordinates = function(coords) { + var idx = jsPlumbUtil.findWithFunction(this.anchors, function(a) { + return a.x === coords[0] && a.y === coords[1]; + }); + if (idx !== -1) { + this.setAnchor(this.anchors[idx]); + return true; + } else { + return false; + } + }; + }; + _ju.extend(_jp.DynamicAnchor, _jp.Anchor); + +// -------- basic anchors ------------------ + var _curryAnchor = function (x, y, ox, oy, type, fnInit) { + _jp.Anchors[type] = function (params) { + var a = params.jsPlumbInstance.makeAnchor([ x, y, ox, oy, 0, 0 ], params.elementId, params.jsPlumbInstance); + a.type = type; + if (fnInit) { + fnInit(a, params); + } + return a; + }; + }; + + _curryAnchor(0.5, 0, 0, -1, "TopCenter"); + _curryAnchor(0.5, 1, 0, 1, "BottomCenter"); + _curryAnchor(0, 0.5, -1, 0, "LeftMiddle"); + _curryAnchor(1, 0.5, 1, 0, "RightMiddle"); + + _curryAnchor(0.5, 0, 0, -1, "Top"); + _curryAnchor(0.5, 1, 0, 1, "Bottom"); + _curryAnchor(0, 0.5, -1, 0, "Left"); + _curryAnchor(1, 0.5, 1, 0, "Right"); + _curryAnchor(0.5, 0.5, 0, 0, "Center"); + _curryAnchor(1, 0, 0, -1, "TopRight"); + _curryAnchor(1, 1, 0, 1, "BottomRight"); + _curryAnchor(0, 0, 0, -1, "TopLeft"); + _curryAnchor(0, 1, 0, 1, "BottomLeft"); + +// ------- dynamic anchors ------------------- + + // default dynamic anchors chooses from Top, Right, Bottom, Left + _jp.Defaults.DynamicAnchors = function (params) { + return params.jsPlumbInstance.makeAnchors(["TopCenter", "RightMiddle", "BottomCenter", "LeftMiddle"], params.elementId, params.jsPlumbInstance); + }; + + // default dynamic anchors bound to name 'AutoDefault' + _jp.Anchors.AutoDefault = function (params) { + var a = params.jsPlumbInstance.makeDynamicAnchor(_jp.Defaults.DynamicAnchors(params)); + a.type = "AutoDefault"; + return a; + }; + +// ------- continuous anchors ------------------- + + var _curryContinuousAnchor = function (type, faces) { + _jp.Anchors[type] = function (params) { + var a = params.jsPlumbInstance.makeAnchor(["Continuous", { faces: faces }], params.elementId, params.jsPlumbInstance); + a.type = type; + return a; + }; + }; + + _jp.Anchors.Continuous = function (params) { + return params.jsPlumbInstance.continuousAnchorFactory.get(params); + }; + + _curryContinuousAnchor("ContinuousLeft", ["left"]); + _curryContinuousAnchor("ContinuousTop", ["top"]); + _curryContinuousAnchor("ContinuousBottom", ["bottom"]); + _curryContinuousAnchor("ContinuousRight", ["right"]); + +// ------- position assign anchors ------------------- + + // this anchor type lets you assign the position at connection time. + _curryAnchor(0, 0, 0, 0, "Assign", function (anchor, params) { + // find what to use as the "position finder". the user may have supplied a String which represents + // the id of a position finder in jsPlumb.AnchorPositionFinders, or the user may have supplied the + // position finder as a function. we find out what to use and then set it on the anchor. + var pf = params.position || "Fixed"; + anchor.positionFinder = pf.constructor === String ? params.jsPlumbInstance.AnchorPositionFinders[pf] : pf; + // always set the constructor params; the position finder might need them later (the Grid one does, + // for example) + anchor.constructorParams = params; + }); + + // these are the default anchor positions finders, which are used by the makeTarget function. supplying + // a position finder argument to that function allows you to specify where the resulting anchor will + // be located + root.jsPlumbInstance.prototype.AnchorPositionFinders = { + "Fixed": function (dp, ep, es) { + return [ (dp.left - ep.left) / es[0], (dp.top - ep.top) / es[1] ]; + }, + "Grid": function (dp, ep, es, params) { + var dx = dp.left - ep.left, dy = dp.top - ep.top, + gx = es[0] / (params.grid[0]), gy = es[1] / (params.grid[1]), + mx = Math.floor(dx / gx), my = Math.floor(dy / gy); + return [ ((mx * gx) + (gx / 2)) / es[0], ((my * gy) + (gy / 2)) / es[1] ]; + } + }; + +// ------- perimeter anchors ------------------- + + _jp.Anchors.Perimeter = function (params) { + params = params || {}; + var anchorCount = params.anchorCount || 60, + shape = params.shape; + + if (!shape) { + throw new Error("no shape supplied to Perimeter Anchor type"); + } + + var _circle = function () { + var r = 0.5, step = Math.PI * 2 / anchorCount, current = 0, a = []; + for (var i = 0; i < anchorCount; i++) { + var x = r + (r * Math.sin(current)), + y = r + (r * Math.cos(current)); + a.push([ x, y, 0, 0 ]); + current += step; + } + return a; + }, + _path = function (segments) { + var anchorsPerFace = anchorCount / segments.length, a = [], + _computeFace = function (x1, y1, x2, y2, fractionalLength, ox, oy) { + anchorsPerFace = anchorCount * fractionalLength; + var dx = (x2 - x1) / anchorsPerFace, dy = (y2 - y1) / anchorsPerFace; + for (var i = 0; i < anchorsPerFace; i++) { + a.push([ + x1 + (dx * i), + y1 + (dy * i), + ox == null ? 0 : ox, + oy == null ? 0 : oy + ]); + } + }; + + for (var i = 0; i < segments.length; i++) { + _computeFace.apply(null, segments[i]); + } + + return a; + }, + _shape = function (faces) { + var s = []; + for (var i = 0; i < faces.length; i++) { + s.push([faces[i][0], faces[i][1], faces[i][2], faces[i][3], 1 / faces.length, faces[i][4], faces[i][5]]); + } + return _path(s); + }, + _rectangle = function () { + return _shape([ + [ 0, 0, 1, 0, 0, -1 ], + [ 1, 0, 1, 1, 1, 0 ], + [ 1, 1, 0, 1, 0, 1 ], + [ 0, 1, 0, 0, -1, 0 ] + ]); + }; + + var _shapes = { + "Circle": _circle, + "Ellipse": _circle, + "Diamond": function () { + return _shape([ + [ 0.5, 0, 1, 0.5 ], + [ 1, 0.5, 0.5, 1 ], + [ 0.5, 1, 0, 0.5 ], + [ 0, 0.5, 0.5, 0 ] + ]); + }, + "Rectangle": _rectangle, + "Square": _rectangle, + "Triangle": function () { + return _shape([ + [ 0.5, 0, 1, 1 ], + [ 1, 1, 0, 1 ], + [ 0, 1, 0.5, 0] + ]); + }, + "Path": function (params) { + var points = params.points, p = [], tl = 0; + for (var i = 0; i < points.length - 1; i++) { + var l = Math.sqrt(Math.pow(points[i][2] - points[i][0]) + Math.pow(points[i][3] - points[i][1])); + tl += l; + p.push([points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], l]); + } + for (var j = 0; j < p.length; j++) { + p[j][4] = p[j][4] / tl; + } + return _path(p); + } + }, + _rotate = function (points, amountInDegrees) { + var o = [], theta = amountInDegrees / 180 * Math.PI; + for (var i = 0; i < points.length; i++) { + var _x = points[i][0] - 0.5, + _y = points[i][1] - 0.5; + + o.push([ + 0.5 + ((_x * Math.cos(theta)) - (_y * Math.sin(theta))), + 0.5 + ((_x * Math.sin(theta)) + (_y * Math.cos(theta))), + points[i][2], + points[i][3] + ]); + } + return o; + }; + + if (!_shapes[shape]) { + throw new Error("Shape [" + shape + "] is unknown by Perimeter Anchor type"); + } + + var da = _shapes[shape](params); + if (params.rotation) { + da = _rotate(da, params.rotation); + } + var a = params.jsPlumbInstance.makeDynamicAnchor(da); + a.type = "Perimeter"; + return a; + }; +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the default Connectors, Endpoint and Overlay definitions. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil, _jg = root.Biltong; + + _jp.Segments = { + + /* + * Class: AbstractSegment + * A Connector is made up of 1..N Segments, each of which has a Type, such as 'Straight', 'Arc', + * 'Bezier'. This is new from 1.4.2, and gives us a lot more flexibility when drawing connections: things such + * as rounded corners for flowchart connectors, for example, or a straight line stub for Bezier connections, are + * much easier to do now. + * + * A Segment is responsible for providing coordinates for painting it, and also must be able to report its length. + * + */ + AbstractSegment: function (params) { + this.params = params; + + /** + * Function: findClosestPointOnPath + * Finds the closest point on this segment to the given [x, y], + * returning both the x and y of the point plus its distance from + * the supplied point, and its location along the length of the + * path inscribed by the segment. This implementation returns + * Infinity for distance and null values for everything else; + * subclasses are expected to override. + */ + this.findClosestPointOnPath = function (x, y) { + return { + d: Infinity, + x: null, + y: null, + l: null + }; + }; + + this.getBounds = function () { + return { + minX: Math.min(params.x1, params.x2), + minY: Math.min(params.y1, params.y2), + maxX: Math.max(params.x1, params.x2), + maxY: Math.max(params.y1, params.y2) + }; + }; + + /** + * Computes the list of points on the segment that intersect the given line. + * @method lineIntersection + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @returns {Array<[number, number]>} + */ + this.lineIntersection = function(x1, y1, x2, y2) { + return []; + }; + + /** + * Computes the list of points on the segment that intersect the box with the given origin and size. + * @method boxIntersection + * @param {number} x1 + * @param {number} y1 + * @param {number} w + * @param {number} h + * @returns {Array<[number, number]>} + */ + this.boxIntersection = function(x, y, w, h) { + var a = []; + a.push.apply(a, this.lineIntersection(x, y, x + w, y)); + a.push.apply(a, this.lineIntersection(x + w, y, x + w, y + h)); + a.push.apply(a, this.lineIntersection(x + w, y + h, x, y + h)); + a.push.apply(a, this.lineIntersection(x, y + h, x, y)); + return a; + }; + + /** + * Computes the list of points on the segment that intersect the given bounding box, which is an object of the form { x:.., y:.., w:.., h:.. }. + * @method lineIntersection + * @param {BoundingRectangle} box + * @returns {Array<[number, number]>} + */ + this.boundingBoxIntersection = function(box) { + return this.boxIntersection(box.x, box.y, box.w, box.y); + }; + }, + Straight: function (params) { + var _super = _jp.Segments.AbstractSegment.apply(this, arguments), + length, m, m2, x1, x2, y1, y2, + _recalc = function () { + length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); + m = _jg.gradient({x: x1, y: y1}, {x: x2, y: y2}); + m2 = -1 / m; + }; + + this.type = "Straight"; + + this.getLength = function () { + return length; + }; + this.getGradient = function () { + return m; + }; + + this.getCoordinates = function () { + return { x1: x1, y1: y1, x2: x2, y2: y2 }; + }; + this.setCoordinates = function (coords) { + x1 = coords.x1; + y1 = coords.y1; + x2 = coords.x2; + y2 = coords.y2; + _recalc(); + }; + this.setCoordinates({x1: params.x1, y1: params.y1, x2: params.x2, y2: params.y2}); + + this.getBounds = function () { + return { + minX: Math.min(x1, x2), + minY: Math.min(y1, y2), + maxX: Math.max(x1, x2), + maxY: Math.max(y1, y2) + }; + }; + + /** + * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from + * 0 to 1 inclusive. for the straight line segment this is simple maths. + */ + this.pointOnPath = function (location, absolute) { + if (location === 0 && !absolute) { + return { x: x1, y: y1 }; + } + else if (location === 1 && !absolute) { + return { x: x2, y: y2 }; + } + else { + var l = absolute ? location > 0 ? location : length + location : location * length; + return _jg.pointOnLine({x: x1, y: y1}, {x: x2, y: y2}, l); + } + }; + + /** + * returns the gradient of the segment at the given point - which for us is constant. + */ + this.gradientAtPoint = function (_) { + return m; + }; + + /** + * returns the point on the segment's path that is 'distance' along the length of the path from 'location', where + * 'location' is a decimal from 0 to 1 inclusive, and 'distance' is a number of pixels. + * this hands off to jsPlumbUtil to do the maths, supplying two points and the distance. + */ + this.pointAlongPathFrom = function (location, distance, absolute) { + var p = this.pointOnPath(location, absolute), + farAwayPoint = distance <= 0 ? {x: x1, y: y1} : {x: x2, y: y2 }; + + /* + location == 1 ? { + x:x1 + ((x2 - x1) * 10), + y:y1 + ((y1 - y2) * 10) + } : + */ + + if (distance <= 0 && Math.abs(distance) > 1) { + distance *= -1; + } + + return _jg.pointOnLine(p, farAwayPoint, distance); + }; + + // is c between a and b? + var within = function (a, b, c) { + return c >= Math.min(a, b) && c <= Math.max(a, b); + }; + // find which of a and b is closest to c + var closest = function (a, b, c) { + return Math.abs(c - a) < Math.abs(c - b) ? a : b; + }; + + /** + Function: findClosestPointOnPath + Finds the closest point on this segment to [x,y]. See + notes on this method in AbstractSegment. + */ + this.findClosestPointOnPath = function (x, y) { + var out = { + d: Infinity, + x: null, + y: null, + l: null, + x1: x1, + x2: x2, + y1: y1, + y2: y2 + }; + + if (m === 0) { + out.y = y1; + out.x = within(x1, x2, x) ? x : closest(x1, x2, x); + } + else if (m === Infinity || m === -Infinity) { + out.x = x1; + out.y = within(y1, y2, y) ? y : closest(y1, y2, y); + } + else { + // closest point lies on normal from given point to this line. + var b = y1 - (m * x1), + b2 = y - (m2 * x), + // y1 = m.x1 + b and y1 = m2.x1 + b2 + // so m.x1 + b = m2.x1 + b2 + // x1(m - m2) = b2 - b + // x1 = (b2 - b) / (m - m2) + _x1 = (b2 - b) / (m - m2), + _y1 = (m * _x1) + b; + + out.x = within(x1, x2, _x1) ? _x1 : closest(x1, x2, _x1);//_x1; + out.y = within(y1, y2, _y1) ? _y1 : closest(y1, y2, _y1);//_y1; + } + + var fractionInSegment = _jg.lineLength([ out.x, out.y ], [ x1, y1 ]); + out.d = _jg.lineLength([x, y], [out.x, out.y]); + out.l = fractionInSegment / length; + return out; + }; + + var _pointLiesBetween = function(q, p1, p2) { + return (p2 > p1) ? (p1 <= q && q <= p2) : (p1 >= q && q >= p2); + }, _plb = _pointLiesBetween; + + /** + * Calculates all intersections of the given line with this segment. + * @param _x1 + * @param _y1 + * @param _x2 + * @param _y2 + * @returns {Array} + */ + this.lineIntersection = function(_x1, _y1, _x2, _y2) { + var m2 = Math.abs(_jg.gradient({x: _x1, y: _y1}, {x: _x2, y: _y2})), + m1 = Math.abs(m), + b = m1 === Infinity ? x1 : y1 - (m1 * x1), + out = [], + b2 = m2 === Infinity ? _x1 : _y1 - (m2 * _x1); + + // if lines parallel, no intersection + if (m2 !== m1) { + // perpendicular, segment horizontal + if(m2 === Infinity && m1 === 0) { + if (_plb(_x1, x1, x2) && _plb(y1, _y1, _y2)) { + out = [ _x1, y1 ]; // we return X on the incident line and Y from the segment + } + } else if(m2 === 0 && m1 === Infinity) { + // perpendicular, segment vertical + if(_plb(_y1, y1, y2) && _plb(x1, _x1, _x2)) { + out = [x1, _y1]; // we return X on the segment and Y from the incident line + } + } else { + var X, Y; + if (m2 === Infinity) { + // test line is a vertical line. where does it cross the segment? + X = _x1; + if (_plb(X, x1, x2)) { + Y = (m1 * _x1) + b; + if (_plb(Y, _y1, _y2)) { + out = [ X, Y ]; + } + } + } else if (m2 === 0) { + Y = _y1; + // test line is a horizontal line. where does it cross the segment? + if (_plb(Y, y1, y2)) { + X = (_y1 - b) / m1; + if (_plb(X, _x1, _x2)) { + out = [ X, Y ]; + } + } + } else { + // mX + b = m2X + b2 + // mX - m2X = b2 - b + // X(m - m2) = b2 - b + // X = (b2 - b) / (m - m2) + // Y = mX + b + X = (b2 - b) / (m1 - m2); + Y = (m1 * X) + b; + if(_plb(X, x1, x2) && _plb(Y, y1, y2)) { + out = [ X, Y]; + } + } + } + } + + return out; + }; + + /** + * Calculates all intersections of the given box with this segment. By default this method simply calls `lineIntersection` with each of the four + * faces of the box; subclasses can override this if they think there's a faster way to compute the entire box at once. + * @param x X position of top left corner of box + * @param y Y position of top left corner of box + * @param w width of box + * @param h height of box + * @returns {Array} + */ + this.boxIntersection = function(x, y, w, h) { + var a = []; + a.push.apply(a, this.lineIntersection(x, y, x + w, y)); + a.push.apply(a, this.lineIntersection(x + w, y, x + w, y + h)); + a.push.apply(a, this.lineIntersection(x + w, y + h, x, y + h)); + a.push.apply(a, this.lineIntersection(x, y + h, x, y)); + return a; + }; + + /** + * Calculates all intersections of the given bounding box with this segment. By default this method simply calls `lineIntersection` with each of the four + * faces of the box; subclasses can override this if they think there's a faster way to compute the entire box at once. + * @param box Bounding box, in { x:.., y:..., w:..., h:... } format. + * @returns {Array} + */ + this.boundingBoxIntersection = function(box) { + return this.boxIntersection(box.x, box.y, box.w, box.h); + }; + }, + + /* + Arc Segment. You need to supply: + + r - radius + cx - center x for the arc + cy - center y for the arc + ac - whether the arc is anticlockwise or not. default is clockwise. + + and then either: + + startAngle - startAngle for the arc. + endAngle - endAngle for the arc. + + or: + + x1 - x for start point + y1 - y for start point + x2 - x for end point + y2 - y for end point + + */ + Arc: function (params) { + var _super = _jp.Segments.AbstractSegment.apply(this, arguments), + _calcAngle = function (_x, _y) { + return _jg.theta([params.cx, params.cy], [_x, _y]); + }, + _calcAngleForLocation = function (segment, location) { + if (segment.anticlockwise) { + var sa = segment.startAngle < segment.endAngle ? segment.startAngle + TWO_PI : segment.startAngle, + s = Math.abs(sa - segment.endAngle); + return sa - (s * location); + } + else { + var ea = segment.endAngle < segment.startAngle ? segment.endAngle + TWO_PI : segment.endAngle, + ss = Math.abs(ea - segment.startAngle); + + return segment.startAngle + (ss * location); + } + }, + TWO_PI = 2 * Math.PI; + + this.radius = params.r; + this.anticlockwise = params.ac; + this.type = "Arc"; + + if (params.startAngle && params.endAngle) { + this.startAngle = params.startAngle; + this.endAngle = params.endAngle; + this.x1 = params.cx + (this.radius * Math.cos(params.startAngle)); + this.y1 = params.cy + (this.radius * Math.sin(params.startAngle)); + this.x2 = params.cx + (this.radius * Math.cos(params.endAngle)); + this.y2 = params.cy + (this.radius * Math.sin(params.endAngle)); + } + else { + this.startAngle = _calcAngle(params.x1, params.y1); + this.endAngle = _calcAngle(params.x2, params.y2); + this.x1 = params.x1; + this.y1 = params.y1; + this.x2 = params.x2; + this.y2 = params.y2; + } + + if (this.endAngle < 0) { + this.endAngle += TWO_PI; + } + if (this.startAngle < 0) { + this.startAngle += TWO_PI; + } + + // segment is used by vml + //this.segment = _jg.quadrant([this.x1, this.y1], [this.x2, this.y2]); + + // we now have startAngle and endAngle as positive numbers, meaning the + // absolute difference (|d|) between them is the sweep (s) of this arc, unless the + // arc is 'anticlockwise' in which case 's' is given by 2PI - |d|. + + var ea = this.endAngle < this.startAngle ? this.endAngle + TWO_PI : this.endAngle; + this.sweep = Math.abs(ea - this.startAngle); + if (this.anticlockwise) { + this.sweep = TWO_PI - this.sweep; + } + var circumference = 2 * Math.PI * this.radius, + frac = this.sweep / TWO_PI, + length = circumference * frac; + + this.getLength = function () { + return length; + }; + + this.getBounds = function () { + return { + minX: params.cx - params.r, + maxX: params.cx + params.r, + minY: params.cy - params.r, + maxY: params.cy + params.r + }; + }; + + var VERY_SMALL_VALUE = 0.0000000001, + gentleRound = function (n) { + var f = Math.floor(n), r = Math.ceil(n); + if (n - f < VERY_SMALL_VALUE) { + return f; + } + else if (r - n < VERY_SMALL_VALUE) { + return r; + } + return n; + }; + + /** + * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from + * 0 to 1 inclusive. + */ + this.pointOnPath = function (location, absolute) { + + if (location === 0) { + return { x: this.x1, y: this.y1, theta: this.startAngle }; + } + else if (location === 1) { + return { x: this.x2, y: this.y2, theta: this.endAngle }; + } + + if (absolute) { + location = location / length; + } + + var angle = _calcAngleForLocation(this, location), + _x = params.cx + (params.r * Math.cos(angle)), + _y = params.cy + (params.r * Math.sin(angle)); + + return { x: gentleRound(_x), y: gentleRound(_y), theta: angle }; + }; + + /** + * returns the gradient of the segment at the given point. + */ + this.gradientAtPoint = function (location, absolute) { + var p = this.pointOnPath(location, absolute); + var m = _jg.normal([ params.cx, params.cy ], [p.x, p.y ]); + if (!this.anticlockwise && (m === Infinity || m === -Infinity)) { + m *= -1; + } + return m; + }; + + this.pointAlongPathFrom = function (location, distance, absolute) { + var p = this.pointOnPath(location, absolute), + arcSpan = distance / circumference * 2 * Math.PI, + dir = this.anticlockwise ? -1 : 1, + startAngle = p.theta + (dir * arcSpan), + startX = params.cx + (this.radius * Math.cos(startAngle)), + startY = params.cy + (this.radius * Math.sin(startAngle)); + + return {x: startX, y: startY}; + }; + + // TODO: lineIntersection + }, + + Bezier: function (params) { + this.curve = [ + { x: params.x1, y: params.y1}, + { x: params.cp1x, y: params.cp1y }, + { x: params.cp2x, y: params.cp2y }, + { x: params.x2, y: params.y2 } + ]; + + var _super = _jp.Segments.AbstractSegment.apply(this, arguments); + // although this is not a strictly rigorous determination of bounds + // of a bezier curve, it works for the types of curves that this segment + // type produces. + this.bounds = { + minX: Math.min(params.x1, params.x2, params.cp1x, params.cp2x), + minY: Math.min(params.y1, params.y2, params.cp1y, params.cp2y), + maxX: Math.max(params.x1, params.x2, params.cp1x, params.cp2x), + maxY: Math.max(params.y1, params.y2, params.cp1y, params.cp2y) + }; + + this.type = "Bezier"; + + var _translateLocation = function (_curve, location, absolute) { + if (absolute) { + location = root.jsBezier.locationAlongCurveFrom(_curve, location > 0 ? 0 : 1, location); + } + + return location; + }; + + /** + * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from + * 0 to 1 inclusive. + */ + this.pointOnPath = function (location, absolute) { + location = _translateLocation(this.curve, location, absolute); + return root.jsBezier.pointOnCurve(this.curve, location); + }; + + /** + * returns the gradient of the segment at the given point. + */ + this.gradientAtPoint = function (location, absolute) { + location = _translateLocation(this.curve, location, absolute); + return root.jsBezier.gradientAtPoint(this.curve, location); + }; + + this.pointAlongPathFrom = function (location, distance, absolute) { + location = _translateLocation(this.curve, location, absolute); + return root.jsBezier.pointAlongCurveFrom(this.curve, location, distance); + }; + + this.getLength = function () { + return root.jsBezier.getLength(this.curve); + }; + + this.getBounds = function () { + return this.bounds; + }; + + this.findClosestPointOnPath = function (x, y) { + var p = root.jsBezier.nearestPointOnCurve({x:x,y:y}, this.curve); + return { + d:Math.sqrt(Math.pow(p.point.x - x, 2) + Math.pow(p.point.y - y, 2)), + x:p.point.x, + y:p.point.y, + l:1 - p.location, + s:this + }; + }; + + this.lineIntersection = function(x1, y1, x2, y2) { + return root.jsBezier.lineIntersection(x1, y1, x2, y2, this.curve); + }; + } + }; + + _jp.SegmentRenderer = { + getPath: function (segment, isFirstSegment) { + return ({ + "Straight": function (isFirstSegment) { + var d = segment.getCoordinates(); + return (isFirstSegment ? "M " + d.x1 + " " + d.y1 + " " : "") + "L " + d.x2 + " " + d.y2; + }, + "Bezier": function (isFirstSegment) { + var d = segment.params; + return (isFirstSegment ? "M " + d.x2 + " " + d.y2 + " " : "") + + "C " + d.cp2x + " " + d.cp2y + " " + d.cp1x + " " + d.cp1y + " " + d.x1 + " " + d.y1; + }, + "Arc": function (isFirstSegment) { + var d = segment.params, + laf = segment.sweep > Math.PI ? 1 : 0, + sf = segment.anticlockwise ? 0 : 1; + + return (isFirstSegment ? "M" + segment.x1 + " " + segment.y1 + " " : "") + "A " + segment.radius + " " + d.r + " 0 " + laf + "," + sf + " " + segment.x2 + " " + segment.y2; + } + })[segment.type](isFirstSegment); + } + }; + + /* + Class: UIComponent + Superclass for Connector and AbstractEndpoint. + */ + var AbstractComponent = function () { + this.resetBounds = function () { + this.bounds = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; + }; + this.resetBounds(); + }; + + /* + * Class: Connector + * Superclass for all Connectors; here is where Segments are managed. This is exposed on jsPlumb just so it + * can be accessed from other files. You should not try to instantiate one of these directly. + * + * When this class is asked for a pointOnPath, or gradient etc, it must first figure out which segment to dispatch + * that request to. This is done by keeping track of the total connector length as segments are added, and also + * their cumulative ratios to the total length. Then when the right segment is found it is a simple case of dispatching + * the request to it (and adjusting 'location' so that it is relative to the beginning of that segment.) + */ + _jp.Connectors.AbstractConnector = function (params) { + + AbstractComponent.apply(this, arguments); + + var segments = [], + totalLength = 0, + segmentProportions = [], + segmentProportionalLengths = [], + stub = params.stub || 0, + sourceStub = _ju.isArray(stub) ? stub[0] : stub, + targetStub = _ju.isArray(stub) ? stub[1] : stub, + gap = params.gap || 0, + sourceGap = _ju.isArray(gap) ? gap[0] : gap, + targetGap = _ju.isArray(gap) ? gap[1] : gap, + userProvidedSegments = null, + paintInfo = null; + + this.getPathData = function() { + var p = ""; + for (var i = 0; i < segments.length; i++) { + p += _jp.SegmentRenderer.getPath(segments[i], i === 0); + p += " "; + } + return p; + }; + + /** + * Function: findSegmentForPoint + * Returns the segment that is closest to the given [x,y], + * null if nothing found. This function returns a JS + * object with: + * + * d - distance from segment + * l - proportional location in segment + * x - x point on the segment + * y - y point on the segment + * s - the segment itself. + * connectorLocation - the location on the connector of the point, expressed as a decimal between 0 and 1 inclusive. + */ + this.findSegmentForPoint = function (x, y) { + var out = { d: Infinity, s: null, x: null, y: null, l: null }; + for (var i = 0; i < segments.length; i++) { + var _s = segments[i].findClosestPointOnPath(x, y); + if (_s.d < out.d) { + out.d = _s.d; + out.l = _s.l; + out.x = _s.x; + out.y = _s.y; + out.s = segments[i]; + out.x1 = _s.x1; + out.x2 = _s.x2; + out.y1 = _s.y1; + out.y2 = _s.y2; + out.index = i; + out.connectorLocation = segmentProportions[i][0] + (_s.l * (segmentProportions[i][1] - segmentProportions[i][0])); + } + } + + return out; + }; + + this.lineIntersection = function(x1, y1, x2, y2) { + var out = []; + for (var i = 0; i < segments.length; i++) { + out.push.apply(out, segments[i].lineIntersection(x1, y1, x2, y2)); + } + return out; + }; + + this.boxIntersection = function(x, y, w, h) { + var out = []; + for (var i = 0; i < segments.length; i++) { + out.push.apply(out, segments[i].boxIntersection(x, y, w, h)); + } + return out; + }; + + this.boundingBoxIntersection = function(box) { + var out = []; + for (var i = 0; i < segments.length; i++) { + out.push.apply(out, segments[i].boundingBoxIntersection(box)); + } + return out; + }; + + var _updateSegmentProportions = function () { + var curLoc = 0; + for (var i = 0; i < segments.length; i++) { + var sl = segments[i].getLength(); + segmentProportionalLengths[i] = sl / totalLength; + segmentProportions[i] = [curLoc, (curLoc += (sl / totalLength)) ]; + } + }, + + /** + * returns [segment, proportion of travel in segment, segment index] for the segment + * that contains the point which is 'location' distance along the entire path, where + * 'location' is a decimal between 0 and 1 inclusive. in this connector type, paths + * are made up of a list of segments, each of which contributes some fraction to + * the total length. + * From 1.3.10 this also supports the 'absolute' property, which lets us specify a location + * as the absolute distance in pixels, rather than a proportion of the total path. + */ + _findSegmentForLocation = function (location, absolute) { + if (absolute) { + location = location > 0 ? location / totalLength : (totalLength + location) / totalLength; + } + var idx = segmentProportions.length - 1, inSegmentProportion = 1; + for (var i = 0; i < segmentProportions.length; i++) { + if (segmentProportions[i][1] >= location) { + idx = i; + // todo is this correct for all connector path types? + inSegmentProportion = location === 1 ? 1 : location === 0 ? 0 : (location - segmentProportions[i][0]) / segmentProportionalLengths[i]; + break; + } + } + return { segment: segments[idx], proportion: inSegmentProportion, index: idx }; + }, + _addSegment = function (conn, type, params) { + if (params.x1 === params.x2 && params.y1 === params.y2) { + return; + } + var s = new _jp.Segments[type](params); + segments.push(s); + totalLength += s.getLength(); + conn.updateBounds(s); + }, + _clearSegments = function () { + totalLength = segments.length = segmentProportions.length = segmentProportionalLengths.length = 0; + }; + + this.setSegments = function (_segs) { + userProvidedSegments = []; + totalLength = 0; + for (var i = 0; i < _segs.length; i++) { + userProvidedSegments.push(_segs[i]); + totalLength += _segs[i].getLength(); + } + }; + + this.getLength = function() { + return totalLength; + }; + + var _prepareCompute = function (params) { + this.strokeWidth = params.strokeWidth; + var segment = _jg.quadrant(params.sourcePos, params.targetPos), + swapX = params.targetPos[0] < params.sourcePos[0], + swapY = params.targetPos[1] < params.sourcePos[1], + lw = params.strokeWidth || 1, + so = params.sourceEndpoint.anchor.getOrientation(params.sourceEndpoint), + to = params.targetEndpoint.anchor.getOrientation(params.targetEndpoint), + x = swapX ? params.targetPos[0] : params.sourcePos[0], + y = swapY ? params.targetPos[1] : params.sourcePos[1], + w = Math.abs(params.targetPos[0] - params.sourcePos[0]), + h = Math.abs(params.targetPos[1] - params.sourcePos[1]); + + // if either anchor does not have an orientation set, we derive one from their relative + // positions. we fix the axis to be the one in which the two elements are further apart, and + // point each anchor at the other element. this is also used when dragging a new connection. + if (so[0] === 0 && so[1] === 0 || to[0] === 0 && to[1] === 0) { + var index = w > h ? 0 : 1, oIndex = [1, 0][index]; + so = []; + to = []; + so[index] = params.sourcePos[index] > params.targetPos[index] ? -1 : 1; + to[index] = params.sourcePos[index] > params.targetPos[index] ? 1 : -1; + so[oIndex] = 0; + to[oIndex] = 0; + } + + var sx = swapX ? w + (sourceGap * so[0]) : sourceGap * so[0], + sy = swapY ? h + (sourceGap * so[1]) : sourceGap * so[1], + tx = swapX ? targetGap * to[0] : w + (targetGap * to[0]), + ty = swapY ? targetGap * to[1] : h + (targetGap * to[1]), + oProduct = ((so[0] * to[0]) + (so[1] * to[1])); + + var result = { + sx: sx, sy: sy, tx: tx, ty: ty, lw: lw, + xSpan: Math.abs(tx - sx), + ySpan: Math.abs(ty - sy), + mx: (sx + tx) / 2, + my: (sy + ty) / 2, + so: so, to: to, x: x, y: y, w: w, h: h, + segment: segment, + startStubX: sx + (so[0] * sourceStub), + startStubY: sy + (so[1] * sourceStub), + endStubX: tx + (to[0] * targetStub), + endStubY: ty + (to[1] * targetStub), + isXGreaterThanStubTimes2: Math.abs(sx - tx) > (sourceStub + targetStub), + isYGreaterThanStubTimes2: Math.abs(sy - ty) > (sourceStub + targetStub), + opposite: oProduct === -1, + perpendicular: oProduct === 0, + orthogonal: oProduct === 1, + sourceAxis: so[0] === 0 ? "y" : "x", + points: [x, y, w, h, sx, sy, tx, ty ], + stubs:[sourceStub, targetStub] + }; + result.anchorOrientation = result.opposite ? "opposite" : result.orthogonal ? "orthogonal" : "perpendicular"; + return result; + }; + + this.getSegments = function () { + return segments; + }; + + this.updateBounds = function (segment) { + var segBounds = segment.getBounds(); + this.bounds.minX = Math.min(this.bounds.minX, segBounds.minX); + this.bounds.maxX = Math.max(this.bounds.maxX, segBounds.maxX); + this.bounds.minY = Math.min(this.bounds.minY, segBounds.minY); + this.bounds.maxY = Math.max(this.bounds.maxY, segBounds.maxY); + }; + + var dumpSegmentsToConsole = function () { + console.log("SEGMENTS:"); + for (var i = 0; i < segments.length; i++) { + console.log(segments[i].type, segments[i].getLength(), segmentProportions[i]); + } + }; + + this.pointOnPath = function (location, absolute) { + var seg = _findSegmentForLocation(location, absolute); + return seg.segment && seg.segment.pointOnPath(seg.proportion, false) || [0, 0]; + }; + + this.gradientAtPoint = function (location, absolute) { + var seg = _findSegmentForLocation(location, absolute); + return seg.segment && seg.segment.gradientAtPoint(seg.proportion, false) || 0; + }; + + this.pointAlongPathFrom = function (location, distance, absolute) { + var seg = _findSegmentForLocation(location, absolute); + // TODO what happens if this crosses to the next segment? + return seg.segment && seg.segment.pointAlongPathFrom(seg.proportion, distance, false) || [0, 0]; + }; + + this.compute = function (params) { + paintInfo = _prepareCompute.call(this, params); + + _clearSegments(); + this._compute(paintInfo, params); + this.x = paintInfo.points[0]; + this.y = paintInfo.points[1]; + this.w = paintInfo.points[2]; + this.h = paintInfo.points[3]; + this.segment = paintInfo.segment; + _updateSegmentProportions(); + }; + + return { + addSegment: _addSegment, + prepareCompute: _prepareCompute, + sourceStub: sourceStub, + targetStub: targetStub, + maxStub: Math.max(sourceStub, targetStub), + sourceGap: sourceGap, + targetGap: targetGap, + maxGap: Math.max(sourceGap, targetGap) + }; + }; + _ju.extend(_jp.Connectors.AbstractConnector, AbstractComponent); + + + // ********************************* END OF CONNECTOR TYPES ******************************************************************* + + // ********************************* ENDPOINT TYPES ******************************************************************* + + _jp.Endpoints.AbstractEndpoint = function (params) { + AbstractComponent.apply(this, arguments); + var compute = this.compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + var out = this._compute.apply(this, arguments); + this.x = out[0]; + this.y = out[1]; + this.w = out[2]; + this.h = out[3]; + this.bounds.minX = this.x; + this.bounds.minY = this.y; + this.bounds.maxX = this.x + this.w; + this.bounds.maxY = this.y + this.h; + return out; + }; + return { + compute: compute, + cssClass: params.cssClass + }; + }; + _ju.extend(_jp.Endpoints.AbstractEndpoint, AbstractComponent); + + /** + * Class: Endpoints.Dot + * A round endpoint, with default radius 10 pixels. + */ + + /** + * Function: Constructor + * + * Parameters: + * + * radius - radius of the endpoint. defaults to 10 pixels. + */ + _jp.Endpoints.Dot = function (params) { + this.type = "Dot"; + var _super = _jp.Endpoints.AbstractEndpoint.apply(this, arguments); + params = params || {}; + this.radius = params.radius || 10; + this.defaultOffset = 0.5 * this.radius; + this.defaultInnerRadius = this.radius / 3; + + this._compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + this.radius = endpointStyle.radius || this.radius; + var x = anchorPoint[0] - this.radius, + y = anchorPoint[1] - this.radius, + w = this.radius * 2, + h = this.radius * 2; + + if (endpointStyle.stroke) { + var lw = endpointStyle.strokeWidth || 1; + x -= lw; + y -= lw; + w += (lw * 2); + h += (lw * 2); + } + return [ x, y, w, h, this.radius ]; + }; + }; + _ju.extend(_jp.Endpoints.Dot, _jp.Endpoints.AbstractEndpoint); + + _jp.Endpoints.Rectangle = function (params) { + this.type = "Rectangle"; + var _super = _jp.Endpoints.AbstractEndpoint.apply(this, arguments); + params = params || {}; + this.width = params.width || 20; + this.height = params.height || 20; + + this._compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + var width = endpointStyle.width || this.width, + height = endpointStyle.height || this.height, + x = anchorPoint[0] - (width / 2), + y = anchorPoint[1] - (height / 2); + + return [ x, y, width, height]; + }; + }; + _ju.extend(_jp.Endpoints.Rectangle, _jp.Endpoints.AbstractEndpoint); + + var DOMElementEndpoint = function (params) { + _jp.jsPlumbUIComponent.apply(this, arguments); + this._jsPlumb.displayElements = []; + }; + _ju.extend(DOMElementEndpoint, _jp.jsPlumbUIComponent, { + getDisplayElements: function () { + return this._jsPlumb.displayElements; + }, + appendDisplayElement: function (el) { + this._jsPlumb.displayElements.push(el); + } + }); + + /** + * Class: Endpoints.Image + * Draws an image as the Endpoint. + */ + /** + * Function: Constructor + * + * Parameters: + * + * src - location of the image to use. + + TODO: multiple references to self. not sure quite how to get rid of them entirely. perhaps self = null in the cleanup + function will suffice + + TODO this class still might leak memory. + + */ + _jp.Endpoints.Image = function (params) { + + this.type = "Image"; + DOMElementEndpoint.apply(this, arguments); + _jp.Endpoints.AbstractEndpoint.apply(this, arguments); + + var _onload = params.onload, + src = params.src || params.url, + clazz = params.cssClass ? " " + params.cssClass : ""; + + this._jsPlumb.img = new Image(); + this._jsPlumb.ready = false; + this._jsPlumb.initialized = false; + this._jsPlumb.deleted = false; + this._jsPlumb.widthToUse = params.width; + this._jsPlumb.heightToUse = params.height; + this._jsPlumb.endpoint = params.endpoint; + + this._jsPlumb.img.onload = function () { + if (this._jsPlumb != null) { + this._jsPlumb.ready = true; + this._jsPlumb.widthToUse = this._jsPlumb.widthToUse || this._jsPlumb.img.width; + this._jsPlumb.heightToUse = this._jsPlumb.heightToUse || this._jsPlumb.img.height; + if (_onload) { + _onload(this); + } + } + }.bind(this); + + /* + Function: setImage + Sets the Image to use in this Endpoint. + + Parameters: + img - may be a URL or an Image object + onload - optional; a callback to execute once the image has loaded. + */ + this._jsPlumb.endpoint.setImage = function (_img, onload) { + var s = _img.constructor === String ? _img : _img.src; + _onload = onload; + this._jsPlumb.img.src = s; + + if (this.canvas != null) { + this.canvas.setAttribute("src", this._jsPlumb.img.src); + } + }.bind(this); + + this._jsPlumb.endpoint.setImage(src, _onload); + this._compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + this.anchorPoint = anchorPoint; + if (this._jsPlumb.ready) { + return [anchorPoint[0] - this._jsPlumb.widthToUse / 2, anchorPoint[1] - this._jsPlumb.heightToUse / 2, + this._jsPlumb.widthToUse, this._jsPlumb.heightToUse]; + } + else { + return [0, 0, 0, 0]; + } + }; + + this.canvas = _jp.createElement("img", { + position:"absolute", + margin:0, + padding:0, + outline:0 + }, this._jsPlumb.instance.endpointClass + clazz); + + if (this._jsPlumb.widthToUse) { + this.canvas.setAttribute("width", this._jsPlumb.widthToUse); + } + if (this._jsPlumb.heightToUse) { + this.canvas.setAttribute("height", this._jsPlumb.heightToUse); + } + this._jsPlumb.instance.appendElement(this.canvas); + + this.actuallyPaint = function (d, style, anchor) { + if (!this._jsPlumb.deleted) { + if (!this._jsPlumb.initialized) { + this.canvas.setAttribute("src", this._jsPlumb.img.src); + this.appendDisplayElement(this.canvas); + this._jsPlumb.initialized = true; + } + var x = this.anchorPoint[0] - (this._jsPlumb.widthToUse / 2), + y = this.anchorPoint[1] - (this._jsPlumb.heightToUse / 2); + _ju.sizeElement(this.canvas, x, y, this._jsPlumb.widthToUse, this._jsPlumb.heightToUse); + } + }; + + this.paint = function (style, anchor) { + if (this._jsPlumb != null) { // may have been deleted + if (this._jsPlumb.ready) { + this.actuallyPaint(style, anchor); + } + else { + root.setTimeout(function () { + this.paint(style, anchor); + }.bind(this), 200); + } + } + }; + }; + _ju.extend(_jp.Endpoints.Image, [ DOMElementEndpoint, _jp.Endpoints.AbstractEndpoint ], { + cleanup: function (force) { + if (force) { + this._jsPlumb.deleted = true; + if (this.canvas) { + this.canvas.parentNode.removeChild(this.canvas); + } + this.canvas = null; + } + } + }); + + /* + * Class: Endpoints.Blank + * An Endpoint that paints nothing (visible) on the screen. Supports cssClass and hoverClass parameters like all Endpoints. + */ + _jp.Endpoints.Blank = function (params) { + var _super = _jp.Endpoints.AbstractEndpoint.apply(this, arguments); + this.type = "Blank"; + DOMElementEndpoint.apply(this, arguments); + this._compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + return [anchorPoint[0], anchorPoint[1], 10, 0]; + }; + + var clazz = params.cssClass ? " " + params.cssClass : ""; + + this.canvas = _jp.createElement("div", { + display: "block", + width: "1px", + height: "1px", + background: "transparent", + position: "absolute" + }, this._jsPlumb.instance.endpointClass + clazz); + + this._jsPlumb.instance.appendElement(this.canvas); + + this.paint = function (style, anchor) { + _ju.sizeElement(this.canvas, this.x, this.y, this.w, this.h); + }; + }; + _ju.extend(_jp.Endpoints.Blank, [_jp.Endpoints.AbstractEndpoint, DOMElementEndpoint], { + cleanup: function () { + if (this.canvas && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + } + }); + + /* + * Class: Endpoints.Triangle + * A triangular Endpoint. + */ + /* + * Function: Constructor + * + * Parameters: + * + * width width of the triangle's base. defaults to 55 pixels. + * height height of the triangle from base to apex. defaults to 55 pixels. + */ + _jp.Endpoints.Triangle = function (params) { + this.type = "Triangle"; + _jp.Endpoints.AbstractEndpoint.apply(this, arguments); + var self = this; + params = params || { }; + params.width = params.width || 55; + params.height = params.height || 55; + this.width = params.width; + this.height = params.height; + this._compute = function (anchorPoint, orientation, endpointStyle, connectorPaintStyle) { + var width = endpointStyle.width || self.width, + height = endpointStyle.height || self.height, + x = anchorPoint[0] - (width / 2), + y = anchorPoint[1] - (height / 2); + return [ x, y, width, height ]; + }; + }; +// ********************************* END OF ENDPOINT TYPES ******************************************************************* + + +// ********************************* OVERLAY DEFINITIONS *********************************************************************** + + var AbstractOverlay = _jp.Overlays.AbstractOverlay = function (params) { + this.visible = true; + this.isAppendedAtTopLevel = true; + this.component = params.component; + this.loc = params.location == null ? 0.5 : params.location; + this.endpointLoc = params.endpointLocation == null ? [ 0.5, 0.5] : params.endpointLocation; + this.visible = params.visible !== false; + }; + AbstractOverlay.prototype = { + cleanup: function (force) { + if (force) { + this.component = null; + this.canvas = null; + this.endpointLoc = null; + } + }, + reattach:function(instance, component) { }, + setVisible: function (val) { + this.visible = val; + this.component.repaint(); + }, + isVisible: function () { + return this.visible; + }, + hide: function () { + this.setVisible(false); + }, + show: function () { + this.setVisible(true); + }, + incrementLocation: function (amount) { + this.loc += amount; + this.component.repaint(); + }, + setLocation: function (l) { + this.loc = l; + this.component.repaint(); + }, + getLocation: function () { + return this.loc; + }, + updateFrom:function() { } + }; + + + /* + * Class: Overlays.Arrow + * + * An arrow overlay, defined by four points: the head, the two sides of the tail, and a 'foldback' point at some distance along the length + * of the arrow that lines from each tail point converge into. The foldback point is defined using a decimal that indicates some fraction + * of the length of the arrow and has a default value of 0.623. A foldback point value of 1 would mean that the arrow had a straight line + * across the tail. + */ + /* + * @constructor + * + * @param {Object} params Constructor params. + * @param {Number} [params.length] Distance in pixels from head to tail baseline. default 20. + * @param {Number} [params.width] Width in pixels of the tail baseline. default 20. + * @param {String} [params.fill] Style to use when filling the arrow. defaults to "black". + * @param {String} [params.stroke] Style to use when stroking the arrow. defaults to null, which means the arrow is not stroked. + * @param {Number} [params.stroke-width] Line width to use when stroking the arrow. defaults to 1, but only used if stroke is not null. + * @param {Number} [params.foldback] Distance (as a decimal from 0 to 1 inclusive) along the length of the arrow marking the point the tail points should fold back to. defaults to 0.623. + * @param {Number} [params.location] Distance (as a decimal from 0 to 1 inclusive) marking where the arrow should sit on the connector. defaults to 0.5. + * @param {NUmber} [params.direction] Indicates the direction the arrow points in. valid values are -1 and 1; 1 is default. + */ + _jp.Overlays.Arrow = function (params) { + this.type = "Arrow"; + AbstractOverlay.apply(this, arguments); + this.isAppendedAtTopLevel = false; + params = params || {}; + var self = this; + + this.length = params.length || 20; + this.width = params.width || 20; + this.id = params.id; + this.direction = (params.direction || 1) < 0 ? -1 : 1; + var paintStyle = params.paintStyle || { "stroke-width": 1 }, + // how far along the arrow the lines folding back in come to. default is 62.3%. + foldback = params.foldback || 0.623; + + this.computeMaxSize = function () { + return self.width * 1.5; + }; + + this.elementCreated = function(p, component) { + this.path = p; + if (params.events) { + for (var i in params.events) { + _jp.on(p, i, params.events[i]); + } + } + }; + + this.draw = function (component, currentConnectionPaintStyle) { + + var hxy, mid, txy, tail, cxy; + if (component.pointAlongPathFrom) { + + if (_ju.isString(this.loc) || this.loc > 1 || this.loc < 0) { + var l = parseInt(this.loc, 10), + fromLoc = this.loc < 0 ? 1 : 0; + hxy = component.pointAlongPathFrom(fromLoc, l, false); + mid = component.pointAlongPathFrom(fromLoc, l - (this.direction * this.length / 2), false); + txy = _jg.pointOnLine(hxy, mid, this.length); + } + else if (this.loc === 1) { + hxy = component.pointOnPath(this.loc); + mid = component.pointAlongPathFrom(this.loc, -(this.length)); + txy = _jg.pointOnLine(hxy, mid, this.length); + + if (this.direction === -1) { + var _ = txy; + txy = hxy; + hxy = _; + } + } + else if (this.loc === 0) { + txy = component.pointOnPath(this.loc); + mid = component.pointAlongPathFrom(this.loc, this.length); + hxy = _jg.pointOnLine(txy, mid, this.length); + if (this.direction === -1) { + var __ = txy; + txy = hxy; + hxy = __; + } + } + else { + hxy = component.pointAlongPathFrom(this.loc, this.direction * this.length / 2); + mid = component.pointOnPath(this.loc); + txy = _jg.pointOnLine(hxy, mid, this.length); + } + + tail = _jg.perpendicularLineTo(hxy, txy, this.width); + cxy = _jg.pointOnLine(hxy, txy, foldback * this.length); + + var d = { hxy: hxy, tail: tail, cxy: cxy }, + stroke = paintStyle.stroke || currentConnectionPaintStyle.stroke, + fill = paintStyle.fill || currentConnectionPaintStyle.stroke, + lineWidth = paintStyle.strokeWidth || currentConnectionPaintStyle.strokeWidth; + + return { + component: component, + d: d, + "stroke-width": lineWidth, + stroke: stroke, + fill: fill, + minX: Math.min(hxy.x, tail[0].x, tail[1].x), + maxX: Math.max(hxy.x, tail[0].x, tail[1].x), + minY: Math.min(hxy.y, tail[0].y, tail[1].y), + maxY: Math.max(hxy.y, tail[0].y, tail[1].y) + }; + } + else { + return {component: component, minX: 0, maxX: 0, minY: 0, maxY: 0}; + } + }; + }; + _ju.extend(_jp.Overlays.Arrow, AbstractOverlay, { + updateFrom:function(d) { + this.length = d.length || this.length; + this.width = d.width|| this.width; + this.direction = d.direction != null ? d.direction : this.direction; + this.foldback = d.foldback|| this.foldback; + }, + cleanup:function() { + if (this.path && this.canvas) { + this.canvas.removeChild(this.path); + } + } + }); + + /* + * Class: Overlays.PlainArrow + * + * A basic arrow. This is in fact just one instance of the more generic case in which the tail folds back on itself to some + * point along the length of the arrow: in this case, that foldback point is the full length of the arrow. so it just does + * a 'call' to Arrow with foldback set appropriately. + */ + /* + * Function: Constructor + * See <Overlays.Arrow> for allowed parameters for this overlay. + */ + _jp.Overlays.PlainArrow = function (params) { + params = params || {}; + var p = _jp.extend(params, {foldback: 1}); + _jp.Overlays.Arrow.call(this, p); + this.type = "PlainArrow"; + }; + _ju.extend(_jp.Overlays.PlainArrow, _jp.Overlays.Arrow); + + /* + * Class: Overlays.Diamond + * + * A diamond. Like PlainArrow, this is a concrete case of the more generic case of the tail points converging on some point...it just + * happens that in this case, that point is greater than the length of the the arrow. + * + * this could probably do with some help with positioning...due to the way it reuses the Arrow paint code, what Arrow thinks is the + * center is actually 1/4 of the way along for this guy. but we don't have any knowledge of pixels at this point, so we're kind of + * stuck when it comes to helping out the Arrow class. possibly we could pass in a 'transpose' parameter or something. the value + * would be -l/4 in this case - move along one quarter of the total length. + */ + /* + * Function: Constructor + * See <Overlays.Arrow> for allowed parameters for this overlay. + */ + _jp.Overlays.Diamond = function (params) { + params = params || {}; + var l = params.length || 40, + p = _jp.extend(params, {length: l / 2, foldback: 2}); + _jp.Overlays.Arrow.call(this, p); + this.type = "Diamond"; + }; + _ju.extend(_jp.Overlays.Diamond, _jp.Overlays.Arrow); + + var _getDimensions = function (component, forceRefresh) { + if (component._jsPlumb.cachedDimensions == null || forceRefresh) { + component._jsPlumb.cachedDimensions = component.getDimensions(); + } + return component._jsPlumb.cachedDimensions; + }; + + // abstract superclass for overlays that add an element to the DOM. + var AbstractDOMOverlay = function (params) { + _jp.jsPlumbUIComponent.apply(this, arguments); + AbstractOverlay.apply(this, arguments); + + // hand off fired events to associated component. + var _f = this.fire; + this.fire = function () { + _f.apply(this, arguments); + if (this.component) { + this.component.fire.apply(this.component, arguments); + } + }; + + this.detached=false; + this.id = params.id; + this._jsPlumb.div = null; + this._jsPlumb.initialised = false; + this._jsPlumb.component = params.component; + this._jsPlumb.cachedDimensions = null; + this._jsPlumb.create = params.create; + this._jsPlumb.initiallyInvisible = params.visible === false; + + this.getElement = function () { + if (this._jsPlumb.div == null) { + var div = this._jsPlumb.div = _jp.getElement(this._jsPlumb.create(this._jsPlumb.component)); + div.style.position = "absolute"; + jsPlumb.addClass(div, this._jsPlumb.instance.overlayClass + " " + + (this.cssClass ? this.cssClass : + params.cssClass ? params.cssClass : "")); + this._jsPlumb.instance.appendElement(div); + this._jsPlumb.instance.getId(div); + this.canvas = div; + + // in IE the top left corner is what it placed at the desired location. This will not + // be fixed. IE8 is not going to be supported for much longer. + var ts = "translate(-50%, -50%)"; + div.style.webkitTransform = ts; + div.style.mozTransform = ts; + div.style.msTransform = ts; + div.style.oTransform = ts; + div.style.transform = ts; + + // write the related component into the created element + div._jsPlumb = this; + + if (params.visible === false) { + div.style.display = "none"; + } + } + return this._jsPlumb.div; + }; + + this.draw = function (component, currentConnectionPaintStyle, absolutePosition) { + var td = _getDimensions(this); + if (td != null && td.length === 2) { + var cxy = { x: 0, y: 0 }; + + // absolutePosition would have been set by a call to connection.setAbsoluteOverlayPosition. + if (absolutePosition) { + cxy = { x: absolutePosition[0], y: absolutePosition[1] }; + } + else if (component.pointOnPath) { + var loc = this.loc, absolute = false; + if (_ju.isString(this.loc) || this.loc < 0 || this.loc > 1) { + loc = parseInt(this.loc, 10); + absolute = true; + } + cxy = component.pointOnPath(loc, absolute); // a connection + } + else { + var locToUse = this.loc.constructor === Array ? this.loc : this.endpointLoc; + cxy = { x: locToUse[0] * component.w, + y: locToUse[1] * component.h }; + } + + var minx = cxy.x - (td[0] / 2), + miny = cxy.y - (td[1] / 2); + + return { + component: component, + d: { minx: minx, miny: miny, td: td, cxy: cxy }, + minX: minx, + maxX: minx + td[0], + minY: miny, + maxY: miny + td[1] + }; + } + else { + return {minX: 0, maxX: 0, minY: 0, maxY: 0}; + } + }; + }; + _ju.extend(AbstractDOMOverlay, [_jp.jsPlumbUIComponent, AbstractOverlay], { + getDimensions: function () { + return [1,1]; + }, + setVisible: function (state) { + if (this._jsPlumb.div) { + this._jsPlumb.div.style.display = state ? "block" : "none"; + // if initially invisible, dimensions are 0,0 and never get updated + if (state && this._jsPlumb.initiallyInvisible) { + _getDimensions(this, true); + this.component.repaint(); + this._jsPlumb.initiallyInvisible = false; + } + } + }, + /* + * Function: clearCachedDimensions + * Clears the cached dimensions for the label. As a performance enhancement, label dimensions are + * cached from 1.3.12 onwards. The cache is cleared when you change the label text, of course, but + * there are other reasons why the text dimensions might change - if you make a change through CSS, for + * example, you might change the font size. in that case you should explicitly call this method. + */ + clearCachedDimensions: function () { + this._jsPlumb.cachedDimensions = null; + }, + cleanup: function (force) { + if (force) { + if (this._jsPlumb.div != null) { + this._jsPlumb.div._jsPlumb = null; + this._jsPlumb.instance.removeElement(this._jsPlumb.div); + } + } + else { + // if not a forced cleanup, just detach child from parent for now. + if (this._jsPlumb && this._jsPlumb.div && this._jsPlumb.div.parentNode) { + this._jsPlumb.div.parentNode.removeChild(this._jsPlumb.div); + } + this.detached = true; + } + + }, + reattach:function(instance, component) { + if (this._jsPlumb.div != null) { + instance.getContainer().appendChild(this._jsPlumb.div); + } + this.detached = false; + }, + computeMaxSize: function () { + var td = _getDimensions(this); + return Math.max(td[0], td[1]); + }, + paint: function (p, containerExtents) { + if (!this._jsPlumb.initialised) { + this.getElement(); + p.component.appendDisplayElement(this._jsPlumb.div); + this._jsPlumb.initialised = true; + if (this.detached) { + this._jsPlumb.div.parentNode.removeChild(this._jsPlumb.div); + } + } + this._jsPlumb.div.style.left = (p.component.x + p.d.minx) + "px"; + this._jsPlumb.div.style.top = (p.component.y + p.d.miny) + "px"; + } + }); + + /* + * Class: Overlays.Custom + * A Custom overlay. You supply a 'create' function which returns some DOM element, and jsPlumb positions it. + * The 'create' function is passed a Connection or Endpoint. + */ + /* + * Function: Constructor + * + * Parameters: + * create - function for jsPlumb to call that returns a DOM element. + * location - distance (as a decimal from 0 to 1 inclusive) marking where the label should sit on the connector. defaults to 0.5. + * id - optional id to use for later retrieval of this overlay. + * + */ + _jp.Overlays.Custom = function (params) { + this.type = "Custom"; + AbstractDOMOverlay.apply(this, arguments); + }; + _ju.extend(_jp.Overlays.Custom, AbstractDOMOverlay); + + _jp.Overlays.GuideLines = function () { + var self = this; + self.length = 50; + self.strokeWidth = 5; + this.type = "GuideLines"; + AbstractOverlay.apply(this, arguments); + _jp.jsPlumbUIComponent.apply(this, arguments); + this.draw = function (connector, currentConnectionPaintStyle) { + + var head = connector.pointAlongPathFrom(self.loc, self.length / 2), + mid = connector.pointOnPath(self.loc), + tail = _jg.pointOnLine(head, mid, self.length), + tailLine = _jg.perpendicularLineTo(head, tail, 40), + headLine = _jg.perpendicularLineTo(tail, head, 20); + + return { + connector: connector, + head: head, + tail: tail, + headLine: headLine, + tailLine: tailLine, + minX: Math.min(head.x, tail.x, headLine[0].x, headLine[1].x), + minY: Math.min(head.y, tail.y, headLine[0].y, headLine[1].y), + maxX: Math.max(head.x, tail.x, headLine[0].x, headLine[1].x), + maxY: Math.max(head.y, tail.y, headLine[0].y, headLine[1].y) + }; + }; + + // this.cleanup = function() { }; // nothing to clean up for GuideLines + }; + + /* + * Class: Overlays.Label + + */ + /* + * Function: Constructor + * + * Parameters: + * cssClass - optional css class string to append to css class. This string is appended "as-is", so you can of course have multiple classes + * defined. This parameter is preferred to using labelStyle, borderWidth and borderStyle. + * label - the label to paint. May be a string or a function that returns a string. Nothing will be painted if your label is null or your + * label function returns null. empty strings _will_ be painted. + * location - distance (as a decimal from 0 to 1 inclusive) marking where the label should sit on the connector. defaults to 0.5. + * id - optional id to use for later retrieval of this overlay. + * + * + */ + _jp.Overlays.Label = function (params) { + this.labelStyle = params.labelStyle; + + var labelWidth = null, labelHeight = null, labelText = null, labelPadding = null; + this.cssClass = this.labelStyle != null ? this.labelStyle.cssClass : null; + var p = _jp.extend({ + create: function () { + return _jp.createElement("div"); + }}, params); + _jp.Overlays.Custom.call(this, p); + this.type = "Label"; + this.label = params.label || ""; + this.labelText = null; + if (this.labelStyle) { + var el = this.getElement(); + this.labelStyle.font = this.labelStyle.font || "12px sans-serif"; + el.style.font = this.labelStyle.font; + el.style.color = this.labelStyle.color || "black"; + if (this.labelStyle.fill) { + el.style.background = this.labelStyle.fill; + } + if (this.labelStyle.borderWidth > 0) { + var dStyle = this.labelStyle.borderStyle ? this.labelStyle.borderStyle : "black"; + el.style.border = this.labelStyle.borderWidth + "px solid " + dStyle; + } + if (this.labelStyle.padding) { + el.style.padding = this.labelStyle.padding; + } + } + + }; + _ju.extend(_jp.Overlays.Label, _jp.Overlays.Custom, { + cleanup: function (force) { + if (force) { + this.div = null; + this.label = null; + this.labelText = null; + this.cssClass = null; + this.labelStyle = null; + } + }, + getLabel: function () { + return this.label; + }, + /* + * Function: setLabel + * sets the label's, um, label. you would think i'd call this function + * 'setText', but you can pass either a Function or a String to this, so + * it makes more sense as 'setLabel'. This uses innerHTML on the label div, so keep + * that in mind if you need escaped HTML. + */ + setLabel: function (l) { + this.label = l; + this.labelText = null; + this.clearCachedDimensions(); + this.update(); + this.component.repaint(); + }, + getDimensions: function () { + this.update(); + return AbstractDOMOverlay.prototype.getDimensions.apply(this, arguments); + }, + update: function () { + if (typeof this.label === "function") { + var lt = this.label(this); + this.getElement().innerHTML = lt.replace(/\r\n/g, "<br/>"); + } + else { + if (this.labelText == null) { + this.labelText = this.label; + this.getElement().innerHTML = this.labelText.replace(/\r\n/g, "<br/>"); + } + } + }, + updateFrom:function(d) { + if(d.label != null){ + this.setLabel(d.label); + } + } + }); + + // ********************************* END OF OVERLAY DEFINITIONS *********************************************************************** + +}).call(typeof window !== 'undefined' ? window : this); + +/* + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +;(function() { + "use strict"; + + var root = this, + _ju = root.jsPlumbUtil, + _jpi = root.jsPlumbInstance; + + var GROUP_COLLAPSED_CLASS = "jtk-group-collapsed"; + var GROUP_EXPANDED_CLASS = "jtk-group-expanded"; + var GROUP_CONTAINER_SELECTOR = "[jtk-group-content]"; + var ELEMENT_DRAGGABLE_EVENT = "elementDraggable"; + var STOP = "stop"; + var REVERT = "revert"; + var GROUP_MANAGER = "_groupManager"; + var GROUP = "_jsPlumbGroup"; + var GROUP_DRAG_SCOPE = "_jsPlumbGroupDrag"; + var EVT_CHILD_ADDED = "group:addMember"; + var EVT_CHILD_REMOVED = "group:removeMember"; + var EVT_GROUP_ADDED = "group:add"; + var EVT_GROUP_REMOVED = "group:remove"; + var EVT_EXPAND = "group:expand"; + var EVT_COLLAPSE = "group:collapse"; + var EVT_GROUP_DRAG_STOP = "groupDragStop"; + var EVT_CONNECTION_MOVED = "connectionMoved"; + var EVT_INTERNAL_CONNECTION_DETACHED = "internal.connectionDetached"; + + var CMD_REMOVE_ALL = "removeAll"; + var CMD_ORPHAN_ALL = "orphanAll"; + var CMD_SHOW = "show"; + var CMD_HIDE = "hide"; + + var GroupManager = function(_jsPlumb) { + var _managedGroups = {}, _connectionSourceMap = {}, _connectionTargetMap = {}, self = this; + + _jsPlumb.bind("connection", function(p) { + if (p.source[GROUP] != null && p.target[GROUP] != null && p.source[GROUP] === p.target[GROUP]) { + _connectionSourceMap[p.connection.id] = p.source[GROUP]; + _connectionTargetMap[p.connection.id] = p.source[GROUP]; + } + else { + if (p.source[GROUP] != null) { + _ju.suggest(p.source[GROUP].connections.source, p.connection); + _connectionSourceMap[p.connection.id] = p.source[GROUP]; + } + if (p.target[GROUP] != null) { + _ju.suggest(p.target[GROUP].connections.target, p.connection); + _connectionTargetMap[p.connection.id] = p.target[GROUP]; + } + } + }); + + function _cleanupDetachedConnection(conn) { + delete conn.proxies; + var group = _connectionSourceMap[conn.id], f; + if (group != null) { + f = function(c) { return c.id === conn.id; }; + _ju.removeWithFunction(group.connections.source, f); + _ju.removeWithFunction(group.connections.target, f); + delete _connectionSourceMap[conn.id]; + } + + group = _connectionTargetMap[conn.id]; + if (group != null) { + f = function(c) { return c.id === conn.id; }; + _ju.removeWithFunction(group.connections.source, f); + _ju.removeWithFunction(group.connections.target, f); + delete _connectionTargetMap[conn.id]; + } + } + + _jsPlumb.bind(EVT_INTERNAL_CONNECTION_DETACHED, function(p) { + _cleanupDetachedConnection(p.connection); + }); + + _jsPlumb.bind(EVT_CONNECTION_MOVED, function(p) { + var connMap = p.index === 0 ? _connectionSourceMap : _connectionTargetMap; + var group = connMap[p.connection.id]; + if (group) { + var list = group.connections[p.index === 0 ? "source" : "target"]; + var idx = list.indexOf(p.connection); + if (idx !== -1) { + list.splice(idx, 1); + } + } + }); + + this.addGroup = function(group) { + _jsPlumb.addClass(group.getEl(), GROUP_EXPANDED_CLASS); + _managedGroups[group.id] = group; + group.manager = this; + _updateConnectionsForGroup(group); + _jsPlumb.fire(EVT_GROUP_ADDED, { group:group }); + }; + + this.addToGroup = function(group, el, doNotFireEvent) { + group = this.getGroup(group); + if (group) { + var groupEl = group.getEl(); + + if (el._isJsPlumbGroup) { + return; + } + var currentGroup = el._jsPlumbGroup; + // if already a member of this group, do nothing + if (currentGroup !== group) { + var elpos = _jsPlumb.getOffset(el, true); + var cpos = group.collapsed ? _jsPlumb.getOffset(groupEl, true) : _jsPlumb.getOffset(group.getDragArea(), true); + + // otherwise, transfer to this group. + if (currentGroup != null) { + currentGroup.remove(el, false, doNotFireEvent, false, group); + self.updateConnectionsForGroup(currentGroup); + } + group.add(el, doNotFireEvent/*, currentGroup*/); + + var handleDroppedConnections = function (list, index) { + var oidx = index === 0 ? 1 : 0; + list.each(function (c) { + c.setVisible(false); + if (c.endpoints[oidx].element._jsPlumbGroup === group) { + c.endpoints[oidx].setVisible(false); + _expandConnection(c, oidx, group); + } + else { + c.endpoints[index].setVisible(false); + _collapseConnection(c, index, group); + } + }); + }; + + if (group.collapsed) { + handleDroppedConnections(_jsPlumb.select({source: el}), 0); + handleDroppedConnections(_jsPlumb.select({target: el}), 1); + } + + var elId = _jsPlumb.getId(el); + _jsPlumb.dragManager.setParent(el, elId, groupEl, _jsPlumb.getId(groupEl), elpos); + + var newPosition = { left: elpos.left - cpos.left, top: elpos.top - cpos.top }; + + _jsPlumb.setPosition(el, newPosition); + + _jsPlumb.dragManager.revalidateParent(el, elId, elpos); + + self.updateConnectionsForGroup(group); + + _jsPlumb.revalidate(elId); + + if (!doNotFireEvent) { + var p = {group: group, el: el, pos:newPosition}; + if (currentGroup) { + p.sourceGroup = currentGroup; + } + _jsPlumb.fire(EVT_CHILD_ADDED, p); + } + } + } + }; + + this.removeFromGroup = function(group, el, doNotFireEvent) { + group = this.getGroup(group); + if (group) { + group.remove(el, null, doNotFireEvent); + } + }; + + this.getGroup = function(groupId) { + var group = groupId; + if (_ju.isString(groupId)) { + group = _managedGroups[groupId]; + if (group == null) { + throw new TypeError("No such group [" + groupId + "]"); + } + } + return group; + }; + + this.getGroups = function() { + var o = []; + for (var g in _managedGroups) { + o.push(_managedGroups[g]); + } + return o; + }; + + this.removeGroup = function(group, deleteMembers, manipulateDOM, doNotFireEvent) { + group = this.getGroup(group); + this.expandGroup(group, true); // this reinstates any original connections and removes all proxies, but does not fire an event. + var newPositions = group[deleteMembers ? CMD_REMOVE_ALL : CMD_ORPHAN_ALL](manipulateDOM, doNotFireEvent); + _jsPlumb.remove(group.getEl()); + delete _managedGroups[group.id]; + delete _jsPlumb._groups[group.id]; + _jsPlumb.fire(EVT_GROUP_REMOVED, { group:group }); + return newPositions; // this will be null in the case or remove, but be a map of {id->[x,y]} in the case of orphan + }; + + this.removeAllGroups = function(deleteMembers, manipulateDOM, doNotFireEvent) { + for (var g in _managedGroups) { + this.removeGroup(_managedGroups[g], deleteMembers, manipulateDOM, doNotFireEvent); + } + }; + + function _setVisible(group, state) { + var m = group.getMembers(); + for (var i = 0; i < m.length; i++) { + _jsPlumb[state ? CMD_SHOW : CMD_HIDE](m[i], true); + } + } + + var _collapseConnection = function(c, index, group) { + + var otherEl = c.endpoints[index === 0 ? 1 : 0].element; + if (otherEl[GROUP] && (!otherEl[GROUP].shouldProxy() && otherEl[GROUP].collapsed)) { + return; + } + + var groupEl = group.getEl(), groupElId = _jsPlumb.getId(groupEl); + + _jsPlumb.proxyConnection(c, index, groupEl, groupElId, function(c, index) { return group.getEndpoint(c, index); }, function(c, index) { return group.getAnchor(c, index); }); + }; + + this.collapseGroup = function(group) { + group = this.getGroup(group); + if (group == null || group.collapsed) { + return; + } + var groupEl = group.getEl(); + + // todo remove old proxy endpoints first, just in case? + //group.proxies.length = 0; + + // hide all connections + _setVisible(group, false); + + if (group.shouldProxy()) { + // collapses all connections in a group. + var _collapseSet = function (conns, index) { + for (var i = 0; i < conns.length; i++) { + var c = conns[i]; + _collapseConnection(c, index, group); + } + }; + + // setup proxies for sources and targets + _collapseSet(group.connections.source, 0); + _collapseSet(group.connections.target, 1); + } + + group.collapsed = true; + _jsPlumb.removeClass(groupEl, GROUP_EXPANDED_CLASS); + _jsPlumb.addClass(groupEl, GROUP_COLLAPSED_CLASS); + _jsPlumb.revalidate(groupEl); + _jsPlumb.fire(EVT_COLLAPSE, { group:group }); + }; + + var _expandConnection = function(c, index, group) { + _jsPlumb.unproxyConnection(c, index, _jsPlumb.getId(group.getEl())); + }; + + this.expandGroup = function(group, doNotFireEvent) { + + group = this.getGroup(group); + + if (group == null || !group.collapsed) { + return; + } + var groupEl = group.getEl(); + + _setVisible(group, true); + + if (group.shouldProxy()) { + // collapses all connections in a group. + var _expandSet = function (conns, index) { + for (var i = 0; i < conns.length; i++) { + var c = conns[i]; + _expandConnection(c, index, group); + } + }; + + // setup proxies for sources and targets + _expandSet(group.connections.source, 0); + _expandSet(group.connections.target, 1); + } + + group.collapsed = false; + _jsPlumb.addClass(groupEl, GROUP_EXPANDED_CLASS); + _jsPlumb.removeClass(groupEl, GROUP_COLLAPSED_CLASS); + _jsPlumb.revalidate(groupEl); + this.repaintGroup(group); + if (!doNotFireEvent) { + _jsPlumb.fire(EVT_EXPAND, { group: group}); + } + }; + + this.repaintGroup = function(group) { + group = this.getGroup(group); + var m = group.getMembers(); + for (var i = 0; i < m.length; i++) { + _jsPlumb.revalidate(m[i]); + } + }; + + // TODO refactor this with the code that responds to `connection` events. + function _updateConnectionsForGroup(group) { + var members = group.getMembers(); + var c1 = _jsPlumb.getConnections({source:members, scope:"*"}, true); + var c2 = _jsPlumb.getConnections({target:members, scope:"*"}, true); + var processed = {}; + group.connections.source.length = 0; + group.connections.target.length = 0; + var oneSet = function(c) { + for (var i = 0; i < c.length; i++) { + if (processed[c[i].id]) { + continue; + } + processed[c[i].id] = true; + if (c[i].source._jsPlumbGroup === group) { + if (c[i].target._jsPlumbGroup !== group) { + group.connections.source.push(c[i]); + } + _connectionSourceMap[c[i].id] = group; + } + else if (c[i].target._jsPlumbGroup === group) { + group.connections.target.push(c[i]); + _connectionTargetMap[c[i].id] = group; + } + } + }; + oneSet(c1); oneSet(c2); + } + + this.updateConnectionsForGroup = _updateConnectionsForGroup; + this.refreshAllGroups = function() { + for (var g in _managedGroups) { + _updateConnectionsForGroup(_managedGroups[g]); + _jsPlumb.dragManager.updateOffsets(_jsPlumb.getId(_managedGroups[g].getEl())); + } + }; + }; + + /** + * + * @param {jsPlumbInstance} _jsPlumb Associated jsPlumb instance. + * @param {Object} params + * @param {Element} params.el The DOM element representing the Group. + * @param {String} [params.id] Optional ID for the Group. A UUID will be assigned as the Group's ID if you do not provide one. + * @param {Boolean} [params.constrain=false] If true, child elements will not be able to be dragged outside of the Group container. + * @param {Boolean} [params.revert=true] By default, child elements revert to the container if dragged outside. You can change this by setting `revert:false`. This behaviour is also overridden if you set `orphan` or `prune`. + * @param {Boolean} [params.orphan=false] If true, child elements dropped outside of the Group container will be removed from the Group (but not from the DOM). + * @param {Boolean} [params.prune=false] If true, child elements dropped outside of the Group container will be removed from the Group and also from the DOM. + * @param {Boolean} [params.dropOverride=false] If true, a child element that has been dropped onto some other Group will not be subject to the controls imposed by `prune`, `revert` or `orphan`. + * @constructor + */ + var Group = function(_jsPlumb, params) { + var self = this; + var el = params.el; + this.getEl = function() { return el; }; + this.id = params.id || _ju.uuid(); + el._isJsPlumbGroup = true; + + var getDragArea = this.getDragArea = function() { + var da = _jsPlumb.getSelector(el, GROUP_CONTAINER_SELECTOR); + return da && da.length > 0 ? da[0] : el; + }; + + var ghost = params.ghost === true; + var constrain = ghost || (params.constrain === true); + var revert = params.revert !== false; + var orphan = params.orphan === true; + var prune = params.prune === true; + var dropOverride = params.dropOverride === true; + var proxied = params.proxied !== false; + var elements = []; + this.connections = { source:[], target:[], internal:[] }; + + // this function, and getEndpoint below, are stubs for a future setup in which we can choose endpoint + // and anchor based upon the connection and the index (source/target) of the endpoint to be proxied. + this.getAnchor = function(conn, endpointIndex) { + return params.anchor || "Continuous"; + }; + + this.getEndpoint = function(conn, endpointIndex) { + return params.endpoint || [ "Dot", { radius:10 }]; + }; + + this.collapsed = false; + if (params.draggable !== false) { + var opts = { + stop:function(params) { + _jsPlumb.fire(EVT_GROUP_DRAG_STOP, jsPlumb.extend(params, {group:self})); + }, + scope:GROUP_DRAG_SCOPE + }; + if (params.dragOptions) { + root.jsPlumb.extend(opts, params.dragOptions); + } + _jsPlumb.draggable(params.el, opts); + } + if (params.droppable !== false) { + _jsPlumb.droppable(params.el, { + drop:function(p) { + var el = p.drag.el; + if (el._isJsPlumbGroup) { + return; + } + var currentGroup = el._jsPlumbGroup; + if (currentGroup !== self) { + if (currentGroup != null) { + if (currentGroup.overrideDrop(el, self)) { + return; + } + } + _jsPlumb.getGroupManager().addToGroup(self, el, false); + } + + } + }); + } + var _each = function(_el, fn) { + var els = _el.nodeType == null ? _el : [ _el ]; + for (var i = 0; i < els.length; i++) { + fn(els[i]); + } + }; + + this.overrideDrop = function(_el, targetGroup) { + return dropOverride && (revert || prune || orphan); + }; + + this.add = function(_el, doNotFireEvent/*, sourceGroup*/) { + var dragArea = getDragArea(); + _each(_el, function(__el) { + + if (__el._jsPlumbGroup != null) { + if (__el._jsPlumbGroup === self) { + return; + } else { + __el._jsPlumbGroup.remove(__el, true, doNotFireEvent, false); + } + } + + __el._jsPlumbGroup = self; + elements.push(__el); + // test if draggable and add handlers if so. + if (_jsPlumb.isAlreadyDraggable(__el)) { + _bindDragHandlers(__el); + } + + if (__el.parentNode !== dragArea) { + dragArea.appendChild(__el); + } + + // if (!doNotFireEvent) { + // var p = {group: self, el: __el}; + // if (sourceGroup) { + // p.sourceGroup = sourceGroup; + // } + // //_jsPlumb.fire(EVT_CHILD_ADDED, p); + // } + }); + + _jsPlumb.getGroupManager().updateConnectionsForGroup(self); + }; + + this.remove = function(el, manipulateDOM, doNotFireEvent, doNotUpdateConnections, targetGroup) { + + _each(el, function(__el) { + if (__el._jsPlumbGroup === self) { + delete __el._jsPlumbGroup; + _ju.removeWithFunction(elements, function (e) { + return e === __el; + }); + + if (manipulateDOM) { + try { + self.getDragArea().removeChild(__el); + } catch (e) { + jsPlumbUtil.log("Could not remove element from Group " + e); + } + } + _unbindDragHandlers(__el); + if (!doNotFireEvent) { + var p = {group: self, el: __el}; + if (targetGroup) { + p.targetGroup = targetGroup; + } + _jsPlumb.fire(EVT_CHILD_REMOVED, p); + } + } + }); + if (!doNotUpdateConnections) { + _jsPlumb.getGroupManager().updateConnectionsForGroup(self); + } + }; + this.removeAll = function(manipulateDOM, doNotFireEvent) { + for (var i = 0, l = elements.length; i < l; i++) { + var el = elements[0]; + self.remove(el, manipulateDOM, doNotFireEvent, true); + _jsPlumb.remove(el, true); + } + elements.length = 0; + _jsPlumb.getGroupManager().updateConnectionsForGroup(self); + }; + this.orphanAll = function() { + var orphanedPositions = {}; + for (var i = 0; i < elements.length; i++) { + var newPosition = _orphan(elements[i]); + orphanedPositions[newPosition[0]] = newPosition[1]; + } + elements.length = 0; + + return orphanedPositions; + }; + this.getMembers = function() { return elements; }; + + el[GROUP] = this; + + _jsPlumb.bind(ELEMENT_DRAGGABLE_EVENT, function(dragParams) { + // if its for the current group, + if (dragParams.el._jsPlumbGroup === this) { + _bindDragHandlers(dragParams.el); + } + }.bind(this)); + + function _findParent(_el) { + return _el.offsetParent; + } + + function _isInsideParent(_el, pos) { + var p = _findParent(_el), + s = _jsPlumb.getSize(p), + ss = _jsPlumb.getSize(_el), + leftEdge = pos[0], + rightEdge = leftEdge + ss[0], + topEdge = pos[1], + bottomEdge = topEdge + ss[1]; + + return rightEdge > 0 && leftEdge < s[0] && bottomEdge > 0 && topEdge < s[1]; + } + + // + // orphaning an element means taking it out of the group and adding it to the main jsplumb container. + // we return the new calculated position from this method and the element's id. + // + function _orphan(_el) { + var id = _jsPlumb.getId(_el); + var pos = _jsPlumb.getOffset(_el); + _el.parentNode.removeChild(_el); + _jsPlumb.getContainer().appendChild(_el); + _jsPlumb.setPosition(_el, pos); + _unbindDragHandlers(_el); + _jsPlumb.dragManager.clearParent(_el, id); + return [id, pos]; + } + + // + // remove an element from the group, then either prune it from the jsplumb instance, or just orphan it. + // + function _pruneOrOrphan(p) { + + var out = []; + + function _one(el, left, top) { + var orphanedPosition = null; + if (!_isInsideParent(el, [left, top])) { + var group = el._jsPlumbGroup; + if (prune) { + _jsPlumb.remove(el); + } else { + orphanedPosition = _orphan(el); + } + + group.remove(el); + } + + return orphanedPosition; + } + + for (var i = 0; i < p.selection.length; i++) { + out.push(_one(p.selection[i][0], p.selection[i][1].left, p.selection[i][1].top)); + } + + return out.length === 1 ? out[0] : out; + + } + + // + // redraws the element + // + function _revalidate(_el) { + var id = _jsPlumb.getId(_el); + _jsPlumb.revalidate(_el); + _jsPlumb.dragManager.revalidateParent(_el, id); + } + + // + // unbind the group specific drag/revert handlers. + // + function _unbindDragHandlers(_el) { + if (!_el._katavorioDrag) { + return; + } + if (prune || orphan) { + _el._katavorioDrag.off(STOP, _pruneOrOrphan); + } + if (!prune && !orphan && revert) { + _el._katavorioDrag.off(REVERT, _revalidate); + _el._katavorioDrag.setRevert(null); + } + } + + function _bindDragHandlers(_el) { + if (!_el._katavorioDrag) { + return; + } + if (prune || orphan) { + _el._katavorioDrag.on(STOP, _pruneOrOrphan); + } + + if (constrain) { + _el._katavorioDrag.setConstrain(true); + } + + if (ghost) { + _el._katavorioDrag.setUseGhostProxy(true); + } + + if (!prune && !orphan && revert) { + _el._katavorioDrag.on(REVERT, _revalidate); + _el._katavorioDrag.setRevert(function(__el, pos) { + return !_isInsideParent(__el, pos); + }); + } + } + + this.shouldProxy = function() { + return proxied; + }; + + _jsPlumb.getGroupManager().addGroup(this); + }; + + /** + * Adds a group to the jsPlumb instance. + * @method addGroup + * @param {Object} params + * @return {Group} The newly created Group. + */ + _jpi.prototype.addGroup = function(params) { + var j = this; + j._groups = j._groups || {}; + if (j._groups[params.id] != null) { + throw new TypeError("cannot create Group [" + params.id + "]; a Group with that ID exists"); + } + if (params.el[GROUP] != null) { + throw new TypeError("cannot create Group [" + params.id + "]; the given element is already a Group"); + } + var group = new Group(j, params); + j._groups[group.id] = group; + if (params.collapsed) { + this.collapseGroup(group); + } + return group; + }; + + /** + * Add an element to a group. + * @method addToGroup + * @param {String} group Group, or ID of the group, to add the element to. + * @param {Element} el Element to add to the group. + */ + _jpi.prototype.addToGroup = function(group, el, doNotFireEvent) { + + var _one = function(_el) { + var id = this.getId(_el); + this.manage(id, _el); + this.getGroupManager().addToGroup(group, _el, doNotFireEvent); + }.bind(this); + + if (Array.isArray(el)) { + for (var i = 0; i < el.length; i++) { + _one(el[i]); + } + } else { + _one(el); + } + }; + + /** + * Remove an element from a group. + * @method removeFromGroup + * @param {String} group Group, or ID of the group, to remove the element from. + * @param {Element} el Element to add to the group. + */ + _jpi.prototype.removeFromGroup = function(group, el, doNotFireEvent) { + this.getGroupManager().removeFromGroup(group, el, doNotFireEvent); + }; + + /** + * Remove a group, and optionally remove its members from the jsPlumb instance. + * @method removeGroup + * @param {String|Group} group Group to delete, or ID of Group to delete. + * @param {Boolean} [deleteMembers=false] If true, group members will be removed along with the group. Otherwise they will + * just be 'orphaned' (returned to the main container). + * @returns {Map[String, Position}} When deleteMembers is false, this method returns a map of {id->position} + */ + _jpi.prototype.removeGroup = function(group, deleteMembers, manipulateDOM, doNotFireEvent) { + return this.getGroupManager().removeGroup(group, deleteMembers, manipulateDOM, doNotFireEvent); + }; + + /** + * Remove all groups, and optionally remove their members from the jsPlumb instance. + * @method removeAllGroup + * @param {Boolean} [deleteMembers=false] If true, group members will be removed along with the groups. Otherwise they will + * just be 'orphaned' (returned to the main container). + */ + _jpi.prototype.removeAllGroups = function(deleteMembers, manipulateDOM, doNotFireEvent) { + this.getGroupManager().removeAllGroups(deleteMembers, manipulateDOM, doNotFireEvent); + }; + + /** + * Get a Group + * @method getGroup + * @param {String} groupId ID of the group to get + * @return {Group} Group with the given ID, null if not found. + */ + _jpi.prototype.getGroup = function(groupId) { + return this.getGroupManager().getGroup(groupId); + }; + + /** + * Gets all the Groups managed by the jsPlumb instance. + * @returns {Group[]} List of Groups. Empty if none. + */ + _jpi.prototype.getGroups = function() { + return this.getGroupManager().getGroups(); + }; + + /** + * Expands a group element. jsPlumb doesn't do "everything" for you here, because what it means to expand a Group + * will vary from application to application. jsPlumb does these things: + * + * - Hides any connections that are internal to the group (connections between members, and connections from member of + * the group to the group itself) + * - Proxies all connections for which the source or target is a member of the group. + * - Hides the proxied connections. + * - Adds the jtk-group-expanded class to the group's element + * - Removes the jtk-group-collapsed class from the group's element. + * + * @method expandGroup + * @param {String|Group} group Group to expand, or ID of Group to expand. + */ + _jpi.prototype.expandGroup = function(group) { + this.getGroupManager().expandGroup(group); + }; + + /** + * Collapses a group element. jsPlumb doesn't do "everything" for you here, because what it means to collapse a Group + * will vary from application to application. jsPlumb does these things: + * + * - Shows any connections that are internal to the group (connections between members, and connections from member of + * the group to the group itself) + * - Removes proxies for all connections for which the source or target is a member of the group. + * - Shows the previously proxied connections. + * - Adds the jtk-group-collapsed class to the group's element + * - Removes the jtk-group-expanded class from the group's element. + * + * @method expandGroup + * @param {String|Group} group Group to expand, or ID of Group to expand. + */ + _jpi.prototype.collapseGroup = function(groupId) { + this.getGroupManager().collapseGroup(groupId); + }; + + + _jpi.prototype.repaintGroup = function(group) { + this.getGroupManager().repaintGroup(group); + }; + + /** + * Collapses or expands a group element depending on its current state. See notes in the collapseGroup and expandGroup method. + * + * @method toggleGroup + * @param {String|Group} group Group to expand/collapse, or ID of Group to expand/collapse. + */ + _jpi.prototype.toggleGroup = function(group) { + group = this.getGroupManager().getGroup(group); + if (group != null) { + this.getGroupManager()[group.collapsed ? "expandGroup" : "collapseGroup"](group); + } + }; + + // + // lazy init a group manager for the given jsplumb instance. + // + _jpi.prototype.getGroupManager = function() { + var mgr = this[GROUP_MANAGER]; + if (mgr == null) { + mgr = this[GROUP_MANAGER] = new GroupManager(this); + } + return mgr; + }; + + _jpi.prototype.removeGroupManager = function() { + delete this[GROUP_MANAGER]; + }; + + /** + * Gets the Group that the given element belongs to, null if none. + * @method getGroupFor + * @param {String|Element} el Element, or element ID. + * @returns {Group} A Group, if found, or null. + */ + _jpi.prototype.getGroupFor = function(el) { + el = this.getElement(el); + if (el) { + return el[GROUP]; + } + }; + +}).call(typeof window !== 'undefined' ? window : this); + + +/* + * This file contains the 'flowchart' connectors, consisting of vertical and horizontal line segments. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + var STRAIGHT = "Straight"; + var ARC = "Arc"; + + var Flowchart = function (params) { + this.type = "Flowchart"; + params = params || {}; + params.stub = params.stub == null ? 30 : params.stub; + var segments, + _super = _jp.Connectors.AbstractConnector.apply(this, arguments), + midpoint = params.midpoint == null ? 0.5 : params.midpoint, + alwaysRespectStubs = params.alwaysRespectStubs === true, + lastx = null, lasty = null, lastOrientation, + cornerRadius = params.cornerRadius != null ? params.cornerRadius : 0, + + // TODO now common between this and AbstractBezierEditor; refactor into superclass? + loopbackRadius = params.loopbackRadius || 25, + isLoopbackCurrently = false, + + sgn = function (n) { + return n < 0 ? -1 : n === 0 ? 0 : 1; + }, + segmentDirections = function(segment) { + return [ + sgn( segment[2] - segment[0] ), + sgn( segment[3] - segment[1] ) + ]; + }, + /** + * helper method to add a segment. + */ + addSegment = function (segments, x, y, paintInfo) { + if (lastx === x && lasty === y) { + return; + } + var lx = lastx == null ? paintInfo.sx : lastx, + ly = lasty == null ? paintInfo.sy : lasty, + o = lx === x ? "v" : "h"; + + lastx = x; + lasty = y; + segments.push([ lx, ly, x, y, o ]); + }, + segLength = function (s) { + return Math.sqrt(Math.pow(s[0] - s[2], 2) + Math.pow(s[1] - s[3], 2)); + }, + _cloneArray = function (a) { + var _a = []; + _a.push.apply(_a, a); + return _a; + }, + writeSegments = function (conn, segments, paintInfo) { + var current = null, next, currentDirection, nextDirection; + for (var i = 0; i < segments.length - 1; i++) { + + current = current || _cloneArray(segments[i]); + next = _cloneArray(segments[i + 1]); + + currentDirection = segmentDirections(current); + nextDirection = segmentDirections(next); + + if (cornerRadius > 0 && current[4] !== next[4]) { + + var minSegLength = Math.min(segLength(current), segLength(next)); + var radiusToUse = Math.min(cornerRadius, minSegLength / 2); + + current[2] -= currentDirection[0] * radiusToUse; + current[3] -= currentDirection[1] * radiusToUse; + next[0] += nextDirection[0] * radiusToUse; + next[1] += nextDirection[1] * radiusToUse; + + var ac = (currentDirection[1] === nextDirection[0] && nextDirection[0] === 1) || + ((currentDirection[1] === nextDirection[0] && nextDirection[0] === 0) && currentDirection[0] !== nextDirection[1]) || + (currentDirection[1] === nextDirection[0] && nextDirection[0] === -1), + sgny = next[1] > current[3] ? 1 : -1, + sgnx = next[0] > current[2] ? 1 : -1, + sgnEqual = sgny === sgnx, + cx = (sgnEqual && ac || (!sgnEqual && !ac)) ? next[0] : current[2], + cy = (sgnEqual && ac || (!sgnEqual && !ac)) ? current[3] : next[1]; + + _super.addSegment(conn, STRAIGHT, { + x1: current[0], y1: current[1], x2: current[2], y2: current[3] + }); + + _super.addSegment(conn, ARC, { + r: radiusToUse, + x1: current[2], + y1: current[3], + x2: next[0], + y2: next[1], + cx: cx, + cy: cy, + ac: ac + }); + } + else { + // dx + dy are used to adjust for line width. + var dx = (current[2] === current[0]) ? 0 : (current[2] > current[0]) ? (paintInfo.lw / 2) : -(paintInfo.lw / 2), + dy = (current[3] === current[1]) ? 0 : (current[3] > current[1]) ? (paintInfo.lw / 2) : -(paintInfo.lw / 2); + + _super.addSegment(conn, STRAIGHT, { + x1: current[0] - dx, y1: current[1] - dy, x2: current[2] + dx, y2: current[3] + dy + }); + } + current = next; + } + if (next != null) { + // last segment + _super.addSegment(conn, STRAIGHT, { + x1: next[0], y1: next[1], x2: next[2], y2: next[3] + }); + } + }; + + this._compute = function (paintInfo, params) { + + segments = []; + lastx = null; + lasty = null; + lastOrientation = null; + + var commonStubCalculator = function () { + return [paintInfo.startStubX, paintInfo.startStubY, paintInfo.endStubX, paintInfo.endStubY]; + }, + stubCalculators = { + perpendicular: commonStubCalculator, + orthogonal: commonStubCalculator, + opposite: function (axis) { + var pi = paintInfo, + idx = axis === "x" ? 0 : 1, + areInProximity = { + "x": function () { + return ( (pi.so[idx] === 1 && ( + ( (pi.startStubX > pi.endStubX) && (pi.tx > pi.startStubX) ) || + ( (pi.sx > pi.endStubX) && (pi.tx > pi.sx))))) || + + ( (pi.so[idx] === -1 && ( + ( (pi.startStubX < pi.endStubX) && (pi.tx < pi.startStubX) ) || + ( (pi.sx < pi.endStubX) && (pi.tx < pi.sx))))); + }, + "y": function () { + return ( (pi.so[idx] === 1 && ( + ( (pi.startStubY > pi.endStubY) && (pi.ty > pi.startStubY) ) || + ( (pi.sy > pi.endStubY) && (pi.ty > pi.sy))))) || + + ( (pi.so[idx] === -1 && ( + ( (pi.startStubY < pi.endStubY) && (pi.ty < pi.startStubY) ) || + ( (pi.sy < pi.endStubY) && (pi.ty < pi.sy))))); + } + }; + + if (!alwaysRespectStubs && areInProximity[axis]()) { + return { + "x": [(paintInfo.sx + paintInfo.tx) / 2, paintInfo.startStubY, (paintInfo.sx + paintInfo.tx) / 2, paintInfo.endStubY], + "y": [paintInfo.startStubX, (paintInfo.sy + paintInfo.ty) / 2, paintInfo.endStubX, (paintInfo.sy + paintInfo.ty) / 2] + }[axis]; + } + else { + return [paintInfo.startStubX, paintInfo.startStubY, paintInfo.endStubX, paintInfo.endStubY]; + } + } + }; + + // calculate Stubs. + var stubs = stubCalculators[paintInfo.anchorOrientation](paintInfo.sourceAxis), + idx = paintInfo.sourceAxis === "x" ? 0 : 1, + oidx = paintInfo.sourceAxis === "x" ? 1 : 0, + ss = stubs[idx], + oss = stubs[oidx], + es = stubs[idx + 2], + oes = stubs[oidx + 2]; + + // add the start stub segment. use stubs for loopback as it will look better, with the loop spaced + // away from the element. + addSegment(segments, stubs[0], stubs[1], paintInfo); + + // if its a loopback and we should treat it differently. + // if (false && params.sourcePos[0] === params.targetPos[0] && params.sourcePos[1] === params.targetPos[1]) { + // + // // we use loopbackRadius here, as statemachine connectors do. + // // so we go radius to the left from stubs[0], then upwards by 2*radius, to the right by 2*radius, + // // down by 2*radius, left by radius. + // addSegment(segments, stubs[0] - loopbackRadius, stubs[1], paintInfo); + // addSegment(segments, stubs[0] - loopbackRadius, stubs[1] - (2 * loopbackRadius), paintInfo); + // addSegment(segments, stubs[0] + loopbackRadius, stubs[1] - (2 * loopbackRadius), paintInfo); + // addSegment(segments, stubs[0] + loopbackRadius, stubs[1], paintInfo); + // addSegment(segments, stubs[0], stubs[1], paintInfo); + // + // } + // else { + + + var midx = paintInfo.startStubX + ((paintInfo.endStubX - paintInfo.startStubX) * midpoint), + midy = paintInfo.startStubY + ((paintInfo.endStubY - paintInfo.startStubY) * midpoint); + + var orientations = {x: [0, 1], y: [1, 0]}, + lineCalculators = { + perpendicular: function (axis) { + var pi = paintInfo, + sis = { + x: [ + [[1, 2, 3, 4], null, [2, 1, 4, 3]], + null, + [[4, 3, 2, 1], null, [3, 4, 1, 2]] + ], + y: [ + [[3, 2, 1, 4], null, [2, 3, 4, 1]], + null, + [[4, 1, 2, 3], null, [1, 4, 3, 2]] + ] + }, + stubs = { + x: [[pi.startStubX, pi.endStubX], null, [pi.endStubX, pi.startStubX]], + y: [[pi.startStubY, pi.endStubY], null, [pi.endStubY, pi.startStubY]] + }, + midLines = { + x: [[midx, pi.startStubY], [midx, pi.endStubY]], + y: [[pi.startStubX, midy], [pi.endStubX, midy]] + }, + linesToEnd = { + x: [[pi.endStubX, pi.startStubY]], + y: [[pi.startStubX, pi.endStubY]] + }, + startToEnd = { + x: [[pi.startStubX, pi.endStubY], [pi.endStubX, pi.endStubY]], + y: [[pi.endStubX, pi.startStubY], [pi.endStubX, pi.endStubY]] + }, + startToMidToEnd = { + x: [[pi.startStubX, midy], [pi.endStubX, midy], [pi.endStubX, pi.endStubY]], + y: [[midx, pi.startStubY], [midx, pi.endStubY], [pi.endStubX, pi.endStubY]] + }, + otherStubs = { + x: [pi.startStubY, pi.endStubY], + y: [pi.startStubX, pi.endStubX] + }, + soIdx = orientations[axis][0], toIdx = orientations[axis][1], + _so = pi.so[soIdx] + 1, + _to = pi.to[toIdx] + 1, + otherFlipped = (pi.to[toIdx] === -1 && (otherStubs[axis][1] < otherStubs[axis][0])) || (pi.to[toIdx] === 1 && (otherStubs[axis][1] > otherStubs[axis][0])), + stub1 = stubs[axis][_so][0], + stub2 = stubs[axis][_so][1], + segmentIndexes = sis[axis][_so][_to]; + + if (pi.segment === segmentIndexes[3] || (pi.segment === segmentIndexes[2] && otherFlipped)) { + return midLines[axis]; + } + else if (pi.segment === segmentIndexes[2] && stub2 < stub1) { + return linesToEnd[axis]; + } + else if ((pi.segment === segmentIndexes[2] && stub2 >= stub1) || (pi.segment === segmentIndexes[1] && !otherFlipped)) { + return startToMidToEnd[axis]; + } + else if (pi.segment === segmentIndexes[0] || (pi.segment === segmentIndexes[1] && otherFlipped)) { + return startToEnd[axis]; + } + }, + orthogonal: function (axis, startStub, otherStartStub, endStub, otherEndStub) { + var pi = paintInfo, + extent = { + "x": pi.so[0] === -1 ? Math.min(startStub, endStub) : Math.max(startStub, endStub), + "y": pi.so[1] === -1 ? Math.min(startStub, endStub) : Math.max(startStub, endStub) + }[axis]; + + return { + "x": [ + [extent, otherStartStub], + [extent, otherEndStub], + [endStub, otherEndStub] + ], + "y": [ + [otherStartStub, extent], + [otherEndStub, extent], + [otherEndStub, endStub] + ] + }[axis]; + }, + opposite: function (axis, ss, oss, es) { + var pi = paintInfo, + otherAxis = {"x": "y", "y": "x"}[axis], + dim = {"x": "height", "y": "width"}[axis], + comparator = pi["is" + axis.toUpperCase() + "GreaterThanStubTimes2"]; + + if (params.sourceEndpoint.elementId === params.targetEndpoint.elementId) { + var _val = oss + ((1 - params.sourceEndpoint.anchor[otherAxis]) * params.sourceInfo[dim]) + _super.maxStub; + return { + "x": [ + [ss, _val], + [es, _val] + ], + "y": [ + [_val, ss], + [_val, es] + ] + }[axis]; + + } + else if (!comparator || (pi.so[idx] === 1 && ss > es) || (pi.so[idx] === -1 && ss < es)) { + return { + "x": [ + [ss, midy], + [es, midy] + ], + "y": [ + [midx, ss], + [midx, es] + ] + }[axis]; + } + else if ((pi.so[idx] === 1 && ss < es) || (pi.so[idx] === -1 && ss > es)) { + return { + "x": [ + [midx, pi.sy], + [midx, pi.ty] + ], + "y": [ + [pi.sx, midy], + [pi.tx, midy] + ] + }[axis]; + } + } + }; + + // compute the rest of the line + var p = lineCalculators[paintInfo.anchorOrientation](paintInfo.sourceAxis, ss, oss, es, oes); + if (p) { + for (var i = 0; i < p.length; i++) { + addSegment(segments, p[i][0], p[i][1], paintInfo); + } + } + + // line to end stub + addSegment(segments, stubs[2], stubs[3], paintInfo); + + //} + + // end stub to end (common) + addSegment(segments, paintInfo.tx, paintInfo.ty, paintInfo); + + + + // write out the segments. + writeSegments(this, segments, paintInfo); + + }; + }; + + _jp.Connectors.Flowchart = Flowchart; + _ju.extend(_jp.Connectors.Flowchart, _jp.Connectors.AbstractConnector); + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the code for the Bezier connector type. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + + _jp.Connectors.AbstractBezierConnector = function(params) { + params = params || {}; + var showLoopback = params.showLoopback !== false, + curviness = params.curviness || 10, + margin = params.margin || 5, + proximityLimit = params.proximityLimit || 80, + clockwise = params.orientation && params.orientation === "clockwise", + loopbackRadius = params.loopbackRadius || 25, + isLoopbackCurrently = false, + _super; + + this._compute = function (paintInfo, p) { + + var sp = p.sourcePos, + tp = p.targetPos, + _w = Math.abs(sp[0] - tp[0]), + _h = Math.abs(sp[1] - tp[1]); + + if (!showLoopback || (p.sourceEndpoint.elementId !== p.targetEndpoint.elementId)) { + isLoopbackCurrently = false; + this._computeBezier(paintInfo, p, sp, tp, _w, _h); + } else { + isLoopbackCurrently = true; + // a loopback connector. draw an arc from one anchor to the other. + var x1 = p.sourcePos[0], y1 = p.sourcePos[1] - margin, + cx = x1, cy = y1 - loopbackRadius, + // canvas sizing stuff, to ensure the whole painted area is visible. + _x = cx - loopbackRadius, + _y = cy - loopbackRadius; + + _w = 2 * loopbackRadius; + _h = 2 * loopbackRadius; + + paintInfo.points[0] = _x; + paintInfo.points[1] = _y; + paintInfo.points[2] = _w; + paintInfo.points[3] = _h; + + // ADD AN ARC SEGMENT. + _super.addSegment(this, "Arc", { + loopback: true, + x1: (x1 - _x) + 4, + y1: y1 - _y, + startAngle: 0, + endAngle: 2 * Math.PI, + r: loopbackRadius, + ac: !clockwise, + x2: (x1 - _x) - 4, + y2: y1 - _y, + cx: cx - _x, + cy: cy - _y + }); + } + }; + + _super = _jp.Connectors.AbstractConnector.apply(this, arguments); + return _super; + }; + _ju.extend(_jp.Connectors.AbstractBezierConnector, _jp.Connectors.AbstractConnector); + + var Bezier = function (params) { + params = params || {}; + this.type = "Bezier"; + + var _super = _jp.Connectors.AbstractBezierConnector.apply(this, arguments), + majorAnchor = params.curviness || 150, + minorAnchor = 10; + + this.getCurviness = function () { + return majorAnchor; + }; + + this._findControlPoint = function (point, sourceAnchorPosition, targetAnchorPosition, sourceEndpoint, targetEndpoint, soo, too) { + // determine if the two anchors are perpendicular to each other in their orientation. we swap the control + // points around if so (code could be tightened up) + var perpendicular = soo[0] !== too[0] || soo[1] === too[1], + p = []; + + if (!perpendicular) { + if (soo[0] === 0) { + p.push(sourceAnchorPosition[0] < targetAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor); + } + else { + p.push(point[0] - (majorAnchor * soo[0])); + } + + if (soo[1] === 0) { + p.push(sourceAnchorPosition[1] < targetAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor); + } + else { + p.push(point[1] + (majorAnchor * too[1])); + } + } + else { + if (too[0] === 0) { + p.push(targetAnchorPosition[0] < sourceAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor); + } + else { + p.push(point[0] + (majorAnchor * too[0])); + } + + if (too[1] === 0) { + p.push(targetAnchorPosition[1] < sourceAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor); + } + else { + p.push(point[1] + (majorAnchor * soo[1])); + } + } + + return p; + }; + + this._computeBezier = function (paintInfo, p, sp, tp, _w, _h) { + + var _CP, _CP2, + _sx = sp[0] < tp[0] ? _w : 0, + _sy = sp[1] < tp[1] ? _h : 0, + _tx = sp[0] < tp[0] ? 0 : _w, + _ty = sp[1] < tp[1] ? 0 : _h; + + _CP = this._findControlPoint([_sx, _sy], sp, tp, p.sourceEndpoint, p.targetEndpoint, paintInfo.so, paintInfo.to); + _CP2 = this._findControlPoint([_tx, _ty], tp, sp, p.targetEndpoint, p.sourceEndpoint, paintInfo.to, paintInfo.so); + + + _super.addSegment(this, "Bezier", { + x1: _sx, y1: _sy, x2: _tx, y2: _ty, + cp1x: _CP[0], cp1y: _CP[1], cp2x: _CP2[0], cp2y: _CP2[1] + }); + }; + + + }; + + _jp.Connectors.Bezier = Bezier; + _ju.extend(Bezier, _jp.Connectors.AbstractBezierConnector); + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the state machine connectors, which extend AbstractBezierConnector. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + + var _segment = function (x1, y1, x2, y2) { + if (x1 <= x2 && y2 <= y1) { + return 1; + } + else if (x1 <= x2 && y1 <= y2) { + return 2; + } + else if (x2 <= x1 && y2 >= y1) { + return 3; + } + return 4; + }, + + // the control point we will use depends on the faces to which each end of the connection is assigned, specifically whether or not the + // two faces are parallel or perpendicular. if they are parallel then the control point lies on the midpoint of the axis in which they + // are parellel and varies only in the other axis; this variation is proportional to the distance that the anchor points lie from the + // center of that face. if the two faces are perpendicular then the control point is at some distance from both the midpoints; the amount and + // direction are dependent on the orientation of the two elements. 'seg', passed in to this method, tells you which segment the target element + // lies in with respect to the source: 1 is top right, 2 is bottom right, 3 is bottom left, 4 is top left. + // + // sourcePos and targetPos are arrays of info about where on the source and target each anchor is located. their contents are: + // + // 0 - absolute x + // 1 - absolute y + // 2 - proportional x in element (0 is left edge, 1 is right edge) + // 3 - proportional y in element (0 is top edge, 1 is bottom edge) + // + _findControlPoint = function (midx, midy, segment, sourceEdge, targetEdge, dx, dy, distance, proximityLimit) { + // TODO (maybe) + // - if anchor pos is 0.5, make the control point take into account the relative position of the elements. + if (distance <= proximityLimit) { + return [midx, midy]; + } + + if (segment === 1) { + if (sourceEdge[3] <= 0 && targetEdge[3] >= 1) { + return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ]; + } + else if (sourceEdge[2] >= 1 && targetEdge[2] <= 0) { + return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ]; + } + else { + return [ midx + (-1 * dx) , midy + (-1 * dy) ]; + } + } + else if (segment === 2) { + if (sourceEdge[3] >= 1 && targetEdge[3] <= 0) { + return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ]; + } + else if (sourceEdge[2] >= 1 && targetEdge[2] <= 0) { + return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ]; + } + else { + return [ midx + dx, midy + (-1 * dy) ]; + } + } + else if (segment === 3) { + if (sourceEdge[3] >= 1 && targetEdge[3] <= 0) { + return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ]; + } + else if (sourceEdge[2] <= 0 && targetEdge[2] >= 1) { + return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ]; + } + else { + return [ midx + (-1 * dx) , midy + (-1 * dy) ]; + } + } + else if (segment === 4) { + if (sourceEdge[3] <= 0 && targetEdge[3] >= 1) { + return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ]; + } + else if (sourceEdge[2] <= 0 && targetEdge[2] >= 1) { + return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ]; + } + else { + return [ midx + dx , midy + (-1 * dy) ]; + } + } + + }; + + var StateMachine = function (params) { + params = params || {}; + this.type = "StateMachine"; + + var _super = _jp.Connectors.AbstractBezierConnector.apply(this, arguments), + curviness = params.curviness || 10, + margin = params.margin || 5, + proximityLimit = params.proximityLimit || 80, + clockwise = params.orientation && params.orientation === "clockwise", + _controlPoint; + + this._computeBezier = function(paintInfo, params, sp, tp, w, h) { + var _sx = params.sourcePos[0] < params.targetPos[0] ? 0 : w, + _sy = params.sourcePos[1] < params.targetPos[1] ? 0 : h, + _tx = params.sourcePos[0] < params.targetPos[0] ? w : 0, + _ty = params.sourcePos[1] < params.targetPos[1] ? h : 0; + + // now adjust for the margin + if (params.sourcePos[2] === 0) { + _sx -= margin; + } + if (params.sourcePos[2] === 1) { + _sx += margin; + } + if (params.sourcePos[3] === 0) { + _sy -= margin; + } + if (params.sourcePos[3] === 1) { + _sy += margin; + } + if (params.targetPos[2] === 0) { + _tx -= margin; + } + if (params.targetPos[2] === 1) { + _tx += margin; + } + if (params.targetPos[3] === 0) { + _ty -= margin; + } + if (params.targetPos[3] === 1) { + _ty += margin; + } + + // + // these connectors are quadratic bezier curves, having a single control point. if both anchors + // are located at 0.5 on their respective faces, the control point is set to the midpoint and you + // get a straight line. this is also the case if the two anchors are within 'proximityLimit', since + // it seems to make good aesthetic sense to do that. outside of that, the control point is positioned + // at 'curviness' pixels away along the normal to the straight line connecting the two anchors. + // + // there may be two improvements to this. firstly, we might actually support the notion of avoiding nodes + // in the UI, or at least making a good effort at doing so. if a connection would pass underneath some node, + // for example, we might increase the distance the control point is away from the midpoint in a bid to + // steer it around that node. this will work within limits, but i think those limits would also be the likely + // limits for, once again, aesthetic good sense in the layout of a chart using these connectors. + // + // the second possible change is actually two possible changes: firstly, it is possible we should gradually + // decrease the 'curviness' as the distance between the anchors decreases; start tailing it off to 0 at some + // point (which should be configurable). secondly, we might slightly increase the 'curviness' for connectors + // with respect to how far their anchor is from the center of its respective face. this could either look cool, + // or stupid, and may indeed work only in a way that is so subtle as to have been a waste of time. + // + + var _midx = (_sx + _tx) / 2, + _midy = (_sy + _ty) / 2, + segment = _segment(_sx, _sy, _tx, _ty), + distance = Math.sqrt(Math.pow(_tx - _sx, 2) + Math.pow(_ty - _sy, 2)), + cp1x, cp2x, cp1y, cp2y; + + + // calculate the control point. this code will be where we'll put in a rudimentary element avoidance scheme; it + // will work by extending the control point to force the curve to be, um, curvier. + _controlPoint = _findControlPoint(_midx, + _midy, + segment, + params.sourcePos, + params.targetPos, + curviness, curviness, + distance, + proximityLimit); + + cp1x = _controlPoint[0]; + cp2x = _controlPoint[0]; + cp1y = _controlPoint[1]; + cp2y = _controlPoint[1]; + + _super.addSegment(this, "Bezier", { + x1: _tx, y1: _ty, x2: _sx, y2: _sy, + cp1x: cp1x, cp1y: cp1y, + cp2x: cp2x, cp2y: cp2y + }); + }; + }; + + _jp.Connectors.StateMachine = StateMachine; + _ju.extend(StateMachine, _jp.Connectors.AbstractBezierConnector); + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the 'flowchart' connectors, consisting of vertical and horizontal line segments. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + var STRAIGHT = "Straight"; + + var Straight = function (params) { + this.type = STRAIGHT; + var _super = _jp.Connectors.AbstractConnector.apply(this, arguments); + + this._compute = function (paintInfo, _) { + _super.addSegment(this, STRAIGHT, {x1: paintInfo.sx, y1: paintInfo.sy, x2: paintInfo.startStubX, y2: paintInfo.startStubY}); + _super.addSegment(this, STRAIGHT, {x1: paintInfo.startStubX, y1: paintInfo.startStubY, x2: paintInfo.endStubX, y2: paintInfo.endStubY}); + _super.addSegment(this, STRAIGHT, {x1: paintInfo.endStubX, y1: paintInfo.endStubY, x2: paintInfo.tx, y2: paintInfo.ty}); + }; + }; + + _jp.Connectors.Straight = Straight; + _ju.extend(Straight, _jp.Connectors.AbstractConnector); + +}).call(typeof window !== 'undefined' ? window : this); +/* + * This file contains the SVG renderers. + * + * Copyright (c) 2010 - 2018 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + +// ************************** SVG utility methods ******************************************** + + "use strict"; + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil; + + var svgAttributeMap = { + "stroke-linejoin": "stroke-linejoin", + "stroke-dashoffset": "stroke-dashoffset", + "stroke-linecap": "stroke-linecap" + }, + STROKE_DASHARRAY = "stroke-dasharray", + DASHSTYLE = "dashstyle", + LINEAR_GRADIENT = "linearGradient", + RADIAL_GRADIENT = "radialGradient", + DEFS = "defs", + FILL = "fill", + STOP = "stop", + STROKE = "stroke", + STROKE_WIDTH = "stroke-width", + STYLE = "style", + NONE = "none", + JSPLUMB_GRADIENT = "jsplumb_gradient_", + LINE_WIDTH = "strokeWidth", + ns = { + svg: "http://www.w3.org/2000/svg" + }, + _attr = function (node, attributes) { + for (var i in attributes) { + node.setAttribute(i, "" + attributes[i]); + } + }, + _node = function (name, attributes) { + attributes = attributes || {}; + attributes.version = "1.1"; + attributes.xmlns = ns.svg; + return _jp.createElementNS(ns.svg, name, null, null, attributes); + }, + _pos = function (d) { + return "position:absolute;left:" + d[0] + "px;top:" + d[1] + "px"; + }, + _clearGradient = function (parent) { + var els = parent.querySelectorAll(" defs,linearGradient,radialGradient"); + for (var i = 0; i < els.length; i++) { + els[i].parentNode.removeChild(els[i]); + } + }, + _updateGradient = function (parent, node, style, dimensions, uiComponent) { + var id = JSPLUMB_GRADIENT + uiComponent._jsPlumb.instance.idstamp(); + // first clear out any existing gradient + _clearGradient(parent); + // this checks for an 'offset' property in the gradient, and in the absence of it, assumes + // we want a linear gradient. if it's there, we create a radial gradient. + // it is possible that a more explicit means of defining the gradient type would be + // better. relying on 'offset' means that we can never have a radial gradient that uses + // some default offset, for instance. + // issue 244 suggested the 'gradientUnits' attribute; without this, straight/flowchart connectors with gradients would + // not show gradients when the line was perfectly horizontal or vertical. + var g; + if (!style.gradient.offset) { + g = _node(LINEAR_GRADIENT, {id: id, gradientUnits: "userSpaceOnUse"}); + } + else { + g = _node(RADIAL_GRADIENT, { id: id }); + } + + var defs = _node(DEFS); + parent.appendChild(defs); + defs.appendChild(g); + + // the svg radial gradient seems to treat stops in the reverse + // order to how canvas does it. so we want to keep all the maths the same, but + // iterate the actual style declarations in reverse order, if the x indexes are not in order. + for (var i = 0; i < style.gradient.stops.length; i++) { + var styleToUse = uiComponent.segment === 1 || uiComponent.segment === 2 ? i : style.gradient.stops.length - 1 - i, + stopColor = style.gradient.stops[styleToUse][1], + s = _node(STOP, {"offset": Math.floor(style.gradient.stops[i][0] * 100) + "%", "stop-color": stopColor}); + + g.appendChild(s); + } + var applyGradientTo = style.stroke ? STROKE : FILL; + node.setAttribute(applyGradientTo, "url(#" + id + ")"); + }, + _applyStyles = function (parent, node, style, dimensions, uiComponent) { + + node.setAttribute(FILL, style.fill ? style.fill : NONE); + node.setAttribute(STROKE, style.stroke ? style.stroke : NONE); + + if (style.gradient) { + _updateGradient(parent, node, style, dimensions, uiComponent); + } + else { + // make sure we clear any existing gradient + _clearGradient(parent); + node.setAttribute(STYLE, ""); + } + + if (style.strokeWidth) { + node.setAttribute(STROKE_WIDTH, style.strokeWidth); + } + + // in SVG there is a stroke-dasharray attribute we can set, and its syntax looks like + // the syntax in VML but is actually kind of nasty: values are given in the pixel + // coordinate space, whereas in VML they are multiples of the width of the stroked + // line, which makes a lot more sense. for that reason, jsPlumb is supporting both + // the native svg 'stroke-dasharray' attribute, and also the 'dashstyle' concept from + // VML, which will be the preferred method. the code below this converts a dashstyle + // attribute given in terms of stroke width into a pixel representation, by using the + // stroke's lineWidth. + if (style[DASHSTYLE] && style[LINE_WIDTH] && !style[STROKE_DASHARRAY]) { + var sep = style[DASHSTYLE].indexOf(",") === -1 ? " " : ",", + parts = style[DASHSTYLE].split(sep), + styleToUse = ""; + parts.forEach(function (p) { + styleToUse += (Math.floor(p * style.strokeWidth) + sep); + }); + node.setAttribute(STROKE_DASHARRAY, styleToUse); + } + else if (style[STROKE_DASHARRAY]) { + node.setAttribute(STROKE_DASHARRAY, style[STROKE_DASHARRAY]); + } + + // extra attributes such as join type, dash offset. + for (var i in svgAttributeMap) { + if (style[i]) { + node.setAttribute(svgAttributeMap[i], style[i]); + } + } + }, + _appendAtIndex = function (svg, path, idx) { + if (svg.childNodes.length > idx) { + svg.insertBefore(path, svg.childNodes[idx]); + } + else { + svg.appendChild(path); + } + }; + + /** + utility methods for other objects to use. + */ + _ju.svg = { + node: _node, + attr: _attr, + pos: _pos + }; + + // ************************** / SVG utility methods ******************************************** + + /* + * Base class for SVG components. + */ + var SvgComponent = function (params) { + var pointerEventsSpec = params.pointerEventsSpec || "all", renderer = {}; + + _jp.jsPlumbUIComponent.apply(this, params.originalArgs); + this.canvas = null; + this.path = null; + this.svg = null; + this.bgCanvas = null; + + var clazz = params.cssClass + " " + (params.originalArgs[0].cssClass || ""), + svgParams = { + "style": "", + "width": 0, + "height": 0, + "pointer-events": pointerEventsSpec, + "position": "absolute" + }; + + this.svg = _node("svg", svgParams); + + if (params.useDivWrapper) { + this.canvas = _jp.createElement("div", { position : "absolute" }); + _ju.sizeElement(this.canvas, 0, 0, 1, 1); + this.canvas.className = clazz; + } + else { + _attr(this.svg, { "class": clazz }); + this.canvas = this.svg; + } + + params._jsPlumb.appendElement(this.canvas, params.originalArgs[0].parent); + if (params.useDivWrapper) { + this.canvas.appendChild(this.svg); + } + + var displayElements = [ this.canvas ]; + this.getDisplayElements = function () { + return displayElements; + }; + + this.appendDisplayElement = function (el) { + displayElements.push(el); + }; + + this.paint = function (style, anchor, extents) { + if (style != null) { + + var xy = [ this.x, this.y ], wh = [ this.w, this.h ], p; + if (extents != null) { + if (extents.xmin < 0) { + xy[0] += extents.xmin; + } + if (extents.ymin < 0) { + xy[1] += extents.ymin; + } + wh[0] = extents.xmax + ((extents.xmin < 0) ? -extents.xmin : 0); + wh[1] = extents.ymax + ((extents.ymin < 0) ? -extents.ymin : 0); + } + + if (params.useDivWrapper) { + _ju.sizeElement(this.canvas, xy[0], xy[1], wh[0], wh[1]); + xy[0] = 0; + xy[1] = 0; + p = _pos([ 0, 0 ]); + } + else { + p = _pos([ xy[0], xy[1] ]); + } + + renderer.paint.apply(this, arguments); + + _attr(this.svg, { + "style": p, + "width": wh[0] || 0, + "height": wh[1] || 0 + }); + } + }; + + return { + renderer: renderer + }; + }; + + _ju.extend(SvgComponent, _jp.jsPlumbUIComponent, { + cleanup: function (force) { + if (force || this.typeId == null) { + if (this.canvas) { + this.canvas._jsPlumb = null; + } + if (this.svg) { + this.svg._jsPlumb = null; + } + if (this.bgCanvas) { + this.bgCanvas._jsPlumb = null; + } + + if (this.canvas && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + if (this.bgCanvas && this.bgCanvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + + this.svg = null; + this.canvas = null; + this.path = null; + this.group = null; + } + else { + // if not a forced cleanup, just detach from DOM for now. + if (this.canvas && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + if (this.bgCanvas && this.bgCanvas.parentNode) { + this.bgCanvas.parentNode.removeChild(this.bgCanvas); + } + } + }, + reattach:function(instance) { + var c = instance.getContainer(); + if (this.canvas && this.canvas.parentNode == null) { + c.appendChild(this.canvas); + } + if (this.bgCanvas && this.bgCanvas.parentNode == null) { + c.appendChild(this.bgCanvas); + } + }, + setVisible: function (v) { + if (this.canvas) { + this.canvas.style.display = v ? "block" : "none"; + } + } + }); + + /* + * Base class for SVG connectors. + */ + _jp.ConnectorRenderers.svg = function (params) { + var self = this, + _super = SvgComponent.apply(this, [ + { + cssClass: params._jsPlumb.connectorClass, + originalArgs: arguments, + pointerEventsSpec: "none", + _jsPlumb: params._jsPlumb + } + ]); + + _super.renderer.paint = function (style, anchor, extents) { + + var segments = self.getSegments(), p = "", offset = [0, 0]; + if (extents.xmin < 0) { + offset[0] = -extents.xmin; + } + if (extents.ymin < 0) { + offset[1] = -extents.ymin; + } + + if (segments.length > 0) { + + p = self.getPathData(); + + var a = { + d: p, + transform: "translate(" + offset[0] + "," + offset[1] + ")", + "pointer-events": params["pointer-events"] || "visibleStroke" + }, + outlineStyle = null, + d = [self.x, self.y, self.w, self.h]; + + // outline style. actually means drawing an svg object underneath the main one. + if (style.outlineStroke) { + var outlineWidth = style.outlineWidth || 1, + outlineStrokeWidth = style.strokeWidth + (2 * outlineWidth); + outlineStyle = _jp.extend({}, style); + delete outlineStyle.gradient; + outlineStyle.stroke = style.outlineStroke; + outlineStyle.strokeWidth = outlineStrokeWidth; + + if (self.bgPath == null) { + self.bgPath = _node("path", a); + _jp.addClass(self.bgPath, _jp.connectorOutlineClass); + _appendAtIndex(self.svg, self.bgPath, 0); + } + else { + _attr(self.bgPath, a); + } + + _applyStyles(self.svg, self.bgPath, outlineStyle, d, self); + } + + if (self.path == null) { + self.path = _node("path", a); + _appendAtIndex(self.svg, self.path, style.outlineStroke ? 1 : 0); + } + else { + _attr(self.path, a); + } + + _applyStyles(self.svg, self.path, style, d, self); + } + }; + }; + _ju.extend(_jp.ConnectorRenderers.svg, SvgComponent); + +// ******************************* svg segment renderer ***************************************************** + + +// ******************************* /svg segments ***************************************************** + + /* + * Base class for SVG endpoints. + */ + var SvgEndpoint = _jp.SvgEndpoint = function (params) { + var _super = SvgComponent.apply(this, [ + { + cssClass: params._jsPlumb.endpointClass, + originalArgs: arguments, + pointerEventsSpec: "all", + useDivWrapper: true, + _jsPlumb: params._jsPlumb + } + ]); + + _super.renderer.paint = function (style) { + var s = _jp.extend({}, style); + if (s.outlineStroke) { + s.stroke = s.outlineStroke; + } + + if (this.node == null) { + this.node = this.makeNode(s); + this.svg.appendChild(this.node); + } + else if (this.updateNode != null) { + this.updateNode(this.node); + } + _applyStyles(this.svg, this.node, s, [ this.x, this.y, this.w, this.h ], this); + _pos(this.node, [ this.x, this.y ]); + }.bind(this); + + }; + _ju.extend(SvgEndpoint, SvgComponent); + + /* + * SVG Dot Endpoint + */ + _jp.Endpoints.svg.Dot = function () { + _jp.Endpoints.Dot.apply(this, arguments); + SvgEndpoint.apply(this, arguments); + this.makeNode = function (style) { + return _node("circle", { + "cx": this.w / 2, + "cy": this.h / 2, + "r": this.radius + }); + }; + this.updateNode = function (node) { + _attr(node, { + "cx": this.w / 2, + "cy": this.h / 2, + "r": this.radius + }); + }; + }; + _ju.extend(_jp.Endpoints.svg.Dot, [_jp.Endpoints.Dot, SvgEndpoint]); + + /* + * SVG Rectangle Endpoint + */ + _jp.Endpoints.svg.Rectangle = function () { + _jp.Endpoints.Rectangle.apply(this, arguments); + SvgEndpoint.apply(this, arguments); + this.makeNode = function (style) { + return _node("rect", { + "width": this.w, + "height": this.h + }); + }; + this.updateNode = function (node) { + _attr(node, { + "width": this.w, + "height": this.h + }); + }; + }; + _ju.extend(_jp.Endpoints.svg.Rectangle, [_jp.Endpoints.Rectangle, SvgEndpoint]); + + /* + * SVG Image Endpoint is the default image endpoint. + */ + _jp.Endpoints.svg.Image = _jp.Endpoints.Image; + /* + * Blank endpoint in svg renderer is the default Blank endpoint. + */ + _jp.Endpoints.svg.Blank = _jp.Endpoints.Blank; + /* + * Label overlay in svg renderer is the default Label overlay. + */ + _jp.Overlays.svg.Label = _jp.Overlays.Label; + /* + * Custom overlay in svg renderer is the default Custom overlay. + */ + _jp.Overlays.svg.Custom = _jp.Overlays.Custom; + + var AbstractSvgArrowOverlay = function (superclass, originalArgs) { + superclass.apply(this, originalArgs); + _jp.jsPlumbUIComponent.apply(this, originalArgs); + this.isAppendedAtTopLevel = false; + var self = this; + this.path = null; + this.paint = function (params, containerExtents) { + // only draws on connections, not endpoints. + if (params.component.svg && containerExtents) { + if (this.path == null) { + this.path = _node("path", { + "pointer-events": "all" + }); + params.component.svg.appendChild(this.path); + if (this.elementCreated) { + this.elementCreated(this.path, params.component); + } + + this.canvas = params.component.svg; // for the sake of completeness; this behaves the same as other overlays + } + var clazz = originalArgs && (originalArgs.length === 1) ? (originalArgs[0].cssClass || "") : "", + offset = [0, 0]; + + if (containerExtents.xmin < 0) { + offset[0] = -containerExtents.xmin; + } + if (containerExtents.ymin < 0) { + offset[1] = -containerExtents.ymin; + } + + _attr(this.path, { + "d": makePath(params.d), + "class": clazz, + stroke: params.stroke ? params.stroke : null, + fill: params.fill ? params.fill : null, + transform: "translate(" + offset[0] + "," + offset[1] + ")" + }); + } + }; + var makePath = function (d) { + return (isNaN(d.cxy.x) || isNaN(d.cxy.y)) ? "" : "M" + d.hxy.x + "," + d.hxy.y + + " L" + d.tail[0].x + "," + d.tail[0].y + + " L" + d.cxy.x + "," + d.cxy.y + + " L" + d.tail[1].x + "," + d.tail[1].y + + " L" + d.hxy.x + "," + d.hxy.y; + }; + this.transfer = function(target) { + if (target.canvas && this.path && this.path.parentNode) { + this.path.parentNode.removeChild(this.path); + target.canvas.appendChild(this.path); + } + }; + }; + _ju.extend(AbstractSvgArrowOverlay, [_jp.jsPlumbUIComponent, _jp.Overlays.AbstractOverlay], { + cleanup: function (force) { + if (this.path != null) { + if (force) { + this._jsPlumb.instance.removeElement(this.path); + } + else { + if (this.path.parentNode) { + this.path.parentNode.removeChild(this.path); + } + } + } + }, + reattach:function(instance, component) { + if (this.path && component.canvas) { + component.canvas.appendChild(this.path); + } + }, + setVisible: function (v) { + if (this.path != null) { + (this.path.style.display = (v ? "block" : "none")); + } + } + }); + + _jp.Overlays.svg.Arrow = function () { + AbstractSvgArrowOverlay.apply(this, [_jp.Overlays.Arrow, arguments]); + }; + _ju.extend(_jp.Overlays.svg.Arrow, [ _jp.Overlays.Arrow, AbstractSvgArrowOverlay ]); + + _jp.Overlays.svg.PlainArrow = function () { + AbstractSvgArrowOverlay.apply(this, [_jp.Overlays.PlainArrow, arguments]); + }; + _ju.extend(_jp.Overlays.svg.PlainArrow, [ _jp.Overlays.PlainArrow, AbstractSvgArrowOverlay ]); + + _jp.Overlays.svg.Diamond = function () { + AbstractSvgArrowOverlay.apply(this, [_jp.Overlays.Diamond, arguments]); + }; + _ju.extend(_jp.Overlays.svg.Diamond, [ _jp.Overlays.Diamond, AbstractSvgArrowOverlay ]); + + // a test + _jp.Overlays.svg.GuideLines = function () { + var path = null, self = this, p1_1, p1_2; + _jp.Overlays.GuideLines.apply(this, arguments); + this.paint = function (params, containerExtents) { + if (path == null) { + path = _node("path"); + params.connector.svg.appendChild(path); + self.attachListeners(path, params.connector); + self.attachListeners(path, self); + + p1_1 = _node("path"); + params.connector.svg.appendChild(p1_1); + self.attachListeners(p1_1, params.connector); + self.attachListeners(p1_1, self); + + p1_2 = _node("path"); + params.connector.svg.appendChild(p1_2); + self.attachListeners(p1_2, params.connector); + self.attachListeners(p1_2, self); + } + + var offset = [0, 0]; + if (containerExtents.xmin < 0) { + offset[0] = -containerExtents.xmin; + } + if (containerExtents.ymin < 0) { + offset[1] = -containerExtents.ymin; + } + + _attr(path, { + "d": makePath(params.head, params.tail), + stroke: "red", + fill: null, + transform: "translate(" + offset[0] + "," + offset[1] + ")" + }); + + _attr(p1_1, { + "d": makePath(params.tailLine[0], params.tailLine[1]), + stroke: "blue", + fill: null, + transform: "translate(" + offset[0] + "," + offset[1] + ")" + }); + + _attr(p1_2, { + "d": makePath(params.headLine[0], params.headLine[1]), + stroke: "green", + fill: null, + transform: "translate(" + offset[0] + "," + offset[1] + ")" + }); + }; + + var makePath = function (d1, d2) { + return "M " + d1.x + "," + d1.y + + " L" + d2.x + "," + d2.y; + }; + }; + _ju.extend(_jp.Overlays.svg.GuideLines, _jp.Overlays.GuideLines); +}).call(typeof window !== 'undefined' ? window : this); + +/* + * This file contains code used when jsPlumb is being rendered in a DOM. + * + * Copyright (c) 2010 - 2019 jsPlumb (hello@jsplumbtoolkit.com) + * + * https://jsplumbtoolkit.com + * https://github.com/jsplumb/jsplumb + * + * Dual licensed under the MIT and GPL2 licenses. + */ +; +(function () { + + "use strict"; + + var root = this, _jp = root.jsPlumb, _ju = root.jsPlumbUtil, + _jk = root.Katavorio, _jg = root.Biltong; + + var _getEventManager = function(instance) { + var e = instance._mottle; + if (!e) { + e = instance._mottle = new root.Mottle(); + } + return e; + }; + + var _getDragManager = function (instance, category) { + + category = category || "main"; + var key = "_katavorio_" + category; + var k = instance[key], + e = instance.getEventManager(); + + if (!k) { + k = new _jk({ + bind: e.on, + unbind: e.off, + getSize: _jp.getSize, + getConstrainingRectangle:function(el) { + return [ el.parentNode.scrollWidth, el.parentNode.scrollHeight ]; + }, + getPosition: function (el, relativeToRoot) { + // if this is a nested draggable then compute the offset against its own offsetParent, otherwise + // compute against the Container's origin. see also the getUIPosition method below. + var o = instance.getOffset(el, relativeToRoot, el._katavorioDrag ? el.offsetParent : null); + return [o.left, o.top]; + }, + setPosition: function (el, xy) { + el.style.left = xy[0] + "px"; + el.style.top = xy[1] + "px"; + }, + addClass: _jp.addClass, + removeClass: _jp.removeClass, + intersects: _jg.intersects, + indexOf: function(l, i) { return l.indexOf(i); }, + scope:instance.getDefaultScope(), + css: { + noSelect: instance.dragSelectClass, + droppable: "jtk-droppable", + draggable: "jtk-draggable", + drag: "jtk-drag", + selected: "jtk-drag-selected", + active: "jtk-drag-active", + hover: "jtk-drag-hover", + ghostProxy:"jtk-ghost-proxy" + } + }); + k.setZoom(instance.getZoom()); + instance[key] = k; + instance.bind("zoom", k.setZoom); + } + return k; + }; + + var _dragStart=function(params) { + var options = params.el._jsPlumbDragOptions; + var cont = true; + if (options.canDrag) { + cont = options.canDrag(); + } + if (cont) { + this.setHoverSuspended(true); + this.select({source: params.el}).addClass(this.elementDraggingClass + " " + this.sourceElementDraggingClass, true); + this.select({target: params.el}).addClass(this.elementDraggingClass + " " + this.targetElementDraggingClass, true); + this.setConnectionBeingDragged(true); + } + return cont; + }; + var _dragMove=function(params) { + var ui = this.getUIPosition(arguments, this.getZoom()); + if (ui != null) { + var o = params.el._jsPlumbDragOptions; + this.draw(params.el, ui, null, true); + if (o._dragging) { + this.addClass(params.el, "jtk-dragged"); + } + o._dragging = true; + } + }; + var _dragStop=function(params) { + var elements = params.selection, uip; + + var _one = function (_e) { + if (_e[1] != null) { + // run the reported offset through the code that takes parent containers + // into account, to adjust if necessary (issue 554) + uip = this.getUIPosition([{ + el:_e[2].el, + pos:[_e[1].left, _e[1].top] + }]); + this.draw(_e[2].el, uip); + } + + if (_e[0]._jsPlumbDragOptions != null) { + delete _e[0]._jsPlumbDragOptions._dragging; + } + + this.removeClass(_e[0], "jtk-dragged"); + this.select({source: _e[2].el}).removeClass(this.elementDraggingClass + " " + this.sourceElementDraggingClass, true); + this.select({target: _e[2].el}).removeClass(this.elementDraggingClass + " " + this.targetElementDraggingClass, true); + this.getDragManager().dragEnded(_e[2].el); + }.bind(this); + + for (var i = 0; i < elements.length; i++) { + _one(elements[i]); + } + + this.setHoverSuspended(false); + this.setConnectionBeingDragged(false); + }; + + var _animProps = function (o, p) { + var _one = function (pName) { + if (p[pName] != null) { + if (_ju.isString(p[pName])) { + var m = p[pName].match(/-=/) ? -1 : 1, + v = p[pName].substring(2); + return o[pName] + (m * v); + } + else { + return p[pName]; + } + } + else { + return o[pName]; + } + }; + return [ _one("left"), _one("top") ]; + }; + + var _genLoc = function (prefix, e) { + if (e == null) { + return [ 0, 0 ]; + } + var ts = _touches(e), t = _getTouch(ts, 0); + return [t[prefix + "X"], t[prefix + "Y"]]; + }, + _pageLocation = _genLoc.bind(this, "page"), + _screenLocation = _genLoc.bind(this, "screen"), + _clientLocation = _genLoc.bind(this, "client"), + _getTouch = function (touches, idx) { + return touches.item ? touches.item(idx) : touches[idx]; + }, + _touches = function (e) { + return e.touches && e.touches.length > 0 ? e.touches : + e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches : + e.targetTouches && e.targetTouches.length > 0 ? e.targetTouches : + [ e ]; + }; + + /** + Manages dragging for some instance of jsPlumb. + + TODO instead of this being accessed directly, it should subscribe to events on the jsPlumb instance: every method + in here is called directly by jsPlumb. But what should happen is that we have unpublished events that this listens + to. The only trick is getting one of these instantiated with every jsPlumb instance: it needs to have a hook somehow. + Basically the general idea is to pull ALL the drag code out (prototype method registrations plus this) into a + dedicated drag script), that does not necessarily need to be included. + + + */ + var DragManager = function (_currentInstance) { + var _draggables = {}, _dlist = [], _delements = {}, _elementsWithEndpoints = {}, + // elementids mapped to the draggable to which they belong. + _draggablesForElements = {}; + + /** + register some element as draggable. right now the drag init stuff is done elsewhere, and it is + possible that will continue to be the case. + */ + this.register = function (el) { + var id = _currentInstance.getId(el), + parentOffset; + + if (!_draggables[id]) { + _draggables[id] = el; + _dlist.push(el); + _delements[id] = {}; + } + + // look for child elements that have endpoints and register them against this draggable. + var _oneLevel = function (p) { + if (p) { + for (var i = 0; i < p.childNodes.length; i++) { + if (p.childNodes[i].nodeType !== 3 && p.childNodes[i].nodeType !== 8) { + var cEl = jsPlumb.getElement(p.childNodes[i]), + cid = _currentInstance.getId(p.childNodes[i], null, true); + if (cid && _elementsWithEndpoints[cid] && _elementsWithEndpoints[cid] > 0) { + if (!parentOffset) { + parentOffset = _currentInstance.getOffset(el); + } + var cOff = _currentInstance.getOffset(cEl); + _delements[id][cid] = { + id: cid, + offset: { + left: cOff.left - parentOffset.left, + top: cOff.top - parentOffset.top + } + }; + _draggablesForElements[cid] = id; + } + _oneLevel(p.childNodes[i]); + } + } + } + }; + + _oneLevel(el); + }; + + // refresh the offsets for child elements of this element. + this.updateOffsets = function (elId, childOffsetOverrides) { + if (elId != null) { + childOffsetOverrides = childOffsetOverrides || {}; + var domEl = jsPlumb.getElement(elId), + id = _currentInstance.getId(domEl), + children = _delements[id], + parentOffset; + + if (children) { + for (var i in children) { + if (children.hasOwnProperty(i)) { + var cel = jsPlumb.getElement(i), + cOff = childOffsetOverrides[i] || _currentInstance.getOffset(cel); + + // do not update if we have a value already and we'd just be writing 0,0 + if (cel.offsetParent == null && _delements[id][i] != null) { + continue; + } + + if (!parentOffset) { + parentOffset = _currentInstance.getOffset(domEl); + } + + _delements[id][i] = { + id: i, + offset: { + left: cOff.left - parentOffset.left, + top: cOff.top - parentOffset.top + } + }; + _draggablesForElements[i] = id; + } + } + } + } + }; + + /** + notification that an endpoint was added to the given el. we go up from that el's parent + node, looking for a parent that has been registered as a draggable. if we find one, we add this + el to that parent's list of elements to update on drag (if it is not there already) + */ + this.endpointAdded = function (el, id) { + + id = id || _currentInstance.getId(el); + + var b = document.body, + p = el.parentNode; + + _elementsWithEndpoints[id] = _elementsWithEndpoints[id] ? _elementsWithEndpoints[id] + 1 : 1; + + while (p != null && p !== b) { + var pid = _currentInstance.getId(p, null, true); + if (pid && _draggables[pid]) { + var pLoc = _currentInstance.getOffset(p); + + if (_delements[pid][id] == null) { + var cLoc = _currentInstance.getOffset(el); + _delements[pid][id] = { + id: id, + offset: { + left: cLoc.left - pLoc.left, + top: cLoc.top - pLoc.top + } + }; + _draggablesForElements[id] = pid; + } + break; + } + p = p.parentNode; + } + }; + + this.endpointDeleted = function (endpoint) { + if (_elementsWithEndpoints[endpoint.elementId]) { + _elementsWithEndpoints[endpoint.elementId]--; + if (_elementsWithEndpoints[endpoint.elementId] <= 0) { + for (var i in _delements) { + if (_delements.hasOwnProperty(i) && _delements[i]) { + delete _delements[i][endpoint.elementId]; + delete _draggablesForElements[endpoint.elementId]; + } + } + } + } + }; + + this.changeId = function (oldId, newId) { + _delements[newId] = _delements[oldId]; + _delements[oldId] = {}; + _draggablesForElements[newId] = _draggablesForElements[oldId]; + _draggablesForElements[oldId] = null; + }; + + this.getElementsForDraggable = function (id) { + return _delements[id]; + }; + + this.elementRemoved = function (elementId) { + var elId = _draggablesForElements[elementId]; + if (elId) { + delete _delements[elId][elementId]; + delete _draggablesForElements[elementId]; + } + }; + + this.reset = function () { + _draggables = {}; + _dlist = []; + _delements = {}; + _elementsWithEndpoints = {}; + }; + + // + // notification drag ended. We check automatically if need to update some + // ancestor's offsets. + // + this.dragEnded = function (el) { + if (el.offsetParent != null) { + var id = _currentInstance.getId(el), + ancestor = _draggablesForElements[id]; + + if (ancestor) { + this.updateOffsets(ancestor); + } + } + }; + + this.setParent = function (el, elId, p, pId, currentChildLocation) { + var current = _draggablesForElements[elId]; + if (!_delements[pId]) { + _delements[pId] = {}; + } + var pLoc = _currentInstance.getOffset(p), + cLoc = currentChildLocation || _currentInstance.getOffset(el); + + if (current && _delements[current]) { + delete _delements[current][elId]; + } + + _delements[pId][elId] = { + id:elId, + offset : { + left: cLoc.left - pLoc.left, + top: cLoc.top - pLoc.top + } + }; + _draggablesForElements[elId] = pId; + }; + + this.clearParent = function(el, elId) { + var current = _draggablesForElements[elId]; + if (current) { + delete _delements[current][elId]; + delete _draggablesForElements[elId]; + } + }; + + this.revalidateParent = function(el, elId, childOffset) { + var current = _draggablesForElements[elId]; + if (current) { + var co = {}; + co[elId] = childOffset; + this.updateOffsets(current, co); + _currentInstance.revalidate(current); + } + }; + + this.getDragAncestor = function (el) { + var de = jsPlumb.getElement(el), + id = _currentInstance.getId(de), + aid = _draggablesForElements[id]; + + if (aid) { + return jsPlumb.getElement(aid); + } + else { + return null; + } + }; + + }; + + var _setClassName = function (el, cn, classList) { + cn = _ju.fastTrim(cn); + if (typeof el.className.baseVal !== "undefined") { + el.className.baseVal = cn; + } + else { + el.className = cn; + } + + // recent (i currently have 61.0.3163.100) version of chrome do not update classList when you set the base val + // of an svg element's className. in the long run we'd like to move to just using classList anyway + try { + var cl = el.classList; + if (cl != null) { + while (cl.length > 0) { + cl.remove(cl.item(0)); + } + for (var i = 0; i < classList.length; i++) { + if (classList[i]) { + cl.add(classList[i]); + } + } + } + } + catch(e) { + // not fatal + _ju.log("JSPLUMB: cannot set class list", e); + } + }, + _getClassName = function (el) { + return (typeof el.className.baseVal === "undefined") ? el.className : el.className.baseVal; + }, + _classManip = function (el, classesToAdd, classesToRemove) { + classesToAdd = classesToAdd == null ? [] : _ju.isArray(classesToAdd) ? classesToAdd : classesToAdd.split(/\s+/); + classesToRemove = classesToRemove == null ? [] : _ju.isArray(classesToRemove) ? classesToRemove : classesToRemove.split(/\s+/); + + var className = _getClassName(el), + curClasses = className.split(/\s+/); + + var _oneSet = function (add, classes) { + for (var i = 0; i < classes.length; i++) { + if (add) { + if (curClasses.indexOf(classes[i]) === -1) { + curClasses.push(classes[i]); + } + } + else { + var idx = curClasses.indexOf(classes[i]); + if (idx !== -1) { + curClasses.splice(idx, 1); + } + } + } + }; + + _oneSet(true, classesToAdd); + _oneSet(false, classesToRemove); + + _setClassName(el, curClasses.join(" "), curClasses); + }; + + root.jsPlumb.extend(root.jsPlumbInstance.prototype, { + + headless: false, + + pageLocation: _pageLocation, + screenLocation: _screenLocation, + clientLocation: _clientLocation, + + getDragManager:function() { + if (this.dragManager == null) { + this.dragManager = new DragManager(this); + } + + return this.dragManager; + }, + + recalculateOffsets:function(elId) { + this.getDragManager().updateOffsets(elId); + }, + + createElement:function(tag, style, clazz, atts) { + return this.createElementNS(null, tag, style, clazz, atts); + }, + + createElementNS:function(ns, tag, style, clazz, atts) { + var e = ns == null ? document.createElement(tag) : document.createElementNS(ns, tag); + var i; + style = style || {}; + for (i in style) { + e.style[i] = style[i]; + } + + if (clazz) { + e.className = clazz; + } + + atts = atts || {}; + for (i in atts) { + e.setAttribute(i, "" + atts[i]); + } + + return e; + }, + + getAttribute: function (el, attName) { + return el.getAttribute != null ? el.getAttribute(attName) : null; + }, + + setAttribute: function (el, a, v) { + if (el.setAttribute != null) { + el.setAttribute(a, v); + } + }, + + setAttributes: function (el, atts) { + for (var i in atts) { + if (atts.hasOwnProperty(i)) { + el.setAttribute(i, atts[i]); + } + } + }, + appendToRoot: function (node) { + document.body.appendChild(node); + }, + getRenderModes: function () { + return [ "svg" ]; + }, + getClass:_getClassName, + addClass: function (el, clazz) { + jsPlumb.each(el, function (e) { + _classManip(e, clazz); + }); + }, + hasClass: function (el, clazz) { + el = jsPlumb.getElement(el); + if (el.classList) { + return el.classList.contains(clazz); + } + else { + return _getClassName(el).indexOf(clazz) !== -1; + } + }, + removeClass: function (el, clazz) { + jsPlumb.each(el, function (e) { + _classManip(e, null, clazz); + }); + }, + toggleClass:function(el, clazz) { + if (jsPlumb.hasClass(el, clazz)) { + jsPlumb.removeClass(el, clazz); + } else { + jsPlumb.addClass(el, clazz); + } + }, + updateClasses: function (el, toAdd, toRemove) { + jsPlumb.each(el, function (e) { + _classManip(e, toAdd, toRemove); + }); + }, + setClass: function (el, clazz) { + if (clazz != null) { + jsPlumb.each(el, function (e) { + _setClassName(e, clazz, clazz.split(/\s+/)); + }); + } + }, + setPosition: function (el, p) { + el.style.left = p.left + "px"; + el.style.top = p.top + "px"; + }, + getPosition: function (el) { + var _one = function (prop) { + var v = el.style[prop]; + return v ? v.substring(0, v.length - 2) : 0; + }; + return { + left: _one("left"), + top: _one("top") + }; + }, + getStyle:function(el, prop) { + if (typeof window.getComputedStyle !== 'undefined') { + return getComputedStyle(el, null).getPropertyValue(prop); + } else { + return el.currentStyle[prop]; + } + }, + getSelector: function (ctx, spec) { + var sel = null; + if (arguments.length === 1) { + sel = ctx.nodeType != null ? ctx : document.querySelectorAll(ctx); + } + else { + sel = ctx.querySelectorAll(spec); + } + + return sel; + }, + getOffset:function(el, relativeToRoot, container) { + el = jsPlumb.getElement(el); + container = container || this.getContainer(); + var out = { + left: el.offsetLeft, + top: el.offsetTop + }, + op = (relativeToRoot || (container != null && (el !== container && el.offsetParent !== container))) ? el.offsetParent : null, + _maybeAdjustScroll = function(offsetParent) { + if (offsetParent != null && offsetParent !== document.body && (offsetParent.scrollTop > 0 || offsetParent.scrollLeft > 0)) { + out.left -= offsetParent.scrollLeft; + out.top -= offsetParent.scrollTop; + } + }.bind(this); + + while (op != null) { + out.left += op.offsetLeft; + out.top += op.offsetTop; + _maybeAdjustScroll(op); + op = relativeToRoot ? op.offsetParent : + op.offsetParent === container ? null : op.offsetParent; + } + + // if container is scrolled and the element (or its offset parent) is not absolute or fixed, adjust accordingly. + if (container != null && !relativeToRoot && (container.scrollTop > 0 || container.scrollLeft > 0)) { + var pp = el.offsetParent != null ? this.getStyle(el.offsetParent, "position") : "static", + p = this.getStyle(el, "position"); + if (p !== "absolute" && p !== "fixed" && pp !== "absolute" && pp !== "fixed") { + out.left -= container.scrollLeft; + out.top -= container.scrollTop; + } + } + return out; + }, + // + // return x+y proportion of the given element's size corresponding to the location of the given event. + // + getPositionOnElement: function (evt, el, zoom) { + var box = typeof el.getBoundingClientRect !== "undefined" ? el.getBoundingClientRect() : { left: 0, top: 0, width: 0, height: 0 }, + body = document.body, + docElem = document.documentElement, + scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop, + scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft, + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + pst = 0, + psl = 0, + top = box.top + scrollTop - clientTop + (pst * zoom), + left = box.left + scrollLeft - clientLeft + (psl * zoom), + cl = jsPlumb.pageLocation(evt), + w = box.width || (el.offsetWidth * zoom), + h = box.height || (el.offsetHeight * zoom), + x = (cl[0] - left) / w, + y = (cl[1] - top) / h; + + return [ x, y ]; + }, + + /** + * Gets the absolute position of some element as read from the left/top properties in its style. + * @method getAbsolutePosition + * @param {Element} el The element to retrieve the absolute coordinates from. **Note** this is a DOM element, not a selector from the underlying library. + * @return {Number[]} [left, top] pixel values. + */ + getAbsolutePosition: function (el) { + var _one = function (s) { + var ss = el.style[s]; + if (ss) { + return parseFloat(ss.substring(0, ss.length - 2)); + } + }; + return [ _one("left"), _one("top") ]; + }, + + /** + * Sets the absolute position of some element by setting the left/top properties in its style. + * @method setAbsolutePosition + * @param {Element} el The element to set the absolute coordinates on. **Note** this is a DOM element, not a selector from the underlying library. + * @param {Number[]} xy x and y coordinates + * @param {Number[]} [animateFrom] Optional previous xy to animate from. + * @param {Object} [animateOptions] Options for the animation. + */ + setAbsolutePosition: function (el, xy, animateFrom, animateOptions) { + if (animateFrom) { + this.animate(el, { + left: "+=" + (xy[0] - animateFrom[0]), + top: "+=" + (xy[1] - animateFrom[1]) + }, animateOptions); + } + else { + el.style.left = xy[0] + "px"; + el.style.top = xy[1] + "px"; + } + }, + /** + * gets the size for the element, in an array : [ width, height ]. + */ + getSize: function (el) { + return [ el.offsetWidth, el.offsetHeight ]; + }, + getWidth: function (el) { + return el.offsetWidth; + }, + getHeight: function (el) { + return el.offsetHeight; + }, + getRenderMode : function() { return "svg"; }, + draggable : function (el, options) { + var info; + el = _ju.isArray(el) || (el.length != null && !_ju.isString(el)) ? el: [ el ]; + Array.prototype.slice.call(el).forEach(function(_el) { + info = this.info(_el); + if (info.el) { + this._initDraggableIfNecessary(info.el, true, options, info.id, true); + } + }.bind(this)); + return this; + }, + snapToGrid : function(el, x, y) { + var out = []; + var _oneEl = function(_el) { + var info = this.info(_el); + if (info.el != null && info.el._katavorioDrag) { + var snapped = info.el._katavorioDrag.snap(x, y); + this.revalidate(info.el); + out.push([info.el, snapped]); + } + }.bind(this); + + // if you call this method with 0 arguments or 2 arguments it is assumed you want to snap all managed elements to + // a grid. if you supply one argument or 3, then you are assumed to be specifying one element. + if(arguments.length === 1 || arguments.length === 3) { + _oneEl(el, x, y); + } else { + var _me = this.getManagedElements(); + for (var mel in _me) { + _oneEl(mel, arguments[0], arguments[1]); + } + } + + return out; + }, + initDraggable: function (el, options, category) { + _getDragManager(this, category).draggable(el, options); + el._jsPlumbDragOptions = options; + }, + destroyDraggable: function (el, category) { + _getDragManager(this, category).destroyDraggable(el); + delete el._jsPlumbDragOptions; + }, + unbindDraggable: function (el, evt, fn, category) { + _getDragManager(this, category).destroyDraggable(el, evt, fn); + }, + setDraggable : function (element, draggable) { + return jsPlumb.each(element, function (el) { + if (this.isDragSupported(el)) { + this._draggableStates[this.getAttribute(el, "id")] = draggable; + this.setElementDraggable(el, draggable); + } + }.bind(this)); + }, + _draggableStates : {}, + /* + * toggles the draggable state of the given element(s). + * el is either an id, or an element object, or a list of ids/element objects. + */ + toggleDraggable : function (el) { + var state; + jsPlumb.each(el, function (el) { + var elId = this.getAttribute(el, "id"); + state = this._draggableStates[elId] == null ? false : this._draggableStates[elId]; + state = !state; + this._draggableStates[elId] = state; + this.setDraggable(el, state); + return state; + }.bind(this)); + return state; + }, + _initDraggableIfNecessary : function (element, isDraggable, dragOptions, id, fireEvent) { + // TODO FIRST: move to DragManager. including as much of the decision to init dragging as possible. + if (!jsPlumb.headless) { + var _draggable = isDraggable == null ? false : isDraggable; + if (_draggable) { + if (jsPlumb.isDragSupported(element, this)) { + var options = dragOptions || this.Defaults.DragOptions; + options = jsPlumb.extend({}, options); // make a copy. + if (!jsPlumb.isAlreadyDraggable(element, this)) { + var dragEvent = jsPlumb.dragEvents.drag, + stopEvent = jsPlumb.dragEvents.stop, + startEvent = jsPlumb.dragEvents.start; + + this.manage(id, element); + + options[startEvent] = _ju.wrap(options[startEvent], _dragStart.bind(this)); + + options[dragEvent] = _ju.wrap(options[dragEvent], _dragMove.bind(this)); + + options[stopEvent] = _ju.wrap(options[stopEvent], _dragStop.bind(this)); + + var elId = this.getId(element); // need ID + + this._draggableStates[elId] = true; + var draggable = this._draggableStates[elId]; + + options.disabled = draggable == null ? false : !draggable; + this.initDraggable(element, options); + this.getDragManager().register(element); + if (fireEvent) { + this.fire("elementDraggable", {el:element, options:options}); + } + } + else { + // already draggable. attach any start, drag or stop listeners to the current Drag. + if (dragOptions.force) { + this.initDraggable(element, options); + } + } + } + } + } + }, + animationSupported:true, + getElement: function (el) { + if (el == null) { + return null; + } + // here we pluck the first entry if el was a list of entries. + // this is not my favourite thing to do, but previous versions of + // jsplumb supported jquery selectors, and it is possible a selector + // will be passed in here. + el = typeof el === "string" ? el : el.length != null && el.enctype == null ? el[0] : el; + return typeof el === "string" ? document.getElementById(el) : el; + }, + removeElement: function (element) { + _getDragManager(this).elementRemoved(element); + this.getEventManager().remove(element); + }, + // + // this adapter supports a rudimentary animation function. no easing is supported. only + // left/top properties are supported. property delta args are expected to be in the form + // + // +=x.xxxx + // + // or + // + // -=x.xxxx + // + doAnimate: function (el, properties, options) { + options = options || {}; + var o = this.getOffset(el), + ap = _animProps(o, properties), + ldist = ap[0] - o.left, + tdist = ap[1] - o.top, + d = options.duration || 250, + step = 15, steps = d / step, + linc = (step / d) * ldist, + tinc = (step / d) * tdist, + idx = 0, + _int = setInterval(function () { + _jp.setPosition(el, { + left: o.left + (linc * (idx + 1)), + top: o.top + (tinc * (idx + 1)) + }); + if (options.step != null) { + options.step(idx, Math.ceil(steps)); + } + idx++; + if (idx >= steps) { + window.clearInterval(_int); + if (options.complete != null) { + options.complete(); + } + } + }, step); + }, + // DRAG/DROP + + + destroyDroppable: function (el, category) { + _getDragManager(this, category).destroyDroppable(el); + }, + unbindDroppable: function (el, evt, fn, category) { + _getDragManager(this, category).destroyDroppable(el, evt, fn); + }, + + droppable :function(el, options) { + el = _ju.isArray(el) || (el.length != null && !_ju.isString(el)) ? el: [ el ]; + var info; + options = options || {}; + options.allowLoopback = false; + Array.prototype.slice.call(el).forEach(function(_el) { + info = this.info(_el); + if (info.el) { + this.initDroppable(info.el, options); + } + }.bind(this)); + return this; + }, + + initDroppable: function (el, options, category) { + _getDragManager(this, category).droppable(el, options); + }, + isAlreadyDraggable: function (el) { + return el._katavorioDrag != null; + }, + isDragSupported: function (el, options) { + return true; + }, + isDropSupported: function (el, options) { + return true; + }, + isElementDraggable: function (el) { + el = _jp.getElement(el); + return el._katavorioDrag && el._katavorioDrag.isEnabled(); + }, + getDragObject: function (eventArgs) { + return eventArgs[0].drag.getDragElement(); + }, + getDragScope: function (el) { + return el._katavorioDrag && el._katavorioDrag.scopes.join(" ") || ""; + }, + getDropEvent: function (args) { + return args[0].e; + }, + getUIPosition: function (eventArgs, zoom) { + // here the position reported to us by Katavorio is relative to the element's offsetParent. For top + // level nodes that is fine, but if we have a nested draggable then its offsetParent is actually + // not going to be the jsplumb container; it's going to be some child of that element. In that case + // we want to adjust the UI position to account for the offsetParent's position relative to the Container + // origin. + var el = eventArgs[0].el; + if (el.offsetParent == null) { + return null; + } + var finalPos = eventArgs[0].finalPos || eventArgs[0].pos; + var p = { left:finalPos[0], top:finalPos[1] }; + if (el._katavorioDrag && el.offsetParent !== this.getContainer()) { + var oc = this.getOffset(el.offsetParent); + p.left += oc.left; + p.top += oc.top; + } + return p; + }, + setDragFilter: function (el, filter, _exclude) { + if (el._katavorioDrag) { + el._katavorioDrag.setFilter(filter, _exclude); + } + }, + setElementDraggable: function (el, draggable) { + el = _jp.getElement(el); + if (el._katavorioDrag) { + el._katavorioDrag.setEnabled(draggable); + } + }, + setDragScope: function (el, scope) { + if (el._katavorioDrag) { + el._katavorioDrag.k.setDragScope(el, scope); + } + }, + setDropScope:function(el, scope) { + if (el._katavorioDrop && el._katavorioDrop.length > 0) { + el._katavorioDrop[0].k.setDropScope(el, scope); + } + }, + addToPosse:function(el, spec) { + var specs = Array.prototype.slice.call(arguments, 1); + var dm = _getDragManager(this); + _jp.each(el, function(_el) { + _el = [ _jp.getElement(_el) ]; + _el.push.apply(_el, specs ); + dm.addToPosse.apply(dm, _el); + }); + }, + setPosse:function(el, spec) { + var specs = Array.prototype.slice.call(arguments, 1); + var dm = _getDragManager(this); + _jp.each(el, function(_el) { + _el = [ _jp.getElement(_el) ]; + _el.push.apply(_el, specs ); + dm.setPosse.apply(dm, _el); + }); + }, + removeFromPosse:function(el, posseId) { + var specs = Array.prototype.slice.call(arguments, 1); + var dm = _getDragManager(this); + _jp.each(el, function(_el) { + _el = [ _jp.getElement(_el) ]; + _el.push.apply(_el, specs ); + dm.removeFromPosse.apply(dm, _el); + }); + }, + removeFromAllPosses:function(el) { + var dm = _getDragManager(this); + _jp.each(el, function(_el) { dm.removeFromAllPosses(_jp.getElement(_el)); }); + }, + setPosseState:function(el, posseId, state) { + var dm = _getDragManager(this); + _jp.each(el, function(_el) { dm.setPosseState(_jp.getElement(_el), posseId, state); }); + }, + dragEvents: { + 'start': 'start', 'stop': 'stop', 'drag': 'drag', 'step': 'step', + 'over': 'over', 'out': 'out', 'drop': 'drop', 'complete': 'complete', + 'beforeStart':'beforeStart' + }, + animEvents: { + 'step': "step", 'complete': 'complete' + }, + stopDrag: function (el) { + if (el._katavorioDrag) { + el._katavorioDrag.abort(); + } + }, + addToDragSelection: function (spec) { + _getDragManager(this).select(spec); + }, + removeFromDragSelection: function (spec) { + _getDragManager(this).deselect(spec); + }, + clearDragSelection: function () { + _getDragManager(this).deselectAll(); + }, + trigger: function (el, event, originalEvent, payload) { + this.getEventManager().trigger(el, event, originalEvent, payload); + }, + doReset:function() { + // look for katavorio instances and reset each one if found. + for (var key in this) { + if (key.indexOf("_katavorio_") === 0) { + this[key].reset(); + } + } + }, + getEventManager:function() { + return _getEventManager(this); + }, + on : function(el, event, callback) { + // TODO: here we would like to map the tap event if we know its + // an internal bind to a click. we have to know its internal because only + // then can we be sure that the UP event wont be consumed (tap is a synthesized + // event from a mousedown followed by a mouseup). + //event = { "click":"tap", "dblclick":"dbltap"}[event] || event; + this.getEventManager().on.apply(this, arguments); + return this; + }, + off : function(el, event, callback) { + this.getEventManager().off.apply(this, arguments); + return this; + } + + }); + + var ready = function (f) { + var _do = function () { + if (/complete|loaded|interactive/.test(document.readyState) && typeof(document.body) !== "undefined" && document.body != null) { + f(); + } + else { + setTimeout(_do, 9); + } + }; + + _do(); + }; + ready(_jp.init); + +}).call(typeof window !== 'undefined' ? window : this); diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/mixins.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/mixins.js" new file mode 100644 index 0000000..37b0d58 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/mixins.js" @@ -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銆丗lowchart锛孊ezier銆丼traight + 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, // 浣嶇疆锛屽缓璁娇鐢�0锝�1涔嬮棿 + 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銆乧anvas + RenderMode: 'svg', + // 榧犳爣婊戣繃绾跨殑鏍峰紡 + HoverPaintStyle: {stroke: '#b0b2b5', strokeWidth: 1}, + // 婊戣繃閿氱偣鏁堟灉 + // EndpointHoverStyle: {fill: 'red'} + Scope: 'jsPlumb_DefaultScope' // 鑼冨洿锛屽叿鏈夌浉鍚宻cope鐨勭偣鎵嶅彲杩炴帴 + }, + /** + * 杩炵嚎鍙傛暟 + */ + jsplumbConnectOptions: { + isSource: true, + isTarget: true, + // 鍔ㄦ�侀敋鐐广�佹彁渚涗簡4涓柟鍚� Continuous銆丄utoDefault + anchor: 'Continuous', + // 璁剧疆杩炵嚎涓婇潰鐨刲abel鏍峰紡 + labelStyle: { + cssClass: 'flowLabel' + }, + // 淇敼浜唈splumb 婧愮爜锛屾敮鎸乴abel 涓虹┖浼犲叆鑷畾涔塻tyle + 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'} + } + } + } +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node.vue" new file mode 100644 index 0000000..5cd8e3d --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node.vue" @@ -0,0 +1,99 @@ +<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' && !disabled" 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, + disabled: { + typeof: Boolean, + default: false + } + }, + 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 + // 娣诲姞璇lass鍙互鎺ㄦ嫿杩炵嚎鍑烘潵锛寁iewOnly 鍙互鎺у埗鑺傜偣鏄惁杩愯缂栬緫 + 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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_filter.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_filter.vue" new file mode 100644 index 0000000..83a80fb --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_filter.vue" @@ -0,0 +1,201 @@ +<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" v-if="!disabled"> + + 娣诲姞瀛楁</el-button></div> + </div> + + <div> + <table> + <tr> + <td>瀛楁</td> + <td style="width:90px">鏉′欢</td> + <td class="value">鍊�</td> + <td style="width: 40px;" v-if="!disabled">鎿嶄綔</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="璇烽�夋嫨" :disabled="disabled"> + <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="璇烽�夋嫨" :disabled="disabled"> + <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" :disabled="disabled" /> + </el-select> + <el-select-v2 style="width: 100%;" v-else multiple size="small" :options="item.data" + v-model="item.value" placeholder="璇烽�夋嫨" :disabled="disabled"> + </el-select-v2> + </template> + <el-input v-else v-model="item.value" size="small" :disabled="disabled"></el-input> + </td> + <td @click="delItem(index)" class="item-del" v-if="!disabled"><i class="el-icon-delete"></i></td> + </tr> + </table> + </div> + <!-- <div> + <label>鑷畾涔塻ql</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 [] + } + }, + disabled:{ + typeof:Boolean, + default:false + } + }, + 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> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_form.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_form.vue" new file mode 100644 index 0000000..f942239 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_form.vue" @@ -0,0 +1,350 @@ +<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"> + <VolForm ref="form" style="padding:0 10px;" :label-width="130" :loadKey="false" :formFields="node" + :formRules="formRules" :disabled="disabled"> + </VolForm> + </div> + <div> + <node-filter :filters="node.filters" :disabled="disabled" :tableName="tableName" ref="filter"> + </node-filter> + </div> + </div> + <!-- <div class="el-node-form-tag"></div>--> + </div> + </div> +</template> + +<script> +// import { cloneDeep } from 'lodash' +import VolForm from '@/components/basic/VolForm.vue'; +import nodeFilter from './node_filter.vue'; +export default { + components: { + VolForm, + 'node-filter': nodeFilter + }, + props: { + disabled:{ + typeof:Boolean, + default:false + } + // 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, + colSize: 12 + } + ], + [ + { + dataKey: 'users', + hidden: false, + title: '瀹℃壒鐢ㄦ埛', + required: true, + field: 'userId', + data: [], + type: 'selectList', + colSize: 12 + } + , + { + dataKey: 'roles', + hidden: true, + title: '瑙掕壊淇℃伅', + required: true, + field: 'roleId', + + data: [], + type: 'select', + colSize: 12 + } + , + { + dataKey: 'dept', + hidden: true, + title: '閮ㄩ棬淇℃伅', + required: true, + field: 'deptId', + data: [], + type: 'select', + colSize: 12 + } + ], [ + { + 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: 'switch' + } + ], + ], + } + }, + 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(.vol-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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_menu.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_menu.vue" new file mode 100644 index 0000000..b76fa81 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/node_menu.vue" @@ -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', + // 涓嶄娇鐢℉5鍘熺敓鐨勯厤缃� + 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() { + /** + * 浠ヤ笅鏄负浜嗚В鍐冲湪鐏嫄娴忚鍣ㄤ笂鎺ㄦ嫿鏃跺脊鍑簍ab椤靛埌鎼滅储闂 + * @param event + */ + if (this.isFirefox()) { + document.body.ondrop = function (event) { + // 瑙e喅鐏嫄娴忚鍣ㄦ棤娉曡幏鍙栭紶鏍囨嫋鎷界粨鏉熺殑鍧愭爣闂 + 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> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/panel.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/panel.vue" new file mode 100644 index 0000000..84d29bc --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/panel.vue" @@ -0,0 +1,612 @@ +<!-- 瀹℃牳娴佺▼鎻掍欢鍩轰簬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> + <VolForm ref="form" style="padding: 10px;" :label-width="180" :loadKey="true" :formFields="formFields" + :disabled="disabled" :formRules="formRules"></VolForm> + <node-menu @addNode="addNode" ref="nodeMenu" v-if="!disabled"></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" + :disabled="disabled" @changeNodeSite="changeNodeSite" @nodeRightMenu="nodeRightMenu" + @clickNode="clickNode"> + </flow-node> + </template> + <!-- 缁欑敾甯冧竴涓粯璁ょ殑瀹藉害鍜岄珮搴� --> + <div style="position:absolute;top: 3000px;left: 4000px;"> </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" :disabled="disabled" + @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 VolForm from '@/components/basic/VolForm.vue'; +export default { + props: { + disabled: { + typeof: Boolean, + default: false + } + }, + 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 銆乴ine + type: undefined, + // 鑺傜偣ID + nodeId: undefined, + // 杩炵嚎ID + sourceId: undefined, + targetId: undefined + }, + zoom: 1 + } + }, + // 涓�浜涘熀纭�閰嶇疆绉诲姩璇ユ枃浠朵腑 + mixins: [easyFlowMixin], + components: { + draggable, flowNode, nodeMenu, FlowNodeForm, VolForm + }, + 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] + if (node.userId && node.userId != '') { + // userId涓烘暟鍊肩被鍨� + if (typeof node.userId == 'number'){ + node.userId = [node.userId] + } else { + node.userId = node.userId.split(',').map(Number); + } + } else { + node.userId = [] + } + // 璁剧疆婧愮偣锛屽彲浠ユ嫋鍑虹嚎杩炴帴鍏朵粬鑺傜偣 + this.jsPlumb.makeSource(node.id, lodash.merge(this.jsplumbSourceOptions, {})) + // // 璁剧疆鐩爣鐐癸紝鍏朵粬婧愮偣鎷栧嚭鐨勭嚎鍙互杩炴帴璇ヨ妭鐐� + this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions) + if (!node.viewOnly && !this.disabled) { + 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: this.disabled ? null : (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.disabled) + return + 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> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/utils.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/utils.js" new file mode 100644 index 0000000..2874a41 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/components/workflow/utils.js" @@ -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) +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/addrouters.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/addrouters.vue" new file mode 100644 index 0000000..28ed38f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/addrouters.vue" @@ -0,0 +1,351 @@ +<template> + <div> + <vol-box + v-model="showDetialBox" + :lazy="true" + :width="width" + :padding="15" + title="璺敱閰嶇疆" + :footer="true" + > + <el-row height="50"> + <el-col :span="24"> + <div class="grid-content right-text"> + <!-- <el-link type="primary" @click="deleteNode">绉婚櫎鑺傜偣</el-link> --> + <el-link type="primary" @click="addNode">娣诲姞鑺傜偣</el-link> + </div> + <div class="grid-content right-text"></div> + </el-col> + </el-row> + <el-row> + <el-col :span="6"> + <div + class="grid-content" + style="font-weight: bold; font-size: 18px; margin-left: 5%" + > + <el-form :rules="routeTypeRules" :model="routerType"> + <el-form-item label="璺敱绫诲瀷" label-width="120" prop="type"> + <el-select + v-model="routerType.type" + filterable + placeholder="璇烽�夋嫨" + > + <el-option + v-for="item in routerTypes" + :key="item.key" + :label="item.value" + :value="item.key" + > + <span style="float: left">{{ item.value }}</span> + <span + style="float: right; color: #8492a6; font-size: 13px" + >{{ item.key }}</span + > + </el-option> + </el-select> + </el-form-item> + </el-form> + </div> + </el-col> + </el-row> + <div style="overflow-x: auto"> + <el-row style="margin-top: 3%"> + <el-col :span="24"> + <el-steps align-center :active="routers.length"> + <el-step + v-for="(router, index) in routers" + :title="getTitle(index)" + :key="index" + icon="" + description="111" + style="min-width: 280px" + > + <template v-slot:description="{}"> + <div> + <el-form + :rules="rules" + class="demo-ruleForm" + :model="router" + label-width="110px" + style="margin-top: 1%" + ref="router" + > + <el-form-item + label="浣嶇疆缂栧彿" + placeholder="璇烽�夋嫨浣嶇疆缂栧彿" + prop="positionCode" + > + <el-select + filterable + v-model="router.positionCode" + @change="deviceCodeChange" + > + <el-option + v-for="deviceCode in filterDeviceCodes" + :key="deviceCode.key" + :label="deviceCode.key" + :value="deviceCode.key" + /> + </el-select> + </el-form-item> + <el-form-item + v-show=" + index == routers.length - 1 && + !router.positionCode.includes('SC') + " + label="瀛愪綅缃紪鍙�" + prop="childPositionCode" + > + <el-select + filterable + v-model="router.childPositionCode" + @change="childDeviceCodeChange" + > + <el-option + v-for="childDeviceCode in filterChildDeviceCodes" + :key="childDeviceCode.key" + :label="childDeviceCode.key" + :value="childDeviceCode.key" + /> + </el-select> + </el-form-item> + <el-form-item + v-show=" + router.positionCode.includes('SC') || + router.childPositionCode.includes('SC') + " + label="鍫嗗灈鏈鸿" + prop="sCRow" + > + <el-input v-model="router.sCRow"></el-input> + </el-form-item> + <el-form-item + v-show=" + router.positionCode.includes('SC') || + router.childPositionCode.includes('SC') + " + label="鍫嗗灈鏈哄垪" + prop="sCColumn" + > + <el-input v-model="router.sCColumn"></el-input> + </el-form-item> + <el-form-item + v-show=" + router.positionCode.includes('SC') || + router.childPositionCode.includes('SC') + " + label="鍫嗗灈鏈哄眰" + prop="sCLayer" + > + <el-input v-model="router.sCLayer"></el-input> + </el-form-item> + </el-form> + </div> + </template> + </el-step> + </el-steps> + </el-col> + </el-row> + </div> + <template #footer> + <div> + <el-button plain type="danger" @click="save" + ><i class="el-icon-check"></i>淇� 瀛�</el-button + > + <el-button type="primary" size="mini" @click="showDetialBox = false" + ><i class="el-icon-close"></i>鍏抽棴</el-button + > + </div> + </template> + </vol-box> + </div> +</template> + + <script> +import VolBox from "@/components/basic/VolBox.vue"; +import { el } from "element-plus/es/locale"; +export default { + components: { VolBox }, + data() { + return { + width: 1200, + active: 0, + showDetialBox: false, + routerTypes: [], + routerType: { type: "" }, + deviceCodes: [], + filterDeviceCodes: [], + childDeviceCodes: [], + filterChildDeviceCodes: [], + routers: [ + { + positionCode: "", + childPositionCode: "", + sCRow: "", + sCColumn: "", + sCLayer: "", + }, + { + positionCode: "", + childPositionCode: "", + sCRow: "", + sCColumn: "", + sCLayer: "", + }, + ], + rules: { + positionCode: [ + { required: true, message: "璇烽�夋嫨浣嶇疆缂栧彿", trigger: "change" }, + ], + }, + routeTypeRules: { + type: [ + { required: true, message: "璇烽�夋嫨璺敱绫诲瀷", trigger: "change" }, + ], + }, + }; + }, + methods: { + open() { + this.routerTypes = []; + this.routerType = { type: "" }; + this.deviceCode = ""; + this.deviceCodes = []; + this.filterDeviceCodes = []; + this.childDeviceCodes = []; + this.filterChildDeviceCodes = []; + this.routers = [ + { + positionCode: "", + childPositionCode: "", + sCRow: "", + sCColumn: "", + sCLayer: "", + }, + { + positionCode: "", + childPositionCode: "", + sCRow: "", + sCColumn: "", + sCLayer: "", + }, + ]; + this.showDetialBox = true; + this.getData(); + }, + getData() { + this.http.post("/api/Router/GetBaseRouterInfo", {}, true).then((x) => { + if (!x.status) return this.$message.error(x.message); + this.routerTypes = x.data.routerTypes; + this.deviceCodes = x.data.deviceCodes; + x.data.areaInfos.forEach((v) => { + this.deviceCodes.push(v); + }); + this.filterDeviceCodes = this.deviceCodes; + this.childDeviceCodes = this.deviceCodes; + this.filterChildDeviceCodes = this.deviceCodes; + }); + }, + addNode() { + this.routers.push({ + positionCode: "", + childPositionCode: "", + childPosiDeviceCode: "", + sCRow: "", + sCColumn: "", + sCLayer: "", + }); + }, + deleteNode() { + this.routers.splice(this.routers.length - 1, 1); + }, + getTitle(index) { + if (index === 0) { + return "璧风偣"; + } else if (index === this.routers.length - 1) { + return "缁堢偣"; + } else { + return "瀛愯妭鐐�" + index; + } + }, + save() { + this.$refs.router.forEach((x) => { + console.log(x); + x.validate((valid) => { + if (!valid) { + return false; + } + }); + }); + this.http + .post( + "/api/Router/AddRouters?routerType=" + this.routerType.type, + this.routers, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.$message.success("鎿嶄綔鎴愬姛"); + this.$emit("parentCall", ($vue) => { + $vue.refresh(); + }); + this.showDetialBox = false; + }); + }, + deviceCodeChange(value) { + var positionCodes = []; + this.routers.forEach((x) => { + if (x.positionCode && x.positionCode != "") { + positionCodes.push(x.positionCode); + } + }); + this.filterDeviceCodes = this.deviceCodes.filter( + (x) => !positionCodes.includes(x.key) + ); + }, + childDeviceCodeChange(value) { + var positionCodes = []; + this.routers.forEach((x) => { + if (x.childPositionCode && x.childPositionCode != "") { + positionCodes.push(x.childPositionCode); + } + }); + this.filterChildDeviceCodes = this.childDeviceCodes.filter( + (x) => !positionCodes.includes(x.key) + ); + }, + }, + created() {}, +}; +</script> + + <style scoped> +.el-col { + border-radius: 4px; +} +.grid-content { + border-radius: 4px; + min-height: 36px; +} +.content-text { + display: flex; + align-items: center; + justify-content: center; +} +.left-text { + display: flex; + align-items: center; + justify-content: flex-start; +} +.right-text { + display: flex; + align-items: center; + justify-content: flex-end; +} +</style> + <style> +.el-step.is-center .el-step__description { + padding-left: 5%; + padding-right: 5%; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/routerview.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/routerview.vue" new file mode 100644 index 0000000..7ad2ff8 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/routerview.vue" @@ -0,0 +1,96 @@ +<template> + <div> + <vol-box + v-model="showDetialBox" + :lazy="true" + width="1200px" + :padding="15" + title="瀹屾暣璺敱鏌ョ湅" + > + <div + style="margin-bottom: 1%" + v-for="(item, index) in routerDatas" + :key="index" + > + <el-row> + <el-col> + <div + class="grid-content right-text" + style="font-weight: bold; font-size: 18px" + > + <span>{{ item.type == "Out" ? "鍑哄簱璺敱" : "鍏ュ簱璺敱" }}</span> + </div> + </el-col> + </el-row> + <el-steps :active="item.routes.length" align-center simple> + <el-step + v-for="itemRouter in item.routes" + :key="itemRouter" + :title="itemRouter" + icon="" + ></el-step> + </el-steps> + </div> + </vol-box> + </div> +</template> + + <script> +import VolBox from "@/components/basic/VolBox.vue"; +export default { + components: { VolBox }, + data() { + return { + active: 0, + showDetialBox: false, + routerDatas: [], + }; + }, + methods: { + open() { + this.showDetialBox = true; + this.getData(); + }, + getData() { + this.http.post("/api/Router/GetAllWholeRouters", {}, true).then((x) => { + if (!x.status) return this.$message.error(x.message); + this.routerDatas = x.data; + }); + }, + }, + created() {}, +}; +</script> + + <style scoped> +.el-col { + border-radius: 4px; +} +.grid-content { + border-radius: 4px; + min-height: 36px; +} +.content-text { + display: flex; + align-items: center; + justify-content: center; +} +.left-text { + display: flex; + align-items: center; + justify-content: flex-start; +} +</style> + <style> +.el-table .warning-row { + background: #fcf1e2; +} + +.el-table .success-row { + background: #f0f9eb; +} + +.el-table .error-row { + background: #fde2e2; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js" new file mode 100644 index 0000000..d907544 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js" @@ -0,0 +1,72 @@ + +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 +import gridBody from './extend/routerview.vue'; +import gridHeader from './extend/addrouters.vue'; +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: gridHeader, + gridBody: gridBody, + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + var viewButton = this.buttons.find((x) => x.value == "ViewAllRouter"); + if (viewButton) { + viewButton.onClick = ()=>{ + this.$refs.gridBody.open(); + } + } + + var addRoutersButton = this.buttons.find((x) => x.value == "AddRouters"); + if (addRoutersButton) { + addRoutersButton.onClick = ()=>{ + this.$refs.gridHeader.open(); + } + } + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + //this.detailOptions.columns.forEach(column=>{ }); + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + } + } + }; + export default extension; + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.js" new file mode 100644 index 0000000..ca16186 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.js" @@ -0,0 +1,78 @@ + +import gridBody from './extend/importDevicePro.vue' +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: gridBody, + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + // console.log(this.detailOptions.buttons); + // console.log(this.$refs.detail) + var detailImport = this.detailOptions.buttons.find(item=>item.value == 'import'); + if(detailImport){ + detailImport.onClick = function () { + this.$refs.gridBody.open(); + } + } + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + // this.detailOptions.columns.forEach(column=>{ }); + // console.log(this) + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + }, + updated() { + //console.log(this.$refs) + this.$nextTick(() => { + if (this.$refs.upload_excel) { + alert('鐐瑰嚮浜嗕笂浼犳寜閽�') + this.$refs.upload_excel.upload = function () { + console.log('鐐瑰嚮浜嗕笂浼犳寜閽�') + } + } + // alert(1) + }) + + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js" new file mode 100644 index 0000000..a39e193 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js" @@ -0,0 +1,58 @@ + +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 + +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: '', + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + //this.detailOptions.columns.forEach(column=>{ }); + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + } + } + }; + export default extension; + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js" new file mode 100644 index 0000000..a39e193 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js" @@ -0,0 +1,58 @@ + +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 + +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: '', + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + //this.detailOptions.columns.forEach(column=>{ }); + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + } + } + }; + export default extension; + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js" new file mode 100644 index 0000000..a39e193 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js" @@ -0,0 +1,58 @@ + +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 + +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: '', + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + //this.detailOptions.columns.forEach(column=>{ }); + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + } + } + }; + export default extension; + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/extend/importDevicePro.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/extend/importDevicePro.vue" new file mode 100644 index 0000000..66b521c --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/quartzJob/extend/importDevicePro.vue" @@ -0,0 +1,116 @@ +<template> + <div> + <vol-box + v-model="showDetialBox" + :lazy="true" + :height="350" + :width="600" + :padding="15" + title="璁惧鍗忚淇℃伅-瀵煎叆" + > + <upload-excel + ref="upload_excel" + :url="url" + :template="template" + ></upload-excel> + </vol-box> + </div> +</template> + + <script> +import VolBox from "@/components/basic/VolBox.vue"; +import UploadExcel from "@/components/basic/UploadExcel.vue"; +export default { + components: { VolBox, UploadExcel }, + data() { + return { + showDetialBox: false, + url: "", + template: { + url: "", //妯℃澘涓嬭浇璺緞锛屽鏋滄病鏈夋ā鏉胯矾寰勶紝鍒欎笉鏄剧ず涓嬭浇妯℃澘鍔熻兘 + fileName: "璁惧鍗忚淇℃伅瀵煎叆妯℃澘", //涓嬭浇妯℃澘鐨勬枃浠跺悕 + }, + }; + }, + methods: { + open() { + this.template.url = `${this.http.ipAddress}api/DeviceProtocol/DownLoadTemplate`; + this.url = `${this.http.ipAddress}api/DeviceProtocol/GetImportData`; + this.showDetialBox = true; + this.$nextTick(() => { + this.$refs.upload_excel.upload = this.upload; + console.log(this.$refs.upload_excel); + }); + }, + upload() { + console.log("upload"); + let _url = this.url; + if (!_url) { + return this.$Message.error("娌℃湁閰嶇疆濂経rl"); + } + + if (!this.$refs.upload_excel.file) { + return this.$Message.error("璇烽�夋嫨鏂囦欢"); + } + var formData = new FormData(); + formData.append("fileInput", this.$refs.upload_excel.file); + if (!this.$refs.upload_excel.importExcelBefore(formData)) { + return; + } + this.$refs.upload_excel.loadingStatus = true; + this.http.post(_url, formData).then( + (x) => { + // this.$refs.uploadFile.clearFiles(); + this.$refs.upload_excel.loadingStatus = false; + this.$refs.upload_excel.file = null; + if (x.status) { + this.$emit("parentCall", ($vue) => { + $vue.$refs.detail.rowData.push(...x.data); + }); + } + + this.message = x.message; + this.resultClass = x.status ? "v-r-success" : "v-r-error"; + }, + (error) => { + this.$refs.upload_excel.loadingStatus = false; + } + ); + }, + }, + created() {}, +}; +</script> + + <style scoped> +.el-col { + border-radius: 4px; +} +.grid-content { + border-radius: 4px; + min-height: 36px; +} +.content-text { + display: flex; + align-items: center; + justify-content: center; +} +.left-text { + display: flex; + align-items: center; + justify-content: flex-start; +} +</style> + <style> +.el-table .warning-row { + background: #fcf1e2; +} + +.el-table .success-row { + background: #f0f9eb; +} + +.el-table .error-row { + background: #fde2e2; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js" new file mode 100644 index 0000000..e37ecf6 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js" @@ -0,0 +1,65 @@ +import { h, resolveComponent } from 'vue'; +let extension = { + components: { //鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + //鎵╁睍缁勪欢寮曞叆鏂瑰紡 + gridHeader: '', + gridBody: '', + gridFooter: '', + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + buttons: [], //鎵╁睍鐨勬寜閽� + methods: { //浜嬩欢鎵╁睍 + onInit() { + //鐐瑰嚮鍗曞厓鏍肩紪杈戜笌缁撴潫缂栬緫(榛樿鏄偣鍑诲崟鍏冩牸缂栬緫锛岄紶鏍囩寮�缁撴潫缂栬緫) + this.detailOptions.clickEdit = true; + this.editFormOptions.forEach(x => { + x.forEach(item => { + if (item.field == 'ParentId') { + item.min = 0; + } + if (item.field == "DbSql") { + item.placeholder = "濡傛灉浠庢暟鎹簱鍔犺浇鏁版嵁婧愶紝璇锋寜姝ゆ牸寮忛厤缃畇ql璇彞锛歴elect orderType as key,orderName as value from order 濡傛灉闇�瑕佹牴鎹敤鎴蜂俊鎭姞杞芥暟鎹簮锛岃閰嶇疆濂芥sql,鍐嶄慨鏀瑰悗鍙癉ictionaryHandler.GetCustomDBSql鏂规硶"; + } + }) + }) + this.detailOptions.columns.forEach(x => { + if (x.field == 'OrderNo') { + x.summary = true; + } + }) + //淇濆瓨鍚庝笉鍏抽棴缂栬緫妗� + this.boxOptions.saveClose = false; + }, + onInited() { + this.boxOptions.height = document.body.clientHeight * 0.87 + this.height = this.height - 45; + }, + addBefore(formData) { + return this.saveBefore(formData); + }, + updateBefore(formData) { + return this.saveBefore(formData); + }, + saveBefore(formData) { + if (this.editFormFields.DbSql && + (this.editFormFields.DbSql.indexOf('value') == -1 || + this.editFormFields.DbSql.indexOf('key') == -1) + ) { + this.$message.error("sql璇彞蹇呴』鍖呮嫭key/value瀛楁,濡�:select orderType as key,orderName as value from order"); + return false; + } + return true; + }, + searchBefore(param) { + return true; + }, + searchAfter(result) { + return true; + } + } +}; +export default extension; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js" new file mode 100644 index 0000000..da94487 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js" @@ -0,0 +1,22 @@ + + +let extension = { + components: {//鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + gridHeader:'', + gridbody:'', + gridFooter: '', + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + buttons: [],//鎵╁睍鐨勬寜閽� + methods: {//浜嬩欢鎵╁睍 + onInit() { + }, + onInited() { + } + } +}; +export default extension; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js" new file mode 100644 index 0000000..5b05c40 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js" @@ -0,0 +1,26 @@ +import { h, resolveComponent } from 'vue'; +let extension = { + components: { + //鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + gridHeader: "", //{ template: "<div>鎵╁睍缁剎x浠�</div>" }, + gridBody: '', + gridFooter: "", + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: "", + modelBody: "", + modelFooter: "" + }, + buttons: [], //鎵╁睍鐨勬寜閽� + methods: { + //浜嬩欢鎵╁睍 + onInit() { + console.log("sys_log") + this.setFiexdSearchForm(true); + }, + onInited() { + this.height = this.height - 170; + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js" new file mode 100644 index 0000000..5ba4845 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js" @@ -0,0 +1,49 @@ + +let extension = { + components: {//鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + gridHeader: '', + gridBody: '', + gridFooter: '', + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + buttons: [],//鎵╁睍鐨勬寜閽� + tableAction:"Sys_Role", + methods: {//浜嬩欢鎵╁睍 + onInited () { + this.height = this.height - 80; + this.editFormOptions.forEach(x => { + x.forEach(item => { + if (item.field == 'ParentId') { + item.title = "涓婄骇瑙掕壊"; + //璁剧疆浠绘剰鑺傜偣閮借兘閫変腑(榛樿鍙兘閫変腑鏈�鍚庝竴涓妭鐐�) + item.changeOnSelect = true; + } + }) + }) + }, + onInit() { + //璁剧疆treetable鐨勫敮涓�鍊煎瓧娈�(杩欎釜瀛楁鐨勫�煎湪琛ㄩ噷闈㈠繀椤绘槸鍞竴鐨�) + this.rowKey="Role_Id"; + }, + /***鍔犺浇鍚庡彴鏁版嵁瑙丼ys_RoleController.cs鏂囦欢***/ + loadTreeChildren(tree, treeNode, resolve) { //鍔犺浇瀛愯妭鐐� + let url=`api/role/getTreeTableChildrenData?roleId=${tree.Role_Id}`; + this.http.post(url,{}).then(result=>{ + resolve(result.rows) + }) + }, + /***鍔犺浇鍚庡彴鏁版嵁瑙丼ys_RoleController.cs鏂囦欢***/ + searchBefore(params){//鍒ゆ柇鍔犺浇鏍硅妭鐐规垨瀛愯妭鐐� + //娌℃湁鏌ヨ鏉′欢锛岄粯璁ゆ煡璇㈣繑鍥炴墍鏈夋牴鑺傜偣鏁版嵁 + if (!params.wheres.length) { + params.value=1; + } + return true; + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js" new file mode 100644 index 0000000..d6715be --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js" @@ -0,0 +1,61 @@ + +import { h, resolveComponent } from 'vue'; +let extension = { + components: {//鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + gridHeader: '', + gridBody: { + render () { + return [ + h(resolveComponent('el-alert'), { + style: { 'margin-bottom': '12px' }, + 'show-icon': true, type: 'error', + closable: false, title: '鍏充簬TreeTable浣跨敤' + }, ' treetable鍚屾牱鍏ㄩ儴浠g爜鑷姩鐢熸垚锛岄〉闈㈢敓鎴愬悗璁剧疆this.rowKe="xxx" tree涓婚敭瀛楁,鍗冲彲瀹屾垚鏍戝舰table閰嶇疆,鍏蜂綋璇存槑瑙丼ys_Role1.js'), + ] + } + }, + + gridFooter: '', + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + buttons: [],//鎵╁睍鐨勬寜閽� + tableAction:"Sys_Role", + methods: {//浜嬩欢鎵╁睍 + onInited () { + this.height = this.height - 80; + this.editFormOptions.forEach(x => { + x.forEach(item => { + if (item.field == 'ParentId') { + item.title = "涓婄骇瑙掕壊"; + //璁剧疆浠绘剰鑺傜偣閮借兘閫変腑(榛樿鍙兘閫変腑鏈�鍚庝竴涓妭鐐�) + item.changeOnSelect = true; + } + }) + }) + }, + onInit() { + //璁剧疆treetable鐨勫敮涓�鍊煎瓧娈�(杩欎釜瀛楁鐨勫�煎湪琛ㄩ噷闈㈠繀椤绘槸鍞竴鐨�) + this.rowKey="Role_Id"; + }, + /***鍔犺浇鍚庡彴鏁版嵁瑙丼ys_RoleController.cs鏂囦欢***/ + loadTreeChildren(tree, treeNode, resolve) { //鍔犺浇瀛愯妭鐐� + let url=`api/role/getTreeTableChildrenData?roleId=${tree.Role_Id}`; + this.http.post(url,{}).then(result=>{ + resolve(result.rows) + }) + }, + /***鍔犺浇鍚庡彴鏁版嵁瑙丼ys_RoleController.cs鏂囦欢***/ + searchBefore(params){//鍒ゆ柇鍔犺浇鏍硅妭鐐规垨瀛愯妭鐐� + //娌℃湁鏌ヨ鏉′欢锛岄粯璁ゆ煡璇㈣繑鍥炴墍鏈夋牴鑺傜偣鏁版嵁 + if (!params.wheres.length) { + params.value=1; + } + return true; + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js" new file mode 100644 index 0000000..4be7155 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js" @@ -0,0 +1,86 @@ +import { defineAsyncComponent } from "vue"; +let extension = { + components: { //鍔ㄦ�佹墿鍏呯粍浠舵垨缁勪欢璺緞 + //琛ㄥ崟header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + gridHeader: defineAsyncComponent(() => + import("./Sys_User/Sys_UserGridHeader.vue")), + gridBody: '', + gridFooter: '', + //寮瑰嚭妗�(淇敼銆佺紪杈戙�佹煡鐪�)header銆乧ontent銆乫ooter瀵瑰簲浣嶇疆鎵╁厖鐨勭粍浠� + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + text: "鍙兘鐪嬪埌褰撳墠瑙掕壊涓嬬殑鎵�鏈夊笎鍙�", + buttons: [], //鎵╁睍鐨勬寜閽� + methods: { //浜嬩欢鎵╁睍 + onInit() { + this.boxOptions.height = 530; + this.columns.push({ + title: '鎿嶄綔', + hidden: false, + align: "center", + fixed: 'right', + width: 120, + render: (h, { row, column, index }) => { + return h( + "div", { style: { 'font-size': '13px', 'cursor': 'pointer', 'color': '#409eff' } }, [ + h( + "a", { + style: { 'margin-right': '15px' }, + onClick: (e) => { + e.stopPropagation() + this.$refs.gridHeader.open(row); + } + }, "淇敼瀵嗙爜" + ), + h( + "a", { + style: {}, + onClick: (e) => { + e.stopPropagation() + this.edit(row); + } + }, + "缂栬緫" + ), + ]) + } + }) + }, + onInited() { }, + addAfter(result) { //鐢ㄦ埛鏂板缓鍚庯紝鏄剧ず闅忔満鐢熸垚鐨勫瘑鐮� + if (!result.status) { + return true; + } + //鏄剧ず鏂板缓鐢ㄦ埛鐨勫瘑鐮� + //2020.08.28浼樺寲鏂板缓鎴愬悗鎻愮ず鏂瑰紡 + this.$confirm(result.message, '鏂板缓鐢ㄦ埛鎴愬姛', { + confirmButtonText: '纭畾', + type: 'success', + center: true + }).then(() => { }) + + this.boxModel = false; + this.refresh(); + return false; + }, + modelOpenAfter() { + //鐐瑰嚮寮瑰嚭妗嗗悗锛屽鏋滄槸缂栬緫鐘舵�侊紝绂佹缂栬緫鐢ㄦ埛鍚嶏紝濡傛灉鏂板缓鐘舵�侊紝灏嗙敤鎴峰悕瀛楁璁剧疆涓哄彲缂栬緫 + let isEDIT = this.currentAction == this.const.EDIT; + this.editFormOptions.forEach(item => { + item.forEach(x => { + if (x.field == "UserName") { + x.disabled=isEDIT; + } + }) + //涓嶆槸鏂板缓锛屾�у埆榛樿鍊艰缃负鐢� + if (!isEDIT) { + this.editFormFields.Gender = "0"; + } + }) + } + + } +}; +export default extension; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User/Sys_UserGridHeader.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User/Sys_UserGridHeader.vue" new file mode 100644 index 0000000..f6f59be --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User/Sys_UserGridHeader.vue" @@ -0,0 +1,85 @@ +<template> + <div> + <vol-box + v-model="model" + :padding="30" + title="淇敼瀵嗙爜" + :width="500" + :height="250" + > + <el-alert type="success"> + <h3> + <span>甯愬彿锛歿{ row.userName }}</span> + <span>鐢ㄦ埛锛歿{ row.userTrueName }}</span> + </h3> + </el-alert> + <div> + <el-input + placeholder="璇疯緭鍏ュ瘑鐮�" + v-model="password" + size="large" + style="width: 100%; margin-top: 15px" + /> + </div> + <template #footer> + <el-button + type="primary" + @click="savePwd()" + >淇敼瀵嗙爜</el-button + > + <el-button + @click="model = false" + >鍏抽棴</el-button + > + </template> + </vol-box> + </div> +</template> +<script> +import { defineComponent, defineAsyncComponent } from "vue"; +export default defineComponent({ + components: { + VolBox: defineAsyncComponent(() => import("@/components/basic/VolBox.vue")) + }, + data() { + return { + row: {}, + password: "", + model: false, + }; + }, + methods: { + open(row) { + this.password = ""; + this.row = row; + this.model = true; + }, + savePwd() { + if (!this.password) return this.$Message.error("璇疯緭瀵嗙爜"); + if (this.password.length < 6) + return this.$Message.error("瀵嗙爜闀垮害鑷冲皯6浣�"); + let url = + "/api/user/modifyUserPwd?password=" + + this.password + + "&userName=" + + this.row.UserName; + this.http.post(url, {}, true).then((x) => { + if (!x.status) { + return this.$message.error(x.message); + } + this.model = false; + this.$Message.success(x.message); + }); + }, + }, + created() {}, +}) +</script> +<style lang="less" scoped> +h3 { + font-weight: 500; + > span:last-child { + margin-left: 30px; + } +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.js" new file mode 100644 index 0000000..5c19061 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.js" @@ -0,0 +1,152 @@ +/***************************************************************************************** +** Author:jxx 2022 +** QQ:283591387 +**瀹屾暣鏂囨。瑙侊細http://v2.volcore.xyz/document/api 銆愪唬鐮佺敓鎴愰〉闈iewGrid銆� +**甯哥敤绀轰緥瑙侊細http://v2.volcore.xyz/document/vueDev +**鍚庡彴鎿嶄綔瑙侊細http://v2.volcore.xyz/document/netCoreDev +*****************************************************************************************/ +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 + +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: '', + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { //妗嗘灦鍒濆鍖栭厤缃墠锛� + this.rowKey = "DepartmentId"; + }, + loadTreeChildren(tree, treeNode, resolve) { //鍔犺浇瀛愯妭鐐� + let url = `api/Sys_Department/getTreeTableChildrenData?departmentId=${tree.DepartmentId}`; + this.http.post(url, {}).then(result => { + resolve(result.rows) + }) + }, + /***鍔犺浇鍚庡彴鏁版嵁瑙丼ys_RoleController.cs鏂囦欢***/ + searchBefore(params) {//鍒ゆ柇鍔犺浇鏍硅妭鐐规垨瀛愯妭鐐� + //娌℃湁鏌ヨ鏉′欢锛岄粯璁ゆ煡璇㈣繑鍥炴墍鏈夋牴鑺傜偣鏁版嵁 + if (!params.wheres.length) { + params.value = 1; + } + return true; + }, + onInited() { + let hasUpdate, hasDel, hasAdd; + this.buttons.forEach((x) => { + if (x.value == 'Update') { + x.hidden = true; + hasUpdate = true; + } else if (x.value == 'Delete') { + hasDel = true; + x.hidden = true;//闅愯棌鎸夐挳 + } + else if (x.value == 'Add') { + x.type="primary"; + hasAdd = true; + } + }); + if (!(hasUpdate || hasDel || hasAdd)) { + return; + } + this.columns.push({ + title: '鎿嶄綔', + field: '鎿嶄綔', + width: 80, + fixed: 'right', + align: 'center', + render: (h, { row, column, index }) => { + return ( + <div> + <el-button + onClick={($e) => { + this.addBtnClick(row) + }} + type="primary" + link + v-show={hasAdd} + icon="Plus" + > + </el-button> + <el-button + onClick={($e) => { + this.edit(row); + }} + type="success" + link + v-show={hasUpdate} + icon="Edit" + > + </el-button> + <el-tooltip + class="box-item" + effect="dark" + content="鍒犻櫎" + placement="top" + > + <el-button + link + onClick={($e) => { + this.del(row); + }} + v-show={hasDel} + type="danger" + icon="Delete" + > + </el-button> + </el-tooltip> + </div> + ); + } + }); + }, + addBtnClick(row) { + //杩欓噷鏄姩鎬乤ddCurrnetRow灞炴�ц褰曞綋鍓嶇偣鍑荤殑琛屾暟鎹�,涓嬮潰modelOpenAfter璁剧疆榛樿鍊� + this.addCurrnetRow = row; + this.add(); + }, + addAfter() {//娣诲姞鍚庡埛鏂板瓧鍏� + this.initDicKeys(); + return true; + }, + updateAfter() { + this.initDicKeys(); + return true; + }, + delAfter(result) {//鏌ヨ鐣岄潰鐨勮〃鍒犻櫎鍚� + this.initDicKeys(); + return true; + }, + modelOpenAfter(row) { + //鐐瑰嚮琛屼笂鐨勬坊鍔犳寜閽簨浠� + if (this.addCurrnetRow) { + + //鑾峰彇褰撳墠缁勭粐鏋勬灦鐨勬墍鏈夌埗绾d,鐢ㄤ簬璁剧疆鏂板缓鏃剁埗绾d鐨勯粯璁ゅ�� + + //鑾峰彇鏁版嵁鏁版嵁婧� + let data = []; + this.editFormOptions.forEach(options => { + options.forEach(option => { + if (option.field == 'ParentId') { + data = option.orginData; + } + }) + }) + let parentIds = this.base.getTreeAllParent(this.addCurrnetRow.DepartmentId, data).map(x => { return x.id }); + //璁剧疆缂栬緫琛ㄥ崟涓婄骇缁勭粐鐨勯粯璁ゅ�� + this.editFormFields.ParentId = parentIds; + this.addCurrnetRow = null; + + } + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/extend/taskExecuteDetail.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/extend/taskExecuteDetail.vue" new file mode 100644 index 0000000..ced7068 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/extend/taskExecuteDetail.vue" @@ -0,0 +1,259 @@ +<template> + <div> + <vol-box + v-model="showDetialBox" + :lazy="true" + width="1200px" + :padding="15" + title="浠诲姟璇︽儏" + > + <el-row height="50"> + <el-col :span="24"> + <div class="grid-content right-text"> + <el-link type="primary" @click="switchView">鍒囨崲瑙嗗浘</el-link> + </div> + </el-col> + </el-row> + <div :style="{ height: height }"> + <div v-if="viewType == 1"> + <el-row height="50"> + <el-col :span="8"> + <div + class="grid-content content-text" + style="font-weight: bold; font-size: 18px" + > + <span>浠诲姟缂栧彿锛歿{ row.taskNum }}</span> + </div> + </el-col> + <el-col :span="8"> + <div + class="grid-content content-text" + style="font-weight: bold; font-size: 18px" + > + <span>鎵樼洏缂栧彿锛歿{ row.palletCode }}</span> + </div> + </el-col> + <el-col :span="8"> + <div + class="grid-content content-text" + style="font-weight: bold; font-size: 18px" + > + <span>浠诲姟鐘舵�侊細{{ row.taskState }}</span> + </div> + </el-col> + </el-row> + <div style="height: 100px; margin-top: 3%"> + <el-steps :active="active" align-center finish-status="success"> + <el-step + v-for="item in steps" + :key="item.title" + :title="item.title" + :description="item.description" + ></el-step> + </el-steps> + </div> + <el-row height="50" v-show="previousShow || nextShow"> + <el-col :span="8"> + <div v-show="previousShow" class="grid-content content-text"> + <el-button type="danger" @click="previous" + >鍥炴粴鍒颁笂涓�姝�</el-button + > + </div> + </el-col> + <el-col :span="8"> + <div v-show="recoveryShow" class="grid-content content-text"> + <el-button type="primary" @click="recovery">浠诲姟鎸傝捣鎭㈠</el-button> + </div> + </el-col> + <el-col :span="8"> + <div v-show="nextShow" class="grid-content content-text"> + <el-button type="warning" @click="next">璺宠浆鍒颁笅涓�姝�</el-button> + </div> + </el-col> + </el-row> + </div> + <div v-else> + <el-table + :data="tableData" + style="width: 100%" + :row-class-name="tableRowClassName" + > + <el-table-column type="index" width="50"> </el-table-column> + <el-table-column prop="taskNum" label="浠诲姟鍙�" width="90"> + </el-table-column> + <el-table-column prop="taskState" label="浠诲姟鐘舵��" width="90"> + </el-table-column> + <el-table-column prop="currentAddress" label="褰撳墠浣嶇疆" width="90"> + </el-table-column> + <el-table-column prop="nextAddress" label="涓嬩竴浣嶇疆" width="90"> + </el-table-column> + <el-table-column prop="isManual" label="鏄惁浜哄伐鎿嶄綔" width="120"> + </el-table-column> + <el-table-column prop="isNormal" label="鏄惁姝e父" width="90"> + </el-table-column> + <el-table-column prop="description" label="鎻忚堪"> </el-table-column> + <el-table-column prop="createDate" label="鍒涘缓鏃堕棿" width="180"> + </el-table-column> + <el-table-column prop="remark" label="澶囨敞" width="180"> + </el-table-column> + </el-table> + </div> + </div> + </vol-box> + </div> +</template> + +<script> +import VolBox from "@/components/basic/VolBox.vue"; +export default { + components: { VolBox }, + data() { + return { + active: 0, + showDetialBox: false, + row: {}, + steps: [], + viewType: 1, + height: "200px", + tableData: [], + previousShow: false, + nextShow: false, + recoveryShow: true, + }; + }, + methods: { + open(row) { + this.row = row; + this.showDetialBox = true; + if (this.viewType == 1) { + this.getSteps(); + } else { + this.getDetailDatas(); + } + + this.$emit("parentCall", ($vue) => { + var previousButton = $vue.buttons.find((x) => x.value == "Previous"); + this.previousShow = previousButton != null; + + var nextButton = $vue.buttons.find((x) => x.value == "Next"); + this.nextShow = nextButton != null; + }); + }, + getSteps() { + this.http + .post( + "/api/TaskExecuteDetail/GetDetailInfo?taskNum=" + this.row.taskNum, + {}, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.steps = x.data.list; + this.active = x.data.active; + }); + }, + getDetailDatas() { + this.http + .post( + "/api/TaskExecuteDetail/GetDetailDatas?taskNum=" + this.row.taskNum, + {}, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.tableData = x.data; + }); + }, + previous() { + this.http + .post( + "/api/Task/RollbackTaskStatusToLast?taskNum=" + this.row.taskNum, + {}, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.$message.success("鎿嶄綔鎴愬姛"); + this.getSteps(); + }); + }, + recovery(){ + this.http + .post( + "/api/Task/TaskStatusRecovery?taskNum=" + this.row.taskNum, + {}, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.$message.success("鎿嶄綔鎴愬姛"); + this.getSteps(); + }); + }, + next() { + this.http + .post( + "/api/Task/UpdateTaskStatusToNext?taskNum=" + this.row.taskNum, + {}, + true + ) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.$message.success("鎿嶄綔鎴愬姛"); + this.getSteps(); + }); + }, + switchView() { + this.viewType = this.viewType == 1 ? 2 : 1; + if (this.viewType == 2) { + this.height = "600px"; + this.getDetailDatas(); + } else { + this.height = "200px"; + } + }, + tableRowClassName({ row, rowIndex }) { + if (!row.isNormal) { + return "error-row"; + } else if (row.isManual) { + return "warning-row"; + } + return "success-row"; + }, + }, + created() {}, +}; +</script> + +<style scoped> +.el-col { + border-radius: 4px; +} +.grid-content { + border-radius: 4px; + min-height: 36px; +} +.content-text { + display: flex; + align-items: center; + justify-content: center; +} +.right-text { + display: flex; + align-items: center; + justify-content: flex-end; +} +</style> +<style> +.el-table .warning-row { + background: #fcf1e2; +} + +.el-table .success-row { + background: #f0f9eb; +} + +.el-table .error-row { + background: #fde2e2; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js" new file mode 100644 index 0000000..8a02afb --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js" @@ -0,0 +1,82 @@ + +//姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜 +import gridBody from './extend/taskExecuteDetail.vue' +let extension = { + components: { + //鏌ヨ鐣岄潰鎵╁睍缁勪欢 + gridHeader: '', + gridBody: gridBody, + gridFooter: '', + //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢 + modelHeader: '', + modelBody: '', + modelFooter: '' + }, + tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓) + buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽� + methods: { + //涓嬮潰杩欎簺鏂规硶鍙互淇濈暀涔熷彲浠ュ垹闄� + onInit() { + // this.$Notice.success({ title: this.detailOptions.cnName + ',鏌ヨ缁撴灉', desc: '杩斿洖鐨勫璞★細' + JSON.stringify(data) }); + + var previousButton = this.buttons.find((x) => x.value == "Previous"); + if (previousButton) previousButton.hidden = true; + var nextButton = this.buttons.find((x) => x.value == "Next"); + if (nextButton) nextButton.hidden = true; + var recoveryButton = this.buttons.find((x) => x.value == "TaskRecovery"); + if (recoveryButton) recoveryButton.hidden = true; + + //鎵╁睍椤甸潰鍒濆鍖栨搷浣� + this.columns.push({ + field: '鎿嶄綔', + title: '鎿嶄綔', + width: 70, + fixed: 'right', + align: 'center', + formatter: (row) => { + return ( + '<i style="cursor: pointer;color: #2d8cf0;"class="el-icon-view">鏌ョ湅</i>' + ); + }, + click: (row) => { + this.$refs.gridBody.open(row); + } + }); + }, + onInited() { + //妗嗘灦鍒濆鍖栭厤缃悗 + //濡傛灉瑕侀厤缃槑缁嗚〃,鍦ㄦ鏂规硶鎿嶄綔 + //this.detailOptions.columns.forEach(column=>{ }); + }, + searchBefore(param) { + //鐣岄潰鏌ヨ鍓�,鍙互缁檖aram.wheres娣诲姞鏌ヨ鍙傛暟 + //杩斿洖false锛屽垯涓嶄細鎵ц鏌ヨ + return true; + }, + searchAfter(result) { + //鏌ヨ鍚庯紝result杩斿洖鐨勬煡璇㈡暟鎹�,鍙互鍦ㄦ樉绀哄埌琛ㄦ牸鍓嶅鐞嗚〃鏍肩殑鍊� + return true; + }, + addBefore(formData) { + //鏂板缓淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛紝鍙互缁欑粰琛ㄥ崟璁剧疆鍊硷紝鑷繁杈撳嚭鐪媐ormData鐨勫�� + return true; + }, + updateBefore(formData) { + //缂栬緫淇濆瓨鍓峟ormData涓哄璞★紝鍖呮嫭鏄庣粏琛ㄣ�佸垹闄よ鐨処d + return true; + }, + rowClick({ row, column, event }) { + //鏌ヨ鐣岄潰鐐瑰嚮琛屼簨浠� + // this.$refs.table.$refs.table.toggleRowSelection(row); //鍗曞嚮琛屾椂閫変腑褰撳墠琛�; + }, + modelOpenAfter(row) { + //鐐瑰嚮缂栬緫銆佹柊寤烘寜閽脊鍑烘鍚庯紝鍙互鍦ㄦ澶勫啓閫昏緫锛屽锛屼粠鍚庡彴鑾峰彇鏁版嵁 + //(1)鍒ゆ柇鏄紪杈戣繕鏄柊寤烘搷浣滐細 this.currentAction=='Add'; + //(2)缁欏脊鍑烘璁剧疆榛樿鍊� + //(3)this.editFormFields.瀛楁='xxx'; + //濡傛灉闇�瑕佺粰涓嬫媺妗嗚缃粯璁ゅ�硷紝璇烽亶鍘唗his.editFormOptions鎵惧埌瀛楁閰嶇疆瀵瑰簲data灞炴�х殑key鍊� + //鐪嬩笉鎳傚氨鎶婅緭鍑虹湅锛歝onsole.log(this.editFormOptions) + } + } +}; +export default extension; diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/main.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/main.js" new file mode 100644 index 0000000..40c4c4f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/main.js" @@ -0,0 +1,63 @@ +import { createApp } from 'vue' +import App from './App.vue' +import router from './router' +import store from './store' +import ElementPlus from 'element-plus'; +// import 'element-plus/lib/theme-chalk/index.css'; +import 'element-plus/dist/index.css' +import './assets/element-icon/icon.css' +import base from './uitils/common' +import http from './api/http' +// import 'dayjs/locale/zh-cn' +// import locale from 'element-plus/lib/locale/lang/zh-cn' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' + + + +import permission from './api/permission' +import viewgird from './components/basic/ViewGrid'; +const app = createApp(App); +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} +app.config.globalProperties.base = base; +app.config.globalProperties.http = http; +app.config.globalProperties.$tabs = {}; +app.config.globalProperties.permission = permission; +app.config.globalProperties.$global = { + signalR: false, //鏄惁寮�鍚痵ignalR + table: { + //vol-table甯︽暟鎹簮鐨勫崟鍏冩牸鏄惁鍚敤tag鏍囩(涓嬫媺妗嗙瓑鍗曞厓鏍间互tag鏍囩鏄剧ず) + useTag: true + }, + audit: { //瀹℃牳閫夐」 + data: [ + { text: '閫氳繃', value: 1 }, + { text: '鎷掔粷', value: 3 }, + { text: '椹冲洖', value: 4 } + ], + status:[0,2] //瀹℃牳涓殑鏁版嵁 + // 寰呭鏍� = 0, + // 瀹℃牳閫氳繃 = 1, + // 瀹℃牳涓� = 2, + // 瀹℃牳鏈�氳繃 = 3, + // 椹冲洖 = 4 + } +} +//2023.03.13锛� +//淇敼瑙侊細volupload.vue锛屽悗鍙癆liOSSController.cs锛岄樋閲屼簯OSS閰嶇疆.doc +window.oss = { + ali: { //闃块噷浜� + use: false,//浣跨敤闃块噷浜戜笂浼犳枃浠� + //闃块噷缂╃暐鍥惧帇缂╁ぇ灏� + //.aliyuncs.com + small: "?x-oss-process=image/resize,m_lfit,w_200" + } +} +app.use(store) + .use(ElementPlus, { size: 'default' }) + .use(router) + .use(viewgird) + .mount('#app'); +app.config.globalProperties.$Message = app.config.globalProperties.$message; + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/charts.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/charts.js" new file mode 100644 index 0000000..55cac54 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/charts.js" @@ -0,0 +1,17 @@ +let charts=[ + { + path: '/chart', + name: 'chart', + component: () => import('@/views/charts/chart.vue') + }, + { + path: '/formChart', + name: 'formChart', + component: () => import('@/views/charts/formChart.vue') + }, + { + path: '/flex', + name: 'flex', + component: () => import('@/views/charts/flex.vue') + }] +export default charts diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/index.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/index.js" new file mode 100644 index 0000000..5445816 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/index.js" @@ -0,0 +1,86 @@ +import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' +import viewgird from './viewGird' +import store from '../store/index' +import redirect from './redirect' +import charts from './charts' +const routes = [ + { + path: '/', + name: 'Index', + component: () => import('@/views/Index'), + redirect: '/home', + children: [ + ...viewgird, + ...redirect, + ...charts, + { + path: '/home', + name: 'home', + component: () => import('@/views/Home.vue') + }, { + path: '/UserInfo', + name: 'UserInfo', + component: () => import('@/views/system/UserInfo.vue') + }, + { + path: '/sysMenu', + name: 'sysMenu', + component: () => import('@/views/system/Sys_Menu.vue') + }, { + path: '/coder', + name: 'coder', + component: () => import('@/views/builder/coder.vue') + } + ] + }, + { + path: '/login', + name: 'login', + component: () => import('@/views/Login.vue'), + meta:{ + anonymous:true + } + }, + { + path: '/bigdata', + name: 'bigdata', + component: () => import('@/views/charts/bigdata.vue'), + meta: { + keepAlive: false + } + } +] + +const router = createRouter({ + history: createWebHashHistory(), //createWebHistory(process.env.BASE_URL), + routes +}) + + +router.beforeEach((to, from, next) => { + if (to.matched.length == 0) return next({ path: '/404' }); + //2020.06.03澧炲姞璺敱鍒囨崲鏃跺姞杞芥彁绀� + store.dispatch("onLoading", true); + if ((to.hasOwnProperty('meta') && to.meta.anonymous) || store.getters.isLogin() || to.path == '/login') { + return next(); + } + + next({ path: '/login', query: { redirect: Math.random() } }); +}) +router.afterEach((to, from) => { + store.dispatch("onLoading", false); +}) +router.onError((error) => { + // const targetPath = router.currentRoute.value.matched; + try { + console.log(error.message); + if (process.env.NODE_ENV == 'development') { + alert(error.message) + } + localStorage.setItem("route_error", error.message) + } catch (e) { + + } + window.location.href = '/' +}); +export default router diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/redirect.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/redirect.js" new file mode 100644 index 0000000..144beb8 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/redirect.js" @@ -0,0 +1,22 @@ + +let redirect = [{ + path: '/404', + name: '404', + component: () => import('@/components/redirect/404'), + meta:{ + anonymous:true + } +}, { + path: '/401', + name: '401', + component: () => import('@/components/redirect/401') +}, { + path: '/coding', + name: 'coding', + component: () => import('@/components/redirect/coding') +}, { + path: '/message', + name: 'message', + component: () => import('@/components/redirect/Message.vue') +}] +export default redirect; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/viewGird.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/viewGird.js" new file mode 100644 index 0000000..cde4249 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/router/viewGird.js" @@ -0,0 +1,62 @@ + +let viewgird = [ + { + path: '/Sys_Log', + name: 'sys_Log', + component: () => import('@/views/system/Sys_Log.vue') + }, + { + path: '/Sys_User', + name: 'Sys_User', + component: () => import('@/views/system/Sys_User.vue') + }, + { + path: '/permission', + name: 'permission', + component: () => import('@/views/system/Permission.vue') + }, + + { + path: '/Sys_Dictionary', + name: 'Sys_Dictionary', + component: () => import('@/views/system/Sys_Dictionary.vue') + }, + { + path: '/Sys_Role', + name: 'Sys_Role', + component: () => import('@/views/system/Sys_Role.vue') + }, { + path: '/Sys_Role1', + name: 'Sys_Role1', + component: () => import('@/views/system/Sys_Role1.vue') + }, { + path: '/Sys_DictionaryList', + name: 'Sys_DictionaryList', + component: () => import('@/views/system/Sys_DictionaryList.vue') + }, { + path: '/deviceInfo', + name: 'deviceInfo', + component: () => import('@/views/quartzJob/deviceInfo.vue') + }, { + path: '/deviceProtocol', + name: 'deviceProtocol', + component: () => import('@/views/quartzJob/deviceProtocol.vue') + }, { + path: '/deviceProtocolDetail', + name: 'deviceProtocolDetail', + component: () => import('@/views/quartzJob/deviceProtocolDetail.vue') + }, { + path: '/dispatchInfo', + name: 'dispatchInfo', + component: () => import('@/views/quartzJob/dispatchInfo.vue') + }, { + path: '/task', + name: 'task', + component: () => import('@/views/taskinfo/task.vue') + }, { + path: '/router', + name: 'router', + component: () => import('@/views/basicinfo/router.vue') + }] + +export default viewgird diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/store/index.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/store/index.js" new file mode 100644 index 0000000..e72d129 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/store/index.js" @@ -0,0 +1,90 @@ +import { createStore } from 'vuex' +const keys = { USER: 'user' } +function getUserInfo(state) { + if (state.userInfo) return state.userInfo; + let userInfo = localStorage.getItem(keys.USER); + if (userInfo) { + state.userInfo = JSON.parse(userInfo); + } + return state.userInfo; +} +export default createStore({ + state: { + data: {}, + permission: [], + isLoading: false,//2020.06.03澧炲姞璺敱鍒囨崲鏃跺姞杞芥彁绀� + userInfo: null, + // wcsState: true//wcs鏈嶅姟鐘舵�� + }, + mutations: { + setPermission(state, data) { //璋冪敤鏂瑰紡 this.$store.commit('setPermission', data) + if (!data || typeof data != 'object') return; + if (data instanceof Array) { + state.permission.push(...data); + } else { + state.permission = data; + } + }, setUserInfo(state, data) { + state.userInfo = data; + localStorage.setItem(keys.USER, JSON.stringify(data)); + }, + clearUserInfo(state) { + state.permission = []; + state.userInfo = null; + localStorage.removeItem(keys.USER); + }, + test(state) { + return 113344; + }, + updateLoadingState(state, flag) { + state.isLoading = flag + } + }, getters: { + getPermission: (state) => (path) => { //璋冪敤鏂瑰紡 store.getters.getPermission('sys_User') + if (!path) return state.permission; + return state.permission.find(x => x.path == path); + }, + getUserInfo: (state) => () => { + getUserInfo(state); + return state.userInfo; + }, getUserName: (state) => () => { + getUserInfo(state); + if (state.userInfo) { + return state.userInfo.userName; + } + return '鏈幏鍙栧埌鐧婚檰淇℃伅'; + }, + getToken: (state) => () => { + getUserInfo(state); + if (state.userInfo) { + return 'Bearer ' + state.userInfo.token; + } + return ''; + }, + isLogin: (state) => () => { + if (getUserInfo(state)) { + return true; + } + return false; + }, + isLoading: (state) => () => { + return state.isLoading; + }, + data: (state) => () => { + return state.data; + }, + getData: (state) => () => { + return state.data; + }, + }, actions: { + setPermission(context, data) { + context.commit('setPermission', data); //璋冪敤鏂瑰紡 store.dispatch('push') + }, + toDo(context) { + return context.Store.m; + }, + onLoading(context, flag) { + context.commit("updateLoadingState", flag); + } + } +}) diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/uitils/common.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/uitils/common.js" new file mode 100644 index 0000000..a745d8d --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/uitils/common.js" @@ -0,0 +1,344 @@ +let base = { + addDays(date, days) { + //缁欐寚瀹氭棩鏈熷鍔犲ぉ鏁� + if (!days) { + return date; + } + let dateArr = date.split(' '); + date = new Date(new Date(date).setDate(new Date(date).getDate() + days)); + var year = date.getFullYear(); + var month = date.getMonth() + 1; + if (month < 10) { + month = '0' + month; + } + var day = date.getDate(); + if (day < 10) { + day = '0' + day; + } + date = year + '-' + month + '-' + day; + if (dateArr.length == 1) { + return date; + } + return date + ' ' + dateArr[1]; + }, + //鑾峰彇褰撳墠鏃堕棿锛宼ime鏄惁甯︽椂鍒嗙 + getDate(time) { + let date = new Date(); + let year = date.getFullYear(); + let month = date.getMonth() + 1; + let day = date.getDate(); + + let datetime = + year + + '-' + + (month < 10 ? '0' + month : month) + + '-' + + (day < 10 ? '0' + day : day); + + if (!time) { + return datetime; + } + + let hour = date.getHours(); + let minutes = date.getMinutes(); + let second = date.getSeconds(); + + return ( + datetime + + '' + + ' ' + + (hour < 10 ? '0' + hour : hour) + + ':' + + (minutes < 10 ? '0' + minutes : minutes) + + ':' + + (second < 10 ? '0' + second : second) + ); + }, + isPhone(val) { + return /^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(val); + }, + isDecimal(val) { + return /(^[\-0-9][0-9]*(.[0-9]+)?)$/.test(val); + }, + isNumber(val) { + return /(^[\-0-9][0-9]*([0-9]+)?)$/.test(val); + }, + isMail(val) { + return /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(val); + }, + isUrl(url) { + return this.checkUrl(url); + }, + checkUrl(url) { + // url= 鍗忚://(ftp鐨勭櫥褰曚俊鎭�)[IP|鍩熷悕](:绔彛鍙�)(/鎴�?璇锋眰鍙傛暟) + var strRegex = + '^((https|http|ftp)://)?' + // (https鎴杊ttp鎴杅tp):// 鍙湁鍙棤 + "(([\\w_!~*'()\\.&=+$%-]+: )?[\\w_!~*'()\\.&=+$%-]+@)?" + // ftp鐨剈ser@ 鍙湁鍙棤 + '(([0-9]{1,3}\\.){3}[0-9]{1,3}' + // IP褰㈠紡鐨刄RL- 3浣嶆暟瀛�.3浣嶆暟瀛�.3浣嶆暟瀛�.3浣嶆暟瀛� + '|' + // 鍏佽IP鍜孌OMAIN锛堝煙鍚嶏級 + '(localhost)|' + // 鍖归厤localhost + "([\\w_!~*'()-]+\\.)*" + // 鍩熷悕- 鑷冲皯涓�涓猍鑻辨枃鎴栨暟瀛梍!~*\'()-]鍔犱笂. + '\\w+\\.' + // 涓�绾у煙鍚� -鑻辨枃鎴栨暟瀛� 鍔犱笂. + '[a-zA-Z]{1,6})' + // 椤剁骇鍩熷悕- 1-6浣嶈嫳鏂� + '(:[0-9]{1,5})?' + // 绔彛- :80 ,1-5浣嶆暟瀛� + '((/?)|' + // url鏃犲弬鏁扮粨灏� - 鏂滄潌鎴栬繖娌℃湁 + "(/[\\w_!~*'()\\.;?:@&=+$,%#-]+)+/?)$"; // 璇锋眰鍙傛暟缁撳熬- 鑻辨枃鎴栨暟瀛楀拰[]鍐呯殑鍚勭瀛楃 + var re = new RegExp(strRegex, 'i'); // i涓嶅尯鍒嗗ぇ灏忓啓 + // 灏唘rl鍋歶ri杞爜鍚庡啀鍖归厤锛岃В闄よ姹傚弬鏁颁腑鐨勪腑鏂囧拰绌哄瓧绗﹀奖鍝� + if (re.test(encodeURI(url))) { + return true; + } + return false; + }, + matchUrlIp(url, ip) { + // url浣跨敤鏄惁浣跨敤鐨勫綋鍓峣p + if (!url || !ip) { + return false; + } + return url.indexOf(ip.replace('https://', '').replace('http://', '')) >= 0; + }, + getImgSrc(src, httpUrl) { + if (this.isUrl(src)) { + return src; + } + if (httpUrl) { + return httpUrl + src; + } + return src; + }, + previewImg(src, httpUrl) { + // 鍥剧墖棰勮锛岀洰鍓嶅彧鏀寔鍗曞浘鐗囬瑙� + if (src && !this.isUrl(src) && httpUrl) { + if ( + src.substr(0, 1) == '/' && + httpUrl.substr(httpUrl.length - 1, 1) == '/' + ) { + src = src.substr(1); + } + src = httpUrl + src; + } + let id = 'vol-preview'; + let $div = document.getElementById(id); + if (!$div) { + $div = document.createElement('div'); + $div.setAttribute('id', 'vol-preview'); + let $mask = document.createElement('div'); + $mask.style.position = 'absolute'; + $mask.style.width = '100%'; + $mask.style.height = '100%'; + $mask.style.background = 'black'; + $mask.style.opacity = '0.6'; + $div.appendChild($mask); + $div.style.position = 'fixed'; + $div.style.width = '100%'; + $div.style.height = '100%'; + // $div.style.overflow = "scroll"; + $div.style.top = 0; + $div.style['z-index'] = 9999999; + let $img = document.createElement('img'); + $img.setAttribute('class', 'vol-preview-img'); + $img.style.position = 'absolute'; + $img.style.top = '50%'; + $img.style.left = '50%'; + $img.style['max-width'] = '90%'; + $img.style['max-height'] = '90%'; + $img.style.transform = 'translate(-50%,-50%)'; + // $img.src = src; + $img.setAttribute('src', src); + $div.appendChild($img); + $div.addEventListener('click', function() { + this.style.display = 'none'; + }); + document.body.appendChild($div); + return; + } + let $img1 = document.body + .appendChild($div) + .querySelector('.vol-preview-img'); + // img.src = src; + $img1.setAttribute('src', src); + $div.style.display = 'block'; + }, + // 涓嬭浇鏂囦欢 $element 鏍囩, url瀹屾暣url, fileName 鏂囦欢鍚�, header 浠ey/value浼犲�� + // backGroundUrl 鍚庡彴url锛屽鏋滃悗鍙皍rl鐩存帴浠庡悗鍙颁笅杞斤紝鍏朵粬鍏ㄩ儴閫氳繃鐐瑰嚮a鏍囩涓嬭浇 + dowloadFile(url, fileName, header, backGroundUrl) { + if (!url) return alert('姝ゆ枃浠舵病鏈塽rl涓嶈兘涓嬭浇'); + if (!this.isUrl(url)) { + url = backGroundUrl + url; + } + window.open(url); + }, + downloadImg(data) { + if (!data.url || !data.callback || typeof data.callback !== 'function') { + return; + } + // url, backGroundUrl, header, callback + if ( + this.isUrl(data.url) && + !this.matchUrlIp(data.url, data.backGroundUrl) + ) { + return data.url; + } + // 閫氳繃鍚庡彴api鏈嶅姟鍣ㄤ笅杞� + if (!this.isUrl(data.url)) { + if (!this.isUrl(data.backGroundUrl + data.url)) { + return; + } + data.url = data.backGroundUrl + data.url; + } + var xmlResquest = new XMLHttpRequest(); + xmlResquest.open('get', data.url, true); + xmlResquest.responseType = 'blob'; + xmlResquest.setRequestHeader('Content-Type', 'application/json'); + if (data.header && typeof data.header === 'object') { + for (const key in data.header) { + xmlResquest.setRequestHeader(key, data.header[key]); + } + } + xmlResquest.onload = function() { + if (this.status == 200) { + var blob = this.response; + callback(window.URL.createObjectURL(blob)); + } + }; + xmlResquest.send(); + }, + // 2020.06.01澧炲姞閫氱敤鏂规硶锛屽皢鏅�氬璞¤浆鎹负tree缁撴瀯 + // data鏁版嵁鏍煎紡[ + // { name: 'tree1', id: 1, parentId: 0 }, + // { name: 'tree2', id: 2, parentId: 0 }] + + // 1銆乮d涓巔arentId杩欎袱涓瓧娈靛繀椤绘湁 + // 2銆佹爲褰ree闇�瑕佹敞鎰廔d涓巔arentId寰幆渚濊禆鐨勯棶棰� + // 3銆乧allback姣忔鐢熸垚涓�鏂扮殑鑺傜偣鐨勬椂鍥炶皟鐨勬柟娉� + + convertTree(data, callback) { + var treeIds = []; + var root_data = []; + if (data.length>100) { + data = JSON.parse(JSON.stringify(data)); + } + data.forEach((x) => { + // if (!x.children) { + // x.children = [] + // } + if ( + !x.hidden && + x.id !== undefined && + x.id !== x.parentId && + !data.some((s) => { + return x.parentId == s.id; + }) + ) { + x.isRoot = true; + callback && callback(x, data, true, treeIds); + root_data.push(x); + getTree(x.id, x, data, callback, treeIds); + } else { + callback && callback(x, data, true, treeIds); + } + }); + var exceptionNodes = data.filter((f) => { + return treeIds.indexOf(f.id) == -1 && !f.hidden; + }); + + root_data.push(...exceptionNodes); + return root_data; + }, + getTreeAllParent(id, data) { + // 鑾峰彇鏌愪釜鑺傜偣鐨勬墍鏈夌埗鑺傜偣淇℃伅2020.11.01 + var nodes = []; + if (!(data instanceof Array)) { + return nodes; + } + if (data.length>100) { + data = JSON.parse(JSON.stringify(data)); + } + data.forEach((x) => { + if (x.id === x.parentId) { + x.parentId = 0; + } else if (data.some((c) => c.parentId === x.id && c.id === x.parentId)) { + x.parentId = 0; + } + }); + + var _child = data.find((x) => { + return x.id === id; + }); + if (!_child) { + return []; + } + nodes.push(_child); + var _parentIds = [_child.parentId]; + for (let index = 0; index < _parentIds.length; index++) { + var _node = data.find((x) => { + return x.id === _parentIds[index] && x.id !== x.parentId; + }); + if (!_node) { + return nodes; + } + _parentIds.push(_node.parentId); + nodes.unshift(_node); + } + return nodes; + }, + //鑾峰彇鎵�鏈夎妭鐐圭殑瀛愯妭鐐� + // data鏁版嵁鏍煎紡[ + // { name: 'tree1', id: 1, parentId: 0 }, + // { name: 'tree2', id: 2, parentId: 0 }] + getTreeAllChildren(id, data) { + //閫掑綊鑾峰彇鏌愪釜鑺傜偣鐨勬墍鏈夊瓙鑺傜偣淇℃伅 + var nodes = []; + if (!(data instanceof Array)) { + return nodes; + } + if (data.length>100) { + data = JSON.parse(JSON.stringify(data)); + } + var _child = data.find((x) => { + return x.id === id; + }); + if (!_child) { + return []; + } + nodes.push(_child); + var _parentIds = [_child.id]; + for (let index = 0; index < _parentIds.length; index++) { + data.forEach((_node) => { + if ( + _node.parentId === _parentIds[index] && + _node.parentId !== _node.id + ) { + _parentIds.push(_node.id); + nodes.unshift(_node); + } + }); + } + return nodes; + }, + //鑾峰彇鎵�鏈夊瓙鑺傜偣鐨刬d + // data鏁版嵁鏍煎紡[ + // { name: 'tree1', id: 1, parentId: 0 }, + // { name: 'tree2', id: 2, parentId: 0 }] + getTreeAllChildrenId(id, data) { + return this.getTreeAllChildren(id, data).map((c) => { + return c.id; + }); + } +}; +export default base; + +// 2020.06.01澧炲姞閫氱敤鏂规硶锛屽皢鏅�氬璞¤浆鎹负tree缁撴瀯 +function getTree(id, node, data, callback, treeIds) { + if (treeIds.indexOf(id) == -1) { + treeIds.push(id); + } + data.forEach((x) => { + if (!x.hidden && x.parentId == id) { + if (!node.children) node.children = []; + callback && callback(x, node, false); + node.children.push(x); + getTree(x.id, x, data, callback, treeIds); + } + }); +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Home.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Home.vue" new file mode 100644 index 0000000..820437a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Home.vue" @@ -0,0 +1,24 @@ +<template> + <div class="title"></div> +</template> + +<script> +import { ref, reactive } from 'vue' + +export default { + setup() { + return { + + } + } +} +</script> + +<style scoped> +.title { + line-height: 70vh; + text-align: center; + font-size: 28px; + color: orange; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Index.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Index.vue" new file mode 100644 index 0000000..e59ef4f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Index.vue" @@ -0,0 +1,716 @@ +<template> + <div id="vol-container" :class="['vol-theme-' + theme]"> + <div class="vol-aside" :style="{ width: menuWidth + 'px' }"> + <div class="header" :style="{ width: menuWidth - 1 + 'px' }"> + <img v-show="!isCollapse" v-bind:src="logo" /> + <i @click="toggleLeft" class="el-icon-s-fold collapse-menu" /> + </div> + <div class="vol-menu"> + <el-scrollbar style="height: 100%"> + <VolMenu :currentMenuId="currentMenuId" :on-select="onSelect" :enable="true" :open-select="false" + :isCollapse="isCollapse" :list="menuOptions"></VolMenu> + </el-scrollbar> + </div> + </div> + <div class="vol-container" :style="{ left: menuWidth - 1 + 'px' }"> + <div class="vol-header"> + <div class="project-name">WCS</div> + <div class="header-text"> + <!-- <div class="h-link"> + <a + href="javascript:void(0)" + @click="to(item)" + v-for="(item, index) in links.filter((c) => { + return !c.icon; + })" + :key="index" + > + <span v-if="!item.icon"> {{ item.text }}</span> + <i v-else :class="item.icon"></i> + </a> + </div> --> + </div> + <div class="header-info"> + <div class="h-link"> + <a href="javascript:void(0)" @click="to(item)" v-for="(item, index) in links.filter((c) => { + return c.icon; + })" :key="index"> + <span> {{ item.text }}</span> + </a> + + </div> + <div> + <img class="user-header" :src="userImg" :onerror="errorImg" /> + </div> + <div class="user"> + <span>{{ userName }}</span> + <span id="index-date"></span> + </div> + <div class="settings"> + <i style="font-size: 20px" class="el-icon-s-tools" @click="drawer_model = true" /> + </div> + </div> + </div> + <div class="vol-path"> + <el-tabs @tab-click="selectNav" @tab-remove="removeNav" @contextmenu.prevent="bindRightClickMenu(false)" + type="border-card" class="header-navigation" v-model="selectId" :strtch="false"> + <el-tab-pane v-for="(item, navIndex) in navigation" type="card" :name="navIndex + ''" :closable="navIndex > 0" + :key="navIndex" :label="item.name"> + <span style="display: none">{{ navIndex }}</span> + </el-tab-pane> + </el-tabs> + <!-- 鍙抽敭鑿滃崟 --> + <div v-show="contextMenuVisible"> + <ul :style="{ left: menuLeft + 'px', top: menuTop + 'px' }" class="contextMenu"> + <li v-show="visibleItem.all"> + <el-button link @click="closeTabs()"> + <i class="el-icon-close"></i> + {{ + navigation.length == 2 ? "鍏抽棴鑿滃崟" : "鍏抽棴鎵�鏈�" + }}</el-button> + </li> + <li v-show="visibleItem.left"> + <el-button link @click="closeTabs('left')"><i class="el-icon-back"></i>鍏抽棴宸﹁竟</el-button> + </li> + <li v-show="visibleItem.right"> + <el-button link @click="closeTabs('right')"> + <i class="el-icon-right"></i>鍏抽棴鍙宠竟</el-button> + </li> + <li v-show="visibleItem.other"> + <el-button link @click="closeTabs('other')"><i class="el-icon-right"></i>鍏抽棴鍏朵粬 + </el-button> + </li> + </ul> + </div> + </div> + <div class="vol-main" id="vol-main"> + <el-scrollbar style="height: 100%" v-if="permissionInited"> + <loading v-show="$store.getters.isLoading()"></loading> + <router-view v-slot="{ Component }"> + <keep-alive> + <component :is="Component" :key="$route.name" + v-if="!$route.meta || ($route.meta && !$route.meta.hasOwnProperty('keepAlive'))" /> + </keep-alive> + <component :is="Component" :key="$route.name" v-if="$route.meta && $route.meta.hasOwnProperty('keepAlive')" /> + </router-view> + </el-scrollbar> + </div> + </div> + <el-drawer title="閫夋嫨涓婚" v-model="drawer_model" direction="rtl" destroy-on-close> + <div class="theme-selector"> + <div @click="changeTheme(item.name)" class="item" v-for="(item, index) in theme_color" :key="index" + :style="{ background: item.color }"> + <div v-show="item.leftColor" :style="{ background: item.leftColor }" style="height: 100%; width: 20px" + class="t-left"></div> + <div class="t-right"></div> + </div> + </div> + </el-drawer> + + <el-drawer title="娑堟伅鍒楄〃" v-model="messageModel" direction="rtl" destroy-on-close> + <Message :list="messageList"></Message> + </el-drawer> + </div> +</template> +<style lang="less" scoped> +@import "./index/index.less"; +</style> +<script> +import loading from "@/components/basic/RouterLoading"; +import VolMenu from "@/components/basic/VolElementMenu.vue"; +import Message from "./index/Message.vue"; +import MessageConfig from "./index/MessageConfig.js"; +var imgUrl = require("@/assets/imgs/wms_x.png"); +var $this; +var $interval; +var $indexDate; +import { + defineComponent, + reactive, + ref, + watch, + onMounted, + getCurrentInstance, +} from "vue"; +import { useRouter, useRoute } from "vue-router"; +import store from "../store/index"; +import http from "@/../src/api/http.js"; +export default defineComponent({ + components: { + VolMenu, + loading, + Message, + }, + + data() { + return { + allTabs: true, + leftTabs: true, + rightTabs: true, + otherTabs: true, + menuLeft: 0, + menuTop: 0, + // contextMenuVisible: false, // 鍙抽敭鍏抽棴鏄�/闅� + }; + }, + setup(props, context) { + // 鑾峰彇鍏ㄥ眬灞炴�у拰鏂规硶 + const { proxy } = getCurrentInstance(); + + // 鑿滃崟瀵艰埅榛樿瀹藉害 + const menuWidth = ref(200); + const contextMenuVisible = ref(false); + const isCollapse = ref(false); + const drawer_model = ref(false); + const messageModel = ref(false); + const theme_color = ref([ + { name: "blue", color: "rgb(45, 140, 240)" }, + { name: "blue2", color: "rgb(45, 140, 240)", leftColor: "#0068d6" }, + { name: "red", color: "rgb(237, 64, 20)" }, + { name: "red2", color: "rgb(237, 64, 20)", leftColor: "#a90000" }, + { name: "dark", color: "#272929" }, + { name: "orange", color: "#ff9900" }, + { name: "orange2", color: "#ff9900", leftColor: "rgb(232 141 5)" }, + { name: "green", color: "rgb(25, 190, 107)" }, + { name: "green2", color: "rgb(25, 190, 107)", leftColor: "#019e4f" }, + { name: "white", color: "#fff" }, + ]); + const links = ref([ + // { + // text: "妗嗘灦瑙嗛", + // path: "https://www.cctalk.com/m/group/90268531", + // id: -3, + // }, + // { text: "澶у睆鏁版嵁", path: "/bigdata", id: -3 }, + // { + // text: "妗嗘灦鏂囨。", + // path: "http://v2.volcore.xyz/document/guide", + // id: -2, + // }, { + // text: "妗嗘灦浼佷笟鐗�", + // path: "http://pro.volcore.xyz/", + // id: 10, + // }, + { text: "涓汉涓績", path: "/UserInfo", id: -1, icon: "el-icon-s-custom" }, + { + text: "瀹夊叏閫�鍑�", + path: "/login", + id: -4, + icon: "el-icon-switch-button", + }, + ]); + const errorImg = ref( + 'this.src="' + require("@/assets/imgs/error-img.png") + '"' + ); + const selectId = ref("1"); + // 銆愰椤点�戞爣绛惧簭鍙�(褰撳墠鍙抽敭閫変腑鐨勮彍鍗�) + const selectMenuIndex = ref("0"); + //2022.05.29澧炲姞tab閫夐」涓庤彍鍗曡仈鍔ㄥ姛鑳� + const currentMenuId = ref(0); + const userName = ref("--"); + const userInfo = ref({}); + const visibleItem = reactive({ + left: false, + right: false, + all: false, + other: false, + }); + const userImg = ref(""); + const navigation = reactive([ + { orderNo: "0", id: "1", name: "棣栭〉", path: "/home" }, + ]); + const logo = ref(imgUrl); + const theme = ref("blue2"); + const menuOptions = ref([]); + const permissionInited = ref(false); + const messageList = reactive([]); + let _config = getCurrentInstance().appContext.config.globalProperties; + let router = useRouter(); + const toggleLeft = () => { + isCollapse.value = !isCollapse.value; + menuWidth.value = isCollapse.value ? 63 : 200; + }; + //2021.08.28寮�鏀炬墜鍔ㄦ姌鍙犺彍鍗曟柟娉� + _config.menu = { + show() { + toggleLeft(); + }, + hide() { + toggleLeft(); + }, + }; + const changeTheme = (name) => { + if (theme.value != name) { + theme.value = name; + } + localStorage.setItem("vol3_theme", name); + }; + const to = (item) => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + if (item.path.indexOf("http") != -1) { + window.open(item.path); + return; + } + if (typeof item == "string" || item.path == "/login") { + if (item == "/login" || item.path == "/login") { + //纭鏄惁閫�鍑� + store.commit("clearUserInfo", ""); + window.location.href = "/"; + return; + } + router.push({ path: item }); + return; + } + if (item.path == "#") return; + open(item); + }; + const open = (item, useRoute) => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + let _index = navigation.findIndex((x) => { + return x.path == item.path; + }); + if (_index == -1) { + navigation.push({ + // orderNo: String(navigation.length),// 搴忓彿 + id: item.id + "", + name: item.name || item.text || "鏃犳爣棰�", + path: item.path, + query: item.query, //2021.03.20淇鑷畾涔変簩娆℃墦寮�$tabs鏃跺弬鏁颁涪澶辩殑闂 + }); + //鏂版墦寮�鐨則ab绉昏嚦鏈�鍚庝竴涓�夐」 + selectId.value = navigation.length - 1 + ""; + } else { + selectId.value = _index + ""; + } + if (useRoute === undefined) { + //闈炴爣鍑嗚彍鍗曪紝璁板綍鏈�鍚庝竴娆¤烦杞殑椤甸潰锛岀敤浜庡埛鏂� + setItem(item); + router.push(item); + // this.$router.push(item); + } + currentMenuId.value = item.id * 1; + // tab鑿滃崟缁戝畾鍙抽敭浜嬩欢 + proxy.$nextTick(function (e) { + proxy.bindRightClickMenu(true); + }); + }; + const close = (path) => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + let index = navigation.findIndex((x) => { + return x.path == path; + }); + if (index == -1) { + return _config.$Message.error("鏈壘鍒拌彍鍗�"); + } + removeNav(index); + }; + const setItem = (item) => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + localStorage.setItem( + window.location.origin + "_tabs", + JSON.stringify(item) + ); + }; + const getItem = () => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + let nav = localStorage.getItem(window.location.origin + "_tabs"); + return nav ? JSON.parse(nav) : null; + }; + const selectNav = (item) => { + //鍗囩骇element姝e紡鐗堜慨鏀� + selectId.value = item.props.name; + let _path = navigation[item.index].path; + currentMenuId.value = ( + menuOptions.value.find((c) => { + return c.path == _path; + }) || { id: 0 } + ).id; + + router.push({ + path: navigation[item.index].path, + query: navigation[item.index].query, + }); + }; + + const removeNav = (_index) => { + return new Promise(() => { + //鍏抽棴鐨勫綋鍓嶉」,璺宠浆鍒板墠涓�涓〉闈� + if (selectId.value == _index + "") { + console.log(navigation[_index - 1]); + setItem(navigation[_index - 1]); + router.push({ + path: navigation[_index - 1].path, + //2022.06.27淇tabs浜屾鍒囨崲鍚庡弬鏁颁涪澶辩殑闂 + query: navigation[_index - 1].query, + }); + navigation.splice(_index, 1); + selectId.value = selectId.value - 1 + ""; + return; + } + if (_index < selectId.value) { + selectId.value = selectId.value - 1 + ""; + } + navigation.splice(_index, 1); + currentMenuId.value = ( + menuOptions.value.find((c) => { + return c.path == navigation[selectId.value * 1].path; + }) || { id: 0 } + ).id; + }); + }; + + const getSelectMenuName = (id) => { + return menuOptions.value.find(function (x) { + return x.id == id; + }); + }; + const onSelect = (treeId) => { + /* 2020.07.31澧炲姞鎵嬪姩鎵撳紑tabs*/ + var item = getSelectMenuName(treeId); + open(item, false); + }; + + /** + * 鏄剧ず鍙抽敭鑿滃崟 + * @param {*} e 浜嬩欢瀵硅薄 + */ + const openTabsMenu = function (e) { + e.preventDefault(); // 闃叉榛樿鑿滃崟寮瑰嚭 + let tabId = e.target.id.split("-")[1] * 1; + + //璁板綍褰撳墠閫変腑鐨勮彍鍗昳ndex + selectMenuIndex.value = + document.getElementById("pane-" + tabId).children[0].textContent * 1; + //鍙湁棣栭〉鏃朵笉鏄剧ず + if (navigation.length == 1) { + return; + } + + //棣栭〉璁剧疆鏄剧ず鍏抽棴鍙宠竟鑿滃崟 + if (!selectMenuIndex.value) { + visibleItem.all = false; + visibleItem.right = true; + visibleItem.left = false; + visibleItem.other = false; + } else { + visibleItem.all = true; + //涓嶆槸鏈�鍚庝竴涓樉绀哄叧闂彸杈硅彍鍗� + visibleItem.right = selectMenuIndex.value != navigation.length - 1; + //鍙湁涓や釜鑿滃崟鏃朵笉鏄剧ず鍏抽棴宸﹁竟 + visibleItem.left = navigation.length != 2; + //鍙湁涓や釜鑿滃崟鏃朵笉鏄剧ず鍏抽棴鍏朵粬 + visibleItem.other = navigation.length != 2; + } + contextMenuVisible.value = true; + // 璁剧疆鍙抽敭鑿滃崟鏄剧ず鐨勪綅缃� + proxy.menuLeft = + e.target.getBoundingClientRect().left - (isCollapse.value ? 63 : 198); //-e.target.clientWidth + proxy.menuTop = 36; + }; + + /** + * 鍏抽棴鍙抽敭鑿滃崟 + */ + const closeTabsMenu = () => { + contextMenuVisible.value = false; + }; + const toHome = () => { + open({ + text: navigation[0].name, + path: navigation[0].path, + }); + }; + /** + * 鍏抽棴鍏跺畠鏍囩椤� + * @param {*} par 鍏抽棴绫诲瀷(left,right,other) + */ + const closeTabs = (value) => { + let _menuId = navigation[selectId.value * 1].id; + let currnetIndex = selectId.value * 1; // navigation.findIndex(c => { return c.id == selectId.value }); + switch (value) { + case "left": { + // 鍒犻櫎宸︿晶tab鏍囩 + navigation.splice(1, currnetIndex - 1); // 鍒犻櫎宸︿晶tab鏍囩 + break; + } + case "right": { + // 鍒犻櫎鍙充晶tab鏍囩 + if (selectMenuIndex.value == 0) { + navigation.splice(currnetIndex); // 鍒犻櫎鍙充晶tab鏍囩 + toHome(); + } else { + navigation.splice(currnetIndex + 1); // 鍒犻櫎鍙充晶tab鏍囩 + if (selectMenuIndex.value < currnetIndex) { + navigation.splice( + selectMenuIndex.value, + currnetIndex - selectMenuIndex.value + ); + } + } + break; + } + case "other": { + // 鍒犻櫎鍏朵粬鎵�鏈塼ab鏍囩 + navigation.splice(currnetIndex + 1); // 鍒犻櫎鍙充晶tab鏍囩(杩欓噷蹇呴』鎸夌収鍙斥啋宸﹂『搴忓垹闄�) + navigation.splice(1, currnetIndex - 1); // 鍒犻櫎宸︿晶tab鏍囩 + break; + } + default: { + //鍏抽棴鎵�鏈� + navigation.splice(1, navigation.length); + toHome(); + break; + } + } + selectId.value = + navigation.findIndex((c) => { + return c.id == _menuId; + }) + ""; + closeTabsMenu(); + }; + + watch( + () => contextMenuVisible.value, + (newVal, oldVal) => { + // 鐩戣 + if (newVal) { + document.body.addEventListener("click", closeTabsMenu); + } else { + document.body.removeEventListener("click", closeTabsMenu); + } + } + ); + + /** + * 绯荤粺鍒涘缓寮�濮� + */ + const created = () => { + let _theme = localStorage.getItem("vol3_theme"); + if (_theme) { + theme.value = _theme; + } + + let _userInfo = store.getters.getUserInfo(); + if (_userInfo) { + userName.value = _userInfo.userName; + if (_userInfo.img) { + userImg.value = _config.base.getImgSrc(_userInfo.img, http.ipAddress); + } + } + Object.assign(_config.$tabs, { open: open, close: close }); + + http.get("api/Sys_Menu/getTreeMenu", {}, true).then((data) => { + data.push({ id: "1", name: "棣栭〉", url: "/home" }); // 涓轰簡鑾峰彇閫変腑id浣跨敤 + data.forEach((d) => { + d.path = (d.url || "").replace("/Manager", ""); + d.to = (d.url || "").replace("/Manager", ""); + if (!d.icon || d.icon.substring(0, 3) != "el-") { + d.icon = "el-icon-menu"; + } + }); + store.dispatch("setPermission", data); + menuOptions.value = data; + permissionInited.value = true; + + //寮�鍚秷鎭帹閫侊紙main.js涓缃槸鍚﹀紑鍚痵ignalR锛�2022.05.05 + if (_config.$global.signalR) { + MessageConfig(http, (result) => { + messageList.unshift(result); + // console.log(result) + }); + } + + //褰撳墠鍒锋柊鏄笉鏄椤� + if (router.currentRoute.value.path != navigation[0].path) { + //鏌ユ壘绯荤粺鑿滃崟 + let item = menuOptions.value.find((x) => { + return x.path == router.currentRoute.value.path; //this.$route.path; + }); + if (item) return onSelect(item.id); + //鏌ユ壘椤堕儴蹇嵎杩炴帴 + item = links.value.find((x) => { + return x.path == router.currentRoute.value.path; //this.$route.path; + }); + //鏌ユ壘鏈�鍚庝竴娆¤烦杞殑椤甸潰 + if (!item) { + item = getItem(); + } + if (item) { + return open(item, false); + } + } + selectId.value = "1"; + }); + }; + created(); + return { + menuWidth, + isCollapse, + drawer_model, + theme_color, + errorImg, + userInfo, + userName, + userImg, + selectId, + selectMenuIndex, + navigation, + links, + onSelect, + openTabsMenu, + selectNav, + getSelectMenuName, + removeNav, + logo, + theme, + menuOptions, + permissionInited, + changeTheme, + to, + toggleLeft, + messageModel, + messageList, + contextMenuVisible, + visibleItem, + closeTabsMenu, + closeTabs, + currentMenuId, + }; + }, + /** + * 鎸傝浇閽╁瓙鍑芥暟 + */ + mounted() { + let _date = showTime(); + $indexDate = document.getElementById("index-date"); + $indexDate.innerText = _date; + $interval = setInterval(function () { + $indexDate.innerText = showTime(); + }, 1000); + + this.bindRightClickMenu(true); + }, + + methods: { + /** + * 缁戝畾鍙抽敭浜嬩欢 + * @param {*} enable 鏄惁鍚敤鍙抽敭浜嬩欢[true:鍚敤;false:绂佺敤;] + * @param {*} $event 浜嬩欢 + */ + bindRightClickMenu(enable) { + if (!enable) return; + let that = this; + // 浣跨敤鍘熺敓js 涓哄崟涓猟om缁戝畾榧犳爣鍙冲嚮浜嬩欢 + that.$nextTick(() => { + let tab_top_dom = Object.assign( + [], + document.getElementsByClassName("el-tabs__item is-top") + ); + tab_top_dom.forEach((item, index) => { + item.oncontextmenu = that.openTabsMenu; + }); + }); + }, + }, + + /** + * 閿�姣侀挬瀛愬嚱鏁� + */ + destroyed() { + $this = null; + clearInterval($interval); + }, +}); +const week = new Array( + "鏄熸湡涓�", + "鏄熸湡浜�", + "鏄熸湡涓�", + "鏄熸湡鍥�", + "鏄熸湡浜�", + "鏄熸湡鍏�", + "鏄熸湡鏃�" +); +function showTime() { + let date = new Date(); + let year = date.getFullYear(); + let month = date.getMonth() + 1; + let day = date.getDate(); + let hour = date.getHours(); + let minutes = date.getMinutes(); + let second = date.getSeconds(); + + return ( + year + + "." + + (month < 10 ? "0" + month : month) + + "." + + (day < 10 ? "0" + day : day) + //202.08.08淇鏃ユ湡澶╂暟灏忎簬10鏃舵坊鍔�0 + "" + + " " + + (hour < 10 ? "0" + hour : hour) + + ":" + + (minutes < 10 ? "0" + minutes : minutes) + + ":" + + (second < 10 ? "0" + second : second) + + " " + //2020.08.30淇棣栭〉鏃ユ湡鏄熸湡澶╀笉鏄剧ず鐨勯棶棰� + (week[date.getDay() - 1] || week[6]) + ); +} +</script> + +<style lang="less" scoped> +.vol-container .vol-path ::v-deep(.el-tabs__content) { + padding: 0; +} + +.contextMenu { + width: 120px; + margin: 0; + border: 1px solid #eaeaea; + background: #fff; + z-index: 30000; + position: absolute; + list-style-type: none; + padding: 5px 0; + border-radius: 4px; + font-size: 14px; + color: #333; + box-shadow: 2px 2px 3px 0 rgb(182 182 182 / 20%); + + i, + button { + font-size: 14px !important; + } +} + +.contextMenu li { + margin: 0; + padding: 5px 17px; +} + +.contextMenu li:hover { + background: #fafafa; + cursor: pointer; +} + +.contextMenu li button { + color: #626060; + font-size: 14px; + letter-spacing: 1px; +} + +.el-tabs.el-tabs--top.el-tabs--border-card.header-navigation>.el-tabs__header .el-tabs__item:last-child, +.el-tabs--top.el-tabs--border-card.header-navigation>.el-tabs__header .el-tabs__item:nth-child(2) { + padding: 0; +} + +.header-navigation ::v-deep(.el-tabs__item.is-top) { + padding: 0 15px; +} +</style> +<style> +.horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, + 0s padding-right ease-in-out; +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Login.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Login.vue" new file mode 100644 index 0000000..c61c0e8 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/Login.vue" @@ -0,0 +1,386 @@ +<template> + <div class="login-container"> + <div class="project-name">WIDESEA_WMS</div> + <div class="login-form"> + <div class="form-user" @keypress="loginPress"> + <div class="login-text"> + <div> + <div>娆㈣繋鐧诲綍...</div> + <div class="login-line"></div> + </div> + <div style="flex:1;"></div> + </div> + <div class="login-text-small">WELCOME TO LOGIN</div> + <div class="item"> + <div class="input-icon el-icon-user"></div> + <input type="text" v-focus v-model="userInfo.userName" placeholder="璇疯緭鍏ヨ处鍙�" /> + </div> + <div class="item"> + <div class="input-icon el-icon-lock"></div> + <input type="password" v-focus v-model="userInfo.password" placeholder="璇疯緭鍏ュ瘑鐮�" /> + </div> + <div class="item"> + <div class="input-icon el-icon-mobile"></div> + + <input v-focus type="text" v-model="userInfo.verificationCode" placeholder="杈撳叆楠岃瘉鐮�" /> + <div class="code" @click="getVierificationCode"> + <img v-show="codeImgSrc != ''" :src="codeImgSrc" /> + </div> + </div> + </div> + <div class="loging-btn"> + <el-button size="large" :loading="loading" color="#3a6cd1" :dark="true" @click="login" long> + <span v-if="!loading">鐧诲綍</span> + <span v-else>姝e湪鐧诲綍...</span> + </el-button> + </div> + + <!-- 璐﹀彿淇℃伅 --> + <!-- <div class="account-info"> + <p>婕旂ず璐﹀彿锛歛dmin666 瀵嗙爜:123456</p> + <p>鏈湴璐﹀彿锛歛dmin 瀵嗙爜:123456</p> + <p><a href="https://jq.qq.com/?_wv=1027&k=Sqstuy0M" style="text-decoration: none" + target="_blank">QQ3缇�:743852316</a> + + <a href="http://v2.volcore.xyz/document/guide" style="text-decoration: none" target="_blank">妗嗘灦鏂囨。</a> + </p> + </div> --> + <!-- 閾炬帴浣嶇疆 --> + <!-- <div class="app-link" > + <a href="#" style="text-decoration: none">绉诲姩绔壂鐮�</a> + <a> + <i class="el-icon-chat-dot-round"></i> 灏忕▼搴� + <img src="https://app-1256993465.cos.ap-nanjing.myqcloud.com/wechat.jpg" /></a> + <a> + <i class="el-icon-apple"></i> + Android + <img src="https://app-1256993465.cos.ap-nanjing.myqcloud.com/Android.png" /></a> + <a> + <i class="el-icon-document"></i> + H5 + <img src="https://app-1256993465.cos.ap-nanjing.myqcloud.com/H5.png" /></a> + </div> --> + </div> + + <!-- 椤甸潰搴曢儴 --> + <!-- <div class="login-footer"> + <a style="text-decoration: none" href="https://beian.miit.gov.cn/" target="_blank">浜琁CP澶�19056538鍙�-1</a> + + + <a href="https://dotnet9.com/" style="text-decoration: none" target="blank">Dotnet9</a> + <a href="https://space.bilibili.com/525836469" style="text-decoration: none" target="blank">NET瑙嗛鏁欑▼(寰蒋MVP-ACE褰曞埗)</a> + <a href="https://www.cctalk.com/m/group/90268531" style="text-decoration: none" target="blank">VOL妗嗘灦瑙嗛</a> + <a href="http://120.48.115.252:9990" style="text-decoration: none" target="blank">瑙嗛婕旂ず鍦板潃</a> + </div> --> + + <img class="login-bg" src="/static/login_bg.png" /> + </div> +</template> + + +<script > +import { + defineComponent, + ref, + reactive, + toRefs, + getCurrentInstance +} from 'vue'; +import { useRouter, useRoute } from 'vue-router'; +import store from '../store/index'; +import http from '@/../src/api/http.js'; +export default defineComponent({ + setup(props, context) { + store.commit('clearUserInfo', ''); + const loading = ref(false); + const codeImgSrc = ref(''); + const userInfo = reactive({ + userName: '', + password: '', + verificationCode: '', + UUID: undefined + }); + + const getVierificationCode = () => { + http.get('/api/User/getVierificationCode').then((x) => { + codeImgSrc.value = 'data:image/png;base64,' + x.img; + userInfo.UUID = x.uuid; + }); + }; + getVierificationCode(); + + let appContext = getCurrentInstance().appContext; + let $message = appContext.config.globalProperties.$message; + let router = useRouter(); + + const login = () => { + if (!userInfo.userName) return $message.error('璇疯緭鍏ョ敤鎴峰悕'); + if (!userInfo.password) return $message.error('璇疯緭鍏ュ瘑鐮�'); + if (!userInfo.verificationCode) { + return $message.error('璇疯緭鍏ラ獙璇佺爜'); + } + loading.value = true; + http.post('/api/User/login', userInfo, '姝e湪鐧诲綍....').then((result) => { + if (!result.status) { + loading.value = false; + getVierificationCode(); + return $message.error(result.message); + } + $message.success('鐧诲綍鎴愬姛,姝e湪璺宠浆!'); + store.commit('setUserInfo', result.data); + router.push({ path: '/' }); + }); + }; + const loginPress = (e) => { + if (e.keyCode == 13) { + login(); + } + }; + const openUrl = (url) => { + window.open(url, '_blank'); + }; + return { + loading, + codeImgSrc, + getVierificationCode, + login, + userInfo, + loginPress, + openUrl + }; + }, + directives: { + focus: { + inserted: function (el) { + el.focus(); + } + } + } +}); +</script> +<style lang="less" scoped> +.login-container { + display: flex; + width: 100%; + height: 100%; + background: rgb(246, 247, 252); + justify-content: flex-end; + align-items: center; +} + +.login-form { + align-items: center; + width: 50%; + display: flex; + flex-direction: column; + // margin-right: 150px; + z-index: 999; + + .form-user { + // margin: 25px 0; + + .item { + border-radius: 5px; + border: 1px solid #ececec; + display: flex; + margin-bottom: 30px; + background: #ffff; + height: 45px; + padding-left: 20px; + display: flex; + + .code { + position: relative; + cursor: pointer; + width: 74px; + padding: 5px 10px 0 0; + } + + .input-icon { + line-height: 45px; + color: #7a7a7a; + padding-right: 20px; + } + } + } + + input:-webkit-autofill { + box-shadow: 0 0 0px 1000px white inset; + -webkit-box-shadow: 0 0 0px 1000px white inset !important; + } + + input { + background: white; + display: block; + box-sizing: border-box; + width: 100%; + min-width: 0; + margin: 0; + padding: 0; + color: #323233; + line-height: inherit; + text-align: left; + border: 0; + outline: none; + font-size: 16px; + line-height: 20px; + } +} + +.form-user, +.loging-btn { + width: 400px; +} + +.loging-btn { + box-shadow: 2px 4px 11px #a4c2ff; + margin-top: 10px; + + button { + padding: 21px; + font-size: 14px !important; + width: 100%; + } +} + +.login-text { + font-weight: bolder; + font-size: 20px; + letter-spacing: 2px; + + position: relative; + display: flex; + + .login-line { + z-index: -1; + padding: 5px; + position: relative; + top: -8px; + width: 100%; + background-image: linear-gradient(to right, #6598ff, white); + } +} + +.login-text-small { + margin-bottom: 20px; + font-size: 13px; + color: #7d7c7c; +} + +.login-bg { + left: 0; + position: absolute; + height: 100%; + width: 50%; + z-index: 0; +} + +.project-name { + position: absolute; + top: 40px; + left: 40px; + z-index: 9999; + font-weight: bolder; + background-image: linear-gradient(to right, #1850c1, #9c009c); + -webkit-background-clip: text; + color: transparent; + font-size: 25px; +} +</style> +<style lang="less" scoped> +.app-link { + // font-weight: bolder; + text-align: center; + padding-top: 5px; + font-size: 12px; + width: 400px; + padding-left: 40px; + display: flex; + + a { + flex: 1; + position: relative; + cursor: pointer; + width: 70px; + color: #666666; + margin: 2px 10px 0 0; + } + + img { + display: none; + } + + a:hover { + color: #0545f6 !important; + + img { + display: block; + position: absolute; + z-index: 999999999; + top: -130px; + width: 120px; + left: -22px; + + border: 1px solid #b1b1b1; + } + } +} + +.login-footer { + position: absolute; + width: 50%; + bottom: 0.5rem; + font-size: 12px; + text-align: center; + padding-bottom: 10px; + color: #4f4f4f; + + a { + margin-right: 10px; + font-size: 12px; + color: #4f4f4f; + } + + div { + margin-bottom: 5px; + } + + a:hover { + cursor: pointer; + color: #0540e3 !important; + } +} + +.account-info { + font-size: 12px; + color: #636363; + margin-top: 15px; +} +</style> + +<style lang="less" scoped> +@media screen and (max-width: 700px) { + + .login-bg, + .account-info, + .app-link, + .login-footer, + .project-name { + display: none; + } + + .login-container { + padding: 2rem; + justify-content: center; + } + + .login-form { + width: 100%; + } + + .form-user, + .loging-btn { + width: 100%; + } +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue" new file mode 100644 index 0000000..319e35e --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue" @@ -0,0 +1,265 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> + <script> +import extend from "@/extension/basicinfo/router.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "id", + footer: "Foots", + cnName: "璺敱閰嶇疆淇℃伅", + name: "router", + url: "/Router/", + sortName: "createDate", + }); + const editFormFields = ref({ + // name: "", + // jobGroup: "", + // assemblyName: "", + // className: "", + // intervalSecond: "", + // beginTime: "", + // endTime: "", + // remark: "", + }); + const editFormOptions = ref([ + // [ + // { + // title: "浠诲姟鍚嶇О", + // required: true, + // field: "name", + // type: "string", + // }, + // { + // title: "浠诲姟鍒嗙粍", + // required: true, + // field: "jobGroup", + // type: "select", + // dataKey: "deviceType", + // data: [], + // }, + // { + // title: "绋嬪簭闆嗗悕绉�", + // required: true, + // field: "assemblyName", + // type: "string", + // type: "select", + // dataKey: "jobAssembly", + // data: [], + // }, + // { + // title: "浠诲姟鎵�鍦ㄧ被", + // required: true, + // field: "className", + // type: "string", + // type: "select", + // dataKey: "jobClassName", + // data: [], + // }, + // ], + // [ + // { + // title: "闂撮殧鏃堕棿", + // required: true, + // field: "intervalSecond", + // type: "number", + // }, + // { + // title: "寮�濮嬫椂闂�", + // field: "beginTime", + // type: "datetime", + // }, + // { + // title: "缁撴潫鏃堕棿", + // field: "endTime", + // type: "datetime", + // }, + // { + // title: "澶囨敞", + // field: "remark", + // type: "string", + // }, + // ], + ]); + const searchFormFields = ref({ + startPosi: "", + nextPosi: "", + // assemblyName: "", + // className: "", + }); + const searchFormOptions = ref([ + [ + { + title: "璧风偣浣嶇疆", + field: "startPosi", + type: "like", + }, + { + title: "缁堢偣浣嶇疆", + field: "nextPosi", + type: "like", + }, + // { + // title: "绋嬪簭闆嗗悕绉�", + // field: "assemblyName", + // type: "like", + // }, + // { + // title: "浠诲姟鎵�鍦ㄧ被", + // field: "className", + // type: "like", + // }, + ], + ]); + const columns = ref([ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "startPosi", + title: "璧风偣浣嶇疆", + type: "string", + width: 90, + align: "left", + }, + { + field: "nextPosi", + title: "缁堢偣浣嶇疆", + type: "string", + width: 180, + align: "left", + }, + { + field: "inOutType", + title: "璺敱绫诲瀷", + type: "string", + width: 180, + align: "left", + }, + { + field: "childPosi", + title: "瀛愪綅缃�", + type: "string", + width: 200, + align: "left", + }, + { + field: "childPosiDeviceCode", + title: "瀛愪綅缃墍灞炶澶�", + type: "string", + width: 120, + align: "left", + }, + { + field: "srmRow", + title: "鍫嗗灈鏈哄彇璐�/鏀捐揣琛�", + type: "int", + width: 150, + align: "left", + }, + { + field: "srmColumn", + title: "鍫嗗灈鏈哄彇璐�/鏀捐揣鍒�", + type: "int", + width: 150, + align: "left", + }, + { + field: "srmLayer", + title: "鍫嗗灈鏈哄彇璐�/鏀捐揣灞�", + type: "int", + width: 150, + align: "left", + }, + { + field: "depth", + title: "娣卞害", + type: "int", + width: 150, + align: "left", + }, + { + field: "isEnd", + title: "鏄惁鏄渶缁堢偣", + type: "bool", + width: 150, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + }, + ]); + const detail = ref({ + cnName: "", + table: "", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/builderData.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/builderData.js" new file mode 100644 index 0000000..78f6b37 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/builderData.js" @@ -0,0 +1,195 @@ +let columnType = [{ "key": 1, "value": "img" }, +{ "key": 2, "value": "excel" }, +{ "key": 3, "value": "file" }, +//2021.07.27澧炲姞table鍒楁樉绀虹被鍨媎ate(鑷姩鏍煎紡鍖�) +{ "key": 4, "value": "date" } +] + +let dataType = [ + { "key": "text", "value": "input" }, + { "key": "textarea", "value": "textarea" }, + { "key": "switch", "value": "switch" }, + // { "key": "dropList", "value": "dropList" }, + { "key": "select", "value": "select" }, + { "key": "selectList", "value": "select澶氶��" }, + { "key": "date", "value": "date" }, + { "key": "datetime", "value": "datetime" }, + { "key": "month", "value": "骞存湀鏃�" }, + { "key": "rate", "value": "rate璇勫垎" }, + { "key": "time", "value": "time" }, + { "key": "checkbox", "value": "checkbox" }, + // 2021.05.16闆嗘垚iview radio缁勪欢 + { "key": "radio", "value": "radio" }, + { "key": "cascader", "value": "绾ц仈" },//2020.11.01澧炲姞绾ц仈閫夋嫨 + { "key": "treeSelect", "value": "鏍戝舰绾ц仈tree-select" },//2020.11.01澧炲姞绾ц仈閫夋嫨 + { "key": "editor", "value": "瀵屾枃鏈紪杈戝櫒" }, + { "key": "mail", "value": "mail" }, + { "key": "number", "value": "number" }, + { "key": "decimal", "value": "decimal" }, + { "key": "phone", "value": "phone" }, + { "key": "img", "value": "img" }, + { "key": "excel", "value": "excel" }, + { "key": "file", "value": "file" } +]; + +let searchDataType = [ + { "key": "text", "value": "input" }, + { "key": "like", "value": "妯$硦鏌ヨ" }, + { "key": "textarea", "value": "textarea" }, + { "key": "switch", "value": "switch" }, + { "key": "select", "value": "select" }, + { "key": "selectList", "value": "select澶氶��" }, + { "key": "date", "value": "date" }, + { "key": "datetime", "value": "datetime" }, + { "key": "month", "value": "year_month" }, + { "key": "time", "value": "time" }, + { "key": "cascader", "value": "绾ц仈" },//2020.11.01澧炲姞绾ц仈閫夋嫨 + { "key": "checkbox", "value": "checkbox" }, + // 2021.05.16闆嗘垚iview radio缁勪欢 + { "key": "radio", "value": "radio" }, + { "key": "range", "value": "鍖洪棿鏌ヨ" }, + { "key": "mail", "value": "mail" }, + { "key": "number", "value": "number" }, + { "key": "decimal", "value": "decimal" }, + { "key": "phone", "value": "phone" } +]; +let data = { + form: { + fields: { + table_Id: '', + parentId: null, + namespace: '', + columnCNName: '', + tableName: '', + tableTrueName: '', + folderName: '', + detailCnName: '', + detailName: '', + expressField: '', + sortName: '', + richtitle: '', + uploadField: '', + uploadMaxCount: '', + enable: 0, + vuePath: '', + appPath: "", + userPermissionDesc: '寮�鍚悗褰撳墠鐢ㄦ埛鍙兘鎿嶄綔鑷繁(涓庝笅绾ц鑹�)鍒涘缓鐨勬暟鎹�,濡�:鏌ヨ銆佸垹闄ゃ�佷慨鏀圭瓑鎿嶄綔' + }, + addOptions: [ + [{ "title": "鐖� 绾� ID", min: 0, "field": "parentId", "required": true, type: 'number', placeholder: '鏀惧湪銆愪唬鐮佺敓鎴愰厤缃�戝垪琛ㄧ殑鏂囦欢澶笽D涓�,濡傛灉濉叆銆�0銆戝氨鏄竴绾х洰褰�' }], + [{ + "title": "椤圭洰绫诲簱", + "field": "namespace", + "placeholder": "浠g爜鐢熸垚鍚庣殑鎵�鍦ㄧ被搴�(鍙互鑷繁鎻愬墠鍦ㄥ悗鍙伴」鐩腑鍒涘缓涓�涓�.netcore绫诲簱)", + "type": "select", + "required": true, + data: [] + }], + [{ "title": "琛ㄤ腑鏂囧悕", "field": "columnCNName", "required": true, placeholder: "琛ㄥ搴旂殑涓枃鍚嶅瓧,鐣岄潰涓婃樉绀轰細鐢ㄥ埌" }], + [{ "title": "瀹為檯琛ㄥ悕", "field": "tableName", "required": true, placeholder: "鏁版嵁搴撳疄闄呰〃鍚嶆垨鑰呰鍥惧悕(澶氳〃鍏宠仈璇峰垱寤鸿鍥惧啀鐢熸垚浠g爜)" }], + [{ "title": "鏂囦欢澶瑰悕", placeholder: "鐢熸垚鏂囦欢鎵�鍦ㄧ被搴撲腑鐨勬枃浠跺す鍚�(鏂囦欢澶瑰彲浠ヤ笉瀛樺湪);娉ㄦ剰鍙渶瑕佸~鍐欐枃浠跺す鍚嶏紝涓嶆槸璺緞", "field": "folderName", "required": true }] + ], + options: [ + [ + { "title": "涓� 閿� ID", "field": "table_Id", "dataSource": [], readonly: true, disabled: true, columnType: 'int' }, + { "title": "鐖� 绾� ID", "field": "parentId", min: 0, "required": true, type: 'number' }, + { + "title": "椤圭洰绫诲簱", + "placeholder": "浠g爜鐢熸垚瀛樻斁鐨勪綅缃�", + "field": "namespace", + "type": "select", + "required": true, + data: [] + } + ], + [ + { "title": "琛ㄤ腑鏂囧悕", "field": "columnCNName", "dataSource": [], "required": true }, + { "title": "琛� 鍒� 鍚�", placeholder: "榛樿涓庡疄闄呰〃鍚嶇浉鍚�", "field": "tableName", "required": true }, + { "title": "瀹為檯琛ㄥ悕", "field": "tableTrueName" }, + + ], + [ + { "title": "鏂囦欢澶瑰悕", placeholder: "鐢熸垚鏂囦欢鎵�鍦ㄧ被搴撲腑鐨勬枃浠跺す鍚�(鏂囦欢澶瑰彲浠ヤ笉瀛樺湪)", "field": "folderName", "required": true }, + { "title": "鏄庣粏琛ㄥ悕", "field": "detailCnName", placeholder: "鏄庣粏琛ㄤ腑鏂囧悕瀛�" }, + { "title": "鏄� 缁� 琛�", "field": "detailName", placeholder: "鏁版嵁搴撶殑琛ㄥ悕" }, + + ], + [ + { "title": "蹇嵎缂栬緫", "field": "expressField", placeholder: "蹇嵎缂栬緫瀛楁" }, + { "title": "鎺掑簭瀛楁", "field": "sortName", "placeholder": "澶氫釜鎺掑簭瀛楁閫楀彿闅斿紑(榛樿闄嶅簭鎺掑簭),濡傦細Name,Age", colSize: 8 }, + + // { "title": "杩樻病鎯冲ソ", "field": "richtitle" } + ], + [{ "title": "Vue璺緞", "field": "vuePath", type: "text", placeholder: 'Vue椤圭洰鎵�鍦ㄧ粷瀵硅矾寰�,鍒皏iews鏂囦欢澶�,濡傦細E:/app/src/views', colSize: 12 }, + // { "title": "app璺緞", "field": "appPath", type: "text", placeholder: 'uniapp椤圭洰鎵�鍦ㄧ粷瀵硅矾寰�,鍒皃ages鏂囦欢澶�,濡傦細E:/uniapp/pages', colSize: 6 } + ] + // [ //寰呭畬 + // { "title": "寮�鍚敤鎴锋潈闄愭暟鎹�", "field": "enable", bind: { data: [{ key: 1, value: '鏄�', key: 0, value: '鍚�' }] }, type: 'switch', colSize: 2 }, + // { "title": "鎻愮ず", "required": true, "field": "userPermissionDesc", colSize: 10, "placeholder": "闈炶嚜澧炰富閿渶瑕佽緭鍏ユ帓搴忓瓧娈�",readonly:true } + // ], + // [ + + // ], + // [ + // { "title": "瀵屾枃鏈紪杈戝瓧娈�", "field": "richtitle", "displayType": "title" }, + // { "title": "鏂囦欢涓婁紶瀛楁", "field": "uploadField", "displayType": "title" }, + // { "title": "鏂囦欢涓婁紶鏁伴噺闄愬埗", "field": "uploadMaxCount", "displayType": "title", columnType: 'int' } + // ], + // [ + // { "title": "Vue瑙嗗浘缁濆璺緞", "field": "vuePath", "displayType": "title", colSize: 12, placeholder: 'Vue椤圭洰鎵�鍦ㄧ粷瀵硅矾寰�,鍒皏iews鏂囦欢澶�,濡傦細E:/app/src/views' }, + // ] + ] + }, + //2021.01.09澧炲姞浠g爜鐢熸垚鍣ㄨ缃畉able鎺掑簭鍔熻兘 + columns: [ + { field: 'columnId', title: 'ColumnId', width: 120, align: 'left', edit: { type: "text" }, hidden: true }, + { field: 'table_Id', title: 'Table_Id', width: 120, align: 'left', editor: 'text', hidden: true }, + { field: 'columnCnName', title: '鍒楁樉绀哄悕绉�', fixed: true, width: 120, align: 'left', edit: { type: "text" } }, + { field: 'columnName', title: '鍒楀悕', fixed: true, width: 120, align: 'left', edit: { type: "text" } }, + { field: 'isKey', title: '涓婚敭', width: 90, align: 'left', edit: { type: "switch" } }, + { field: 'sortable', title: '鏄惁鎺掑簭', width: 90, align: 'left', edit: { type: "switch", keep: true } }, + { + field: 'enable', title: 'app鍒�', width: 140, align: 'left', edit: { type: "select" }, + bind: { + data: [ + { key: 1, value: "鏄剧ず/鏌ヨ/缂栬緫" }, + { key: 2, value: "鏄剧ず/缂栬緫" }, + { key: 3, value: "鏄剧ず/鏌ヨ" }, + { key: 4, value: "鏄剧ず" }, + { key: 5, value: "鏌ヨ/缂栬緫" }, + { key: 6, value: "鏌ヨ" }, + { key: 7, value: "缂栬緫" }, + ] + } + }, + { field: 'searchRowNo', title: '鏌ヨ琛�', width: 90, align: 'left', edit: { type: "text" } }, + { field: 'searchColNo', title: '鏌ヨ鍒�', width: 90, align: 'left', edit: { type: "text" } }, + { field: 'searchType', title: '鏌ヨ绫诲瀷', width: 150, align: 'left', edit: { type: "select" }, bind: { data: searchDataType } }, + { field: 'editRowNo', title: '缂栬緫琛�', width: 90, align: 'numberbox', edit: { type: "text" } }, + { field: 'editColNo', title: '缂栬緫鍒�', width: 90, align: 'numberbox', edit: { type: "text" } }, + { field: 'editType', title: '缂栬緫绫诲瀷', width: 150, align: 'left', edit: { type: "select" }, bind: { data: dataType } }, + { field: 'dropNo', title: '鏁版嵁婧�', width: 120, align: 'left', bind: { data: [] }, edit: { type: "select", data: [] } }, + { field: 'isImage', title: 'table鍒楁樉绀虹被鍨�', hidden: false, width: 130, align: 'left', edit: { type: "select" }, bind: { data: columnType } }, + { field: 'orderNo', title: '鍒楁樉绀洪『搴�', width: 120, align: 'left', edit: { type: "text" } }, + { field: 'maxlength', title: '瀛楁鏈�澶ч暱搴�', width: 130, align: 'left', edit: { type: "text" } }, + { field: 'columnType', title: '鏁版嵁绫诲瀷', width: 120, align: 'left', edit: { type: "text" } }, + { field: 'isNull', title: '鍙负绌�', width: 120, align: 'left', edit: { type: "switch", keep: true } }, + { field: 'isReadDataset', title: '鏄惁鍙', width: 120, align: 'left', edit: { type: "switch", keep: true } }, + { field: 'isColumnData', title: '鏁版嵁鍒�', width: 120, align: 'left', edit: { type: "switch", keep: true } }, + { field: 'isDisplay', title: '鏄惁鏄剧ず', width: 120, align: 'left', edit: { type: "switch", keep: true } }, + { field: 'columnWidth', title: 'table鍒楀搴�', width: 120, align: 'left', edit: { type: "text" } }, + { field: 'colSize', title: '缂栬緫鍒楁爣绛惧搴olSize', width: 180, align: 'left', edit: { type: "text" } }, + // { field: 'import', title: '瀵煎叆鍒�', hidden: true, width: 100, align: 'left', edit: { type: "switch" } }, + // { field: 'apiInPut', title: 'Api杈撳叆鍒�(寰呭疄鐜�)', width: 100, align: 'left', edit: { type: "switch" } }, + // { field: 'apiIsNull', title: 'Api杈撳叆鍒楀彲涓虹┖(寰呭疄鐜�)', width: 130, align: 'left', edit: { type: "switch" } }, + // { field: 'apiOutPut', title: 'Api杈撳嚭鍒�(寰呭疄鐜�)', width: 100, align: 'left', edit: { type: "switch" } }, + // { field: 'columnformat', title: '鏄剧ず鏍煎紡', width: 120, align: 'left', editor: 'text', editor: 'textarea' }, + // { field: 'script', title: '鑴氭湰', width: 120, align: 'left', editor: 'textarea' }, + // { field: 'creator', title: '鍒涘缓浜�', width: 120, align: 'left' }, + { field: 'createDate', title: '鍒涘缓鏃堕棿', width: 120, align: 'left' }, + // { field: 'modifier', title: '淇敼浜�', width: 120, align: 'left' }, + // { field: 'modifyDate', title: '淇敼鏃堕棿', width: 120, align: 'left' } + ] +} + +export default data \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue" new file mode 100644 index 0000000..ea9b52a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue" @@ -0,0 +1,658 @@ +<template> + <div class="builder-container"> + <vol-box + ref="add" + :width="850" + :height="450" + title="鏂板缓閰嶇疆淇℃伅" + padding="10px" + v-model="addModel" + > + <div style="padding: 30px 30px 10px 34px"> + <el-alert type="warning" :closable="false"> + 1銆佸鏋滃彧鏄垱寤虹洰褰曪紝鐖剁骇id濉�0,鍏朵粬闅忎究濉啓; + <br /> + 2銆佸鏋滄槸鐢熸垚浠g爜锛岀埗绾d濉啓銆愪唬鐮佺敓鎴愰厤缃�戝垪琛ㄩ〉闈㈢殑id + </el-alert> + </div> + <div class="addModel" style="padding-right: 30px"> + <vol-form + ref="addForm" + :formRules="addOptions" + :formFields="layOutOptins.fields" + > + </vol-form> + </div> + <template #footer> + <div> + <el-button type="primary" size="small" @click="add" + ><i class="el-icon-plus"></i> 纭� 瀹�</el-button + > + </div> + </template> + </vol-box> + <div class="builder-left"> + <div class="module-name">浠g爜鐢熸垚閰嶇疆</div> + <div class="builder-tree"> + <el-scrollbar style="height: 100%; width: 200px"> + <!-- :onOpenChange="onOpenChange" --> + <VolMenu :list="tree" :onSelect="onSelect"></VolMenu> + </el-scrollbar> + </div> + </div> + <div class="builder-content"> + <div style="height: 100%"> + <el-scrollbar style="height: 100%"> + <div class="coder-container"> + <div class="coder-item" style="padding-top: 7px"> + <VolHeader icon="ios-chatbubbles" text="浠g爜鐢熸垚鍣�"> + <template #content> + <div style="color: red; font-size: 13px"> + 鍒犻櫎宸︿晶閰嶇疆鑿滃崟:鍒犻櫎琛�->淇濆瓨->鍒犻櫎鑿滃崟 + </div> + </template> + <div class="action"> + <span @click="save"> <i class="el-icon-check"></i>淇濆瓨 </span> + <span @click="addVisible()"> + <i class="el-icon-plus"></i>鏂板缓 + </span> + <span @click="ceateVuePage(0)"> + <i class="el-icon-document"></i>鐢熸垚Vue椤甸潰 + </span> + <!-- <span @click="ceateVuePage(1)"> + <i class="el-icon-document"></i>鐢熸垚app椤甸潰 + </span> --> + <span @click="ceateModel"> + <i class="el-icon-tickets"></i>鐢熸垚Model + </span> + <span @click="createService"> + <i class="el-icon-document"></i>鐢熸垚涓氬姟绫� + </span> + <span @click="delTree"> + <i class="el-icon-delete"></i>鍒犻櫎鑿滃崟 + </span> + </div> + </VolHeader> + <div class="config"> + <vol-form + :label-width="90" + ref="form" + :formRules="layOutOptins.options" + :formFields="layOutOptins.fields" + ></vol-form> + </div> + </div> + <el-alert type="warning" :closable="false"> + 1銆佸鏋滈渶瑕佷慨鏀硅〃缁撴瀯锛岃鍦ㄦ暟鎹簱淇敼锛屽啀鐐瑰悓姝ヨ〃缁撴瀯->鐢熸垚vue椤甸潰->鐢熸垚model銆� + 2銆佷慨鏀圭紪杈戣鍚庨渶瑕佺偣鍑荤敓鎴恗odel銆佺敓鎴恦ue椤甸潰 + </el-alert> + <div class="coder-item"> + <VolHeader + icon="md-podium" + style="border-bottom: 0" + text="琛ㄧ粨鏋�" + > + <template #content> + <div style="color: red; font-size: 13px"> + 鏁版嵁搴撹〃缁撴瀯鍙戠敓鍙樺寲鏃惰鐐广�愬悓姝ヨ〃缁撴瀯銆� + </div></template + > + + <div class="action"> + <span + style="color: rgb(23, 156, 216)" + class="ivu-icon ivu-icon-ios-folder" + @click="help" + >浠g爜鐢熸垚鍣ㄥ弬鏁版枃妗�</span + > + <span @click="delRow" class="ivu-icon ivu-icon-md-close" + >鍒犻櫎琛屾暟鎹�</span + > + <span @click="syncTable" class="ivu-icon ivu-icon-md-sync" + >鍚屾琛ㄧ粨鏋�</span + > + </div> + </VolHeader> + + <div class="grid-container" style="padding-bottom: 20px"> + <vol-table + ref="table" + :paginationHide="true" + :tableData="data" + :height="tableHeight" + :columns="layOutOptins.columns" + :color="false" + :index="true" + :allowEmpty="true" + :clickEdit="true" + ></vol-table> + </div> + </div> + </div> + </el-scrollbar> + </div> + </div> + </div> +</template> +<script> +import builderData from './builderData'; +import VolForm from '@/components/basic/VolForm.vue'; +import VolTable from '@/components/basic/VolTable.vue'; +import VolBox from '@/components/basic/VolBox.vue'; +import VolHeader from '@/components/basic/VolHeader.vue'; +import VolMenu from '@/components/basic/VolElementMenu.vue'; +export default { + components: { + VolForm: VolForm, + VolTable: VolTable, + VolBox: VolBox, + VolHeader: VolHeader, + VolMenu + }, + data() { + return { + more: { + addChild: 'addChild', + ceateController: 'ceateController', + addRow: 'addRow', + delRow: 'delRow', + delTree: 'delTree' + }, + addModel: false, + helpModel: false, + tableHeight: 500, + addOptions: builderData.form.addOptions, + layOutOptins: { + fields: builderData.form.fields, + options: builderData.form.options, + columns: builderData.columns + }, + tableInfo: null, + data: [], + tree: [] + }; + }, + watch: { + 'layOutOptins.fields.vuePath'(val) { + localStorage.setItem('vuePath', val); + }, + deep: true + //localStorage.setItem("vuePath", this.layOutOptins.fields.vuePath || ""); + }, + methods: { + changeMore(funName) { + this[funName](); + }, + help() { + window.open('http://v2.volcore.xyz/document/coder'); + // this.helpModel = true; + }, + addVisible(pid) { + this.addModel = true; + this.$refs.form.reset(); + this.data.splice(0); + if (pid) { + this.layOutOptins.fields.parentId = pid; + } + }, + delTree() { + let tableId = this.layOutOptins.fields.table_Id; + if (!tableId) return this.$message.error('璇烽�夋嫨鑺傜偣'); + let tigger = false; + this.$confirm('鍒犻櫎璀﹀憡?', '纭瑕佸垹闄ゅ悧', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning', + center: true + }).then(() => { + if (tigger) return; + tigger = true; + this.http + .post('/api/builder/delTree?table_Id=' + tableId, {}, true) + .then((x) => { + if (!x.status) return this.$message.error(x.message); + this.$message.error('鍒犻櫎鎴愬姛,璇峰埛鏂伴〉闈�'); + // for (let index = 0; index < this.tree.length; index++) { + // if (this.tree[index].id == tableId) { + // this.tree.splice(index, 1); + // } + // } + }); + }); + }, + add() { + this.$refs.form.validate(() => { + // this.layOutOptins.fields.tableName = + // this.layOutOptins.fields.tableName.slice(0, 1).toUpperCase() + + // this.layOutOptins.fields.tableName.slice(1); + if (!this.layOutOptins.fields.tableTrueName) { + this.layOutOptins.fields.tableTrueName = this.layOutOptins.fields.tableName; + } + + let queryParam = + 'parentId=' + + this.layOutOptins.fields.parentId + + '&tableName=' + + this.layOutOptins.fields.tableName + + '&columnCNName=' + + this.layOutOptins.fields.columnCNName + + '&nameSpace=' + + this.layOutOptins.fields.namespace + + '&foldername=' + + this.layOutOptins.fields.folderName + + '&isTreeLoad=false'; + this.http + .post('/api/builder/LoadTableInfo?' + queryParam, {}, true) + .then((x) => { + if (!x.status) { + this.$message.error(x.message); + return; + } + let hasTree = this.tree.some((t) => { + return t.id == x.data.table_Id; + }); + if (!hasTree) { + this.tree.push({ + id: x.data.table_Id, + pId: x.data.parentId, + parentId: x.data.parentId, + name: x.data.columnCNName, + orderNo: x.data.orderNo + }); + } + if (!x.data.tableTrueName) { + x.data.tableTrueName = x.data.tableName; + } + this.addModel = false; + this.tableInfo = x.data; + this.$refs.form.reset(x.data); + this.data = x.data.tableColumns; + }); + }); + }, + addChild() { + // this.$message.info("寮�鍙戜腑"); + let id = this.layOutOptins.fields.table_Id; + if (!id) { + return this.$message.error('璇烽�変腑鑺傜偣'); + } + this.addVisible(id); + }, + addRow() { + this.data.push({}); + }, + delRow() { + let tigger = false; + this.$confirm('鍒犻櫎璀﹀憡?', '纭瑕佸垹闄ら�夋嫨鐨勬暟鎹悧', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning', + center: true + }).then(() => { + if (tigger) return; + tigger = true; + this.$refs.table.delRow(); + }); + }, + validateTableInfo(callback) { + this.$refs.form.validate(() => { + if (!this.tableInfo) { + this.$message.error('璇峰厛鍔犺浇鏁版嵁'); + return false; + } + if (this.data && this.data.length > 0) { + let keyInfo = this.data.find((x) => { + return x.isKey; + }); + if (!keyInfo) { + this.$message.error('璇峰嬀閫夎缃富閿�'); + return false; + } + if (keyInfo.isNull == 1) { + this.$message.error('涓婚敭銆愬彲涓虹┖銆戝繀椤昏缃负鍚�'); + return false; + } + if ( + keyInfo.columnType != 'int' && + keyInfo.columnType != 'bigint' && + !this.layOutOptins.fields.sortName + ) { + this.$message.error('涓婚敭闈炶嚜澧炵被鍨�,璇疯缃笂闈㈣〃鍗曠殑銆愭帓搴忓瓧娈点��'); + return false; + } + } + + for (const key in this.tableInfo) { + if (this.layOutOptins.fields.hasOwnProperty(key)) { + let newVal = this.layOutOptins.fields[key]; + this.tableInfo[key] = newVal; + } + } + callback(); + }); + }, + ceateVuePage(isApp) { + this.validateTableInfo(() => { + let vuePath; + if (!isApp) { + vuePath = localStorage.getItem('vuePath'); + if (!vuePath) { + return this.$message.error( + '璇峰厛璁剧疆Vue椤圭洰瀵瑰簲Views鐨勭粷瀵硅矾寰�,鐒跺悗鍐嶄繚瀛�!' + ); + } + } else { + vuePath = localStorage.getItem('appPath'); + if (!vuePath) { + return this.$message.error('璇峰厛璁剧疆app璺緞,鐒跺悗鍐嶄繚瀛�!'); + } + } + + let url = `/api/builder/createVuePage?vuePath=${vuePath}&v3=1&app=${isApp || + 0}`; + this.http.post(url, this.tableInfo, true).then((x) => { + this.$Message.info(x); + }); + }); + }, + createService() { + this.validateTableInfo(() => { + let queryParam = + 'tableName=' + + this.layOutOptins.fields.tableName + + '&nameSpace=' + + this.layOutOptins.fields.namespace + + '&foldername=' + + this.layOutOptins.fields.folderName; + this.http + .post( + '/api/builder/CreateServices?' + queryParam, + this.tableInfo, + true + ) + .then((x) => { + this.$Message.info(x); + }); + }); + }, + ceateModel() { + this.validateTableInfo(() => { + this.http + .post('/api/builder/CreateModel', this.tableInfo, true) + .then((x) => { + this.$message.info(x); + }); + }); + }, + syncTable() { + if (!this.layOutOptins.fields.tableName) + return this.$Message.error('璇烽�夋ā鍧�'); + this.http + .post( + '/api/builder/syncTable?tableName=' + + this.layOutOptins.fields.tableName, + {}, + true + ) + .then((x) => { + if (!x.status) { + return this.$Message.error(x.message); + } + this.$Message.info(x.message); + this.loadTableInfo(this.layOutOptins.fields.table_Id); + }); + }, + ceateApiController() {}, + ceateController() {}, + checkSortName() {}, + save() { + localStorage.setItem('vuePath', this.layOutOptins.fields.vuePath || ''); + localStorage.setItem('appPath', this.layOutOptins.fields.appPath || ''); + + if ( + this.tableInfo && + this.tableInfo.tableColumns && + this.tableInfo.tableColumns.length && + this.tableInfo.tableColumns.filter((x) => { + return x.isKey == 1; + }).length > 1 + ) { + return this.$Message.error('琛ㄧ粨鏋勫彧鑳藉嬀閫変竴涓富閿瓧娈�'); + } + this.validateTableInfo(() => { + this.http.post('/api/builder/Save', this.tableInfo, true).then((x) => { + if (!x.status) { + this.$Message.error(x.message); + return; + } + this.$Message.info(x.message); + this.tree.forEach((x) => { + if (x.id == this.layOutOptins.fields.table_Id) { + x.name = this.layOutOptins.fields.columnCNName; + x.parentId = this.layOutOptins.fields.parentId; + } + }); + this.tableInfo = x.data; + x.data.vuePath = this.layOutOptins.fields.vuePath; + x.data.appPath = this.layOutOptins.fields.appPath; + this.$refs.form.reset(x.data); + // this.layOutOptins.fields.vuePath = localStorage.getItem("vuePath"); + this.data = x.data.tableColumns; + // this.$Message.info(x); + }); + }); + }, + onSelect(node) { + this.loadTableInfo(node); + }, + onOpenChange(node) { + if (node.length == 0) return; + this.loadTableInfo(node.length == 1 ? node[0] : node[node.length - 1]); + }, + loadTableInfo(id) { + // localStorage.setItem("vuePath", this.layOutOptins.fields.vuePath || ""); + this.http + .post( + '/api/builder/LoadTableInfo?table_Id=' + id + '&isTreeLoad=true', + {}, + true + ) + .then((x) => { + if (!x.data.tableTrueName) { + x.data.tableTrueName = x.data.tableName; + } + //2021.01.09澧炲姞浠g爜鐢熸垚鍣ㄨ缃畉able鎺掑簭鍔熻兘 + const _fields = [ + 'sortable', + 'isNull', + 'isReadDataset', + 'isColumnData', + 'isDisplay' + ]; + x.data.tableColumns.forEach((item) => { + for (let index = 0; index < _fields.length; index++) { + item[_fields[index]] = item[_fields[index]] || 0; + } + }); + this.tableInfo = x.data; + + this.$refs.form.reset(x.data); + this.data = x.data.tableColumns; + }); + }, + getVuePath(key) { + let vuePath = localStorage.getItem(key); + if (!vuePath || vuePath == 'null' || vuePath == 'undefined') { + vuePath = ''; + } + return vuePath; + } + }, + mounted() {}, + created() { + let clientHeight = document.documentElement.clientHeight - 170; + this.tableHeight = clientHeight < 400 ? 400 : clientHeight; + this.http + .post('/api/Sys_Dictionary/GetBuilderDictionary', {}, true) + .then((dic) => { + let column = this.layOutOptins.columns.find((x) => { + return x.field == 'dropNo'; + }); + if (!column) return; + + let data = [{ key: '', value: '' }]; + for (let index = 0; index < dic.length; index++) { + data.push({ key: dic[index], value: dic[index] }); + } + + column.bind.data = data; + }); + + builderData.form.fields.vuePath = this.getVuePath('vuePath'); + builderData.form.fields.appPath = this.getVuePath('appPath'); + this.http.post('/api/builder/GetTableTree', {}, false).then((x) => { + this.tree = JSON.parse(x.list); + if (!x.nameSpace) { + return this.$message.error( + '鏈幏鍙栧悗鍙伴」鐩被搴撴墍鍦ㄥ懡鍚嶇┖闂�,璇风‘璁ょ洰褰曟垨璋冭瘯Sys_TableInfoService绫籊etTableTree鏂规硶' + ); + } + let nameSpace = JSON.parse(x.nameSpace); + let nameSpaceArr = []; + for (let index = 0; index < nameSpace.length; index++) { + nameSpaceArr.push({ + key: nameSpace[index], + value: nameSpace[index] + }); + } + + //鍒濆鍖栭」鐩懡浠ょ┖闂� + this.layOutOptins.options.forEach((option) => { + option.forEach((item) => { + if (item.field == 'namespace') { + item.data.push(...nameSpaceArr); + } + }); + }); + this.addOptions.forEach((option) => { + option.forEach((item) => { + if (item.field == 'namespace') { + item.data.push(...nameSpaceArr); + } + }); + }); + }); + } +}; +</script> +<style scoped> +.builder-tree { + position: absolute; + top: 41px; + bottom: 0; +} +.builder-tree >>> .ivu-menu { + text-align: left; + width: 200px !important; +} +.builder-container { + widows: 100%; + /* padding: 20px; */ + position: absolute; + top: 0px; + left: 0; + right: 0; + display: inline-block; + bottom: 0; +} +.grid-container >>> tr:hover { + cursor: pointer; +} +.builder-left { + position: relative; + width: 201px; + height: 100%; + border-right: 2px solid #dcd6d6; +} +.builder-content { + position: absolute; + top: 0px; + left: 200px; + display: inline-block; + bottom: 0; + right: 0px; +} +.builder-content .ivu-alert { + position: relative; + display: flex; + padding: 12px 18px 12px 38px; +} +.builder-content .ivu-alert-icon { + top: 10px; +} +.builder-content .action { + text-align: right; + line-height: 33px; + padding-right: 26px; +} +.builder-content .action i { + top: 0px; + position: relative; +} +.builder-content .action > span { + padding: 0px 6px; + font-size: 12px; + letter-spacing: 1px; + color: #5a5f5e; +} +.builder-content .action > span:hover { + cursor: pointer; + color: black; +} +.builder-content .config { + /* border: 1px solid #e9e8e8; */ + padding: 15px 15px 0px 15px; + border-radius: 3px; + background: #ffffff; + margin-bottom: 10px; +} +.builder-container .config >>> .ivu-form > .ivu-form-item { + display: none; +} +.coder-container { + background: #eee; +} +.coder-container .coder-item { + background: white; + padding: 0px 15px; +} +.module-name { + color: #2d8cf0; + font-size: 13px; + line-height: 39px; + padding-left: 15px; + border: 1px solid #abdcff; + background-color: #f0faff; +} +.module-name >>> .ivu-alert-icon { + top: 12px; +} +.help { + margin-left: 15px; + top: 2px; + position: relative; + border-bottom: 1px solid; +} +.help:hover { + color: #f56c6c; + cursor: pointer; +} +.more { + text-align: left; + position: relative; + top: 2px; +} +.addModel { + padding: 10px; +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue" new file mode 100644 index 0000000..e75b6f9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue" @@ -0,0 +1,258 @@ + +<template> + <div id="big-data-container" class="big-data-container"> + <div class="head"> + <h1>澶у睆鏁版嵁缁熻鍒嗘瀽鏄剧ず</h1> + </div> + <div class="data-container"> + <div class="data-left"> + <div class="data-left-item"> + <div class="title">鍟嗗搧閿�閲忓垎绫�</div> + <div id="chart-vleft-1" style="height: calc(100% - 30px)"></div> + <div class="data-foot-line"></div> + </div> + <div class="data-left-item"> + <div class="title">鏈湀鍟嗗搧閿�閲�</div> + <div id="chart-vleft-3" style="height: calc(100% - 30px)"></div> + + <div class="data-foot-line"></div> + </div> + <div class="data-left-item"> + <div class="title">7鏃ヨ鍗曢攢閲�</div> + <div id="chart-vleft-2" style="height: calc(100% - 30px)"></div> + <div class="data-foot-line"></div> + </div> + </div> + <div class="data-center"> + <!-- <div class="title">涓棿浣嶇疆</div> --> + <div class="center-top-num"> + <div class="item"> + <div class="text">绱閿�閲�</div> + <div class="num">220,000</div> + </div> + <div class="item"> + <div class="text">绱閿�鍞噾棰�</div> + <div class="num">58,000,000</div> + </div> + <div class="item"> + <div class="text">璐拱鐢ㄦ埛浜烘暟</div> + <div class="num">15,000</div> + </div> + <div class="data-foot-line"></div> + </div> + <div + class="center-top" + style="height: 260px; padding-top: 25px; overflow: hidden" + > + <!-- <div class="title">鐢ㄦ埛娲昏穬淇℃伅-1</div> --> + <div id="chart-vgauge-1" style="height: 400px"></div> + <!-- <iview-circle :size="200" style="padding: 8px 0;"></iview-circle> --> + <div class="data-foot-line"></div> + </div> + <div class="title">璁㈠崟閿�鍞粺璁�</div> + <div id="chart-vcenter" style="height:400px;" class="chart-vcenter"></div> + </div> + <div class="data-right"> + <div class="data-right-item"> + <div class="title">閿�鍞儏鍐佃蛋鍔�</div> + <div id="chart-vright-1" style="height: calc(100% - 30px)"></div> + <div class="data-foot-line"></div> + </div> + <div class="data-right-item" style="height: 220px; padding-top: 25px"> + <!-- <div class="title">鐢ㄦ埛娲昏穬淇℃伅</div> --> + <!-- <iview-circle></iview-circle> --> + <div id="chart-vgauge-2" style="height: 300px"></div> + <div class="data-foot-line"></div> + </div> + <div class="data-right-item right-3"> + <div class="title">鍟嗗搧閿�鍞帓琛�</div> + <div id="chart-vright-3" class="right-item"> + <div class="item"> + <div class="top">鎺掑悕</div> + <div class="pro-name">鍟嗗搧鍚嶇О</div> + <div class="num">閿�閲�</div> + <div class="num">閿�鍞噾棰�</div> + </div> + <div class="item"> + <div class="top top-1"> + <span>1</span> + </div> + <div class="pro-name">鍗″笣涔愰硠楸�</div> + <div class="num">2,200</div> + <div class="num">360,00</div> + </div> + <div class="item"> + <div class="top top-2"> + <span>2</span> + </div> + <div class="pro-name">鏄ュ鐢稵鎭�</div> + <div class="num">1,700</div> + <div class="num">24,500</div> + </div> + <div class="item"> + <div class="top top-3"> + <span>3</span> + </div> + <div class="pro-name">鐢峰コ鍚屾浼戦棽闉�</div> + <div class="num">1,120</div> + <div class="num">12,700</div> + </div> + </div> + <div class="boxfoot"></div> + </div> + </div> + </div> + </div> +</template> +<script> +var echarts = require("echarts"); +let $chartLeft1, + $chartLeft2, + $chartLeft3, + $chartCenter, + $chartRight1, + $chartGauge1, + $chartGauge2; +import { + chartLeft1, + chartLeft2, + chartLeft3, + chartRight1, + gauge, +} from "./bigdata/chart-options"; +// import IviewCircle from "./bigdata/IviewCircle"; +import "./bigdata/layout.less"; +export default { + components: { + // "iview-circle": IviewCircle + }, + data() { + return {}; + }, + created() { + console.log("chart"); + }, + mounted() { + if ($chartLeft1) { + $chartLeft1.dispose(); + $chartLeft2.dispose(); + $chartLeft3.dispose(); + $chartCenter.dispose(); + $chartRight1.dispose(); + $chartGauge1.dispose(); + $chartGauge2.dispose(); + } + $chartLeft1 = echarts.init(document.getElementById("chart-vleft-1")); + $chartLeft1.setOption(chartLeft1, true); + + $chartLeft2 = echarts.init(document.getElementById("chart-vleft-2")); + $chartLeft2.setOption(chartLeft2, true); + + $chartLeft3 = echarts.init(document.getElementById("chart-vleft-3")); + $chartLeft3.setOption(chartLeft3, true); + + $chartCenter = echarts.init(document.getElementById("chart-vcenter")); + $chartCenter.setOption(chartRight1, true); + + $chartRight1 = echarts.init(document.getElementById("chart-vright-1")); + $chartRight1.setOption(chartRight1, true); + + $chartGauge1 = echarts.init(document.getElementById("chart-vgauge-1")); + $chartGauge1.setOption(gauge, true); + + $chartGauge2 = echarts.init(document.getElementById("chart-vgauge-2")); + $chartGauge2.setOption(gauge); + }, + destroyed() { + $chartLeft1 = null; + $chartLeft2 = null; + $chartLeft3 = null; + $chartCenter = null; + $chartRight1 = null; + $chartGauge1 = null; + $chartGauge2 = null; + }, +}; +</script> +<style scoped> +/* .chart-center { + display: flex; + border: 1px solid #0000ff; + height: 200px; + flex-direction: column; + margin-top: 20px; +} +.chart-center .item { + text-align: center; + border: 1px solid #00c1b3; + flex: 1; +} */ +.right-3 { + display: flex; + flex-direction: column; + /* margin-top: 20px; */ +} + +.right-3 .right-item { + flex: 1; + display: flex; + flex-direction: column; +} + +.right-3 .item { + text-align: left; + border-bottom: 1px solid #549069; + flex: 1; + display: flex; + padding: 5px 10px; + margin: 0 10px; + font-size: 14px; + line-height: 30px; +} + +.right-3 .item:last-child { + border-bottom: 0; +} + +.right-3 .item > div { + color: white; +} + +.right-3 .top { + width: 60px; + position: relative; +} + +.right-3 .top span { + position: absolute; + width: 20px; + line-height: 20px; + top: 5px; + text-align: center; + border-radius: 5px; +} + +.right-3 .top-1 span { + background: #e80d0d; +} + +.right-3 .top-2 span { + background: #00c935; +} + +.right-3 .top-3 span { + background: #0083f4; +} + +.right-3 .num { + width: 88px; +} + +.right-3 .pro-name { + flex: 1; +} +</style> + + + + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue" new file mode 100644 index 0000000..c166d91 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue" @@ -0,0 +1,102 @@ +<template> + <div class="demo-Circle"> + <div style> + <i-circle + :size="size" + :trail-width="4" + :stroke-width="5" + :percent="75" + stroke-linecap="square" + stroke-color="#43a3fb" + > + <div class="demo-Circle-custom"> + <h1>1500</h1> + <p>鏄ㄦ棩娲昏穬鐢ㄦ埛鏁伴噺</p> + <span> + 鍗犳瘮 + <i>{{1500/20000}}%</i> + </span> + </div> + </i-circle> + </div> + <div style> + <i-circle + :size="size" + :trail-width="4" + :stroke-width="5" + :percent="75" + stroke-linecap="square" + stroke-color="#43a3fb" + > + <div class="demo-Circle-custom"> + <h1>12000</h1> + <p>涓婃湀娲昏穬鐢ㄦ埛鏁伴噺</p> + <span> + 鍗犳瘮 + <i>{{12000/150000}}%</i> + </span> + </div> + </i-circle> + </div> + </div> +</template> +<script> +export default { + props:{ + size:{ + type:Number, + default:150 + } + } +} +</script> +<style scoped> +.demo-Circle { + display: flex; +} +.demo-Circle > div { + flex: 1; + text-align: center; +} +.demo-Circle > div:first-child{ + padding-left:10%; +} + +.demo-Circle > div:last-child{ + padding-right:10%; +} +</style> +<style lang="less" scoped> +.demo-Circle-custom { + & h1 { + color:#ffffff; + font-size: 28px; + font-weight: normal; + } + & p { + color: #ece8e8; + font-size: 14px; + margin: 10px 0 15px; + } + & span { + display: block; + padding-top: 15px; + color: wheat; + font-size: 14px; + &:before { + content: ""; + display: block; + width: 50px; + height: 1px; + margin: 0 auto; + background: #e0e3e6; + position: relative; + top: -15px; + } + } + & span i { + font-style: normal; + color: white; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js" new file mode 100644 index 0000000..fe30d3e --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js" @@ -0,0 +1,551 @@ +var echarts = require("echarts"); +let chartLeft1 = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow" + } + }, + grid: { + left: "0%", + top: "10px", + right: "0%", + bottom: "4%", + containLabel: true + }, + xAxis: [ + { + type: "category", + data: [ + "鍟嗚秴闂ㄥ簵", + "鏁欒偛鍩硅", + "鎴垮湴浜�", + "鐢熸椿鏈嶅姟", + "姹借溅閿�鍞�", + "鏃呮父閰掑簵", + "浜旈噾寤烘潗" + ], + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1)", + width: 1, + type: "solid" + } + }, + + axisTick: { + show: false + }, + axisLabel: { + interval: 0, + show: true, + splitNumber: 15, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + } + } + ], + yAxis: [ + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], + series: [ + { + type: "bar", + data: [200, 600, 300, 900, 1500, 1200, 600], + barWidth: "35%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + } + } + ] +}; + + +let chartLeft2 = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow" + } + }, + grid: { + left: "0%", + top: "10px", + right: "0%", + bottom: "4%", + containLabel: true + }, + xAxis: [ + { + type: "category", + data: [ + "07.01", + "07.02", + "07.03", + "07.04", + "07.05", + "07.06", + ], + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1)", + width: 1, + type: "solid" + } + }, + axisTick: { + show: false + }, + axisLabel: { + interval: 0, + // rotate:50, + show: true, + splitNumber: 15, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + } + } + ], + yAxis: [ + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + }, + { + type: "value", + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], series: [ + { + type: "bar", + name: "閿�閲�", + data: [1200, 800, 300, 500, 560, 220], + barWidth: "25%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + } + }, { + type: "bar", + name: "璁㈠崟", + data: [1000, 750, 380, 450, 450, 120], + barWidth: "25%", + itemStyle: { + normal: { + color: "#46d000", + opacity: 1, + barBorderRadius: 5 + } + } + } + ] +}; + +let chartLeft3 = { + tooltip: { + trigger: 'axis', + axisPointer: { // 鍧愭爣杞存寚绀哄櫒锛屽潗鏍囪酱瑙﹀彂鏈夋晥 + type: 'shadow' // 榛樿涓虹洿绾匡紝鍙�変负锛�'line' | 'shadow' + } + }, + grid: { + left: "0%", + top: "-5px", + right: "3%", + bottom: "4%", + containLabel: true + }, + xAxis: { + type: 'value', + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + }, + yAxis: { + type: 'category', + axisLabel: { + show: true, + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: "12" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: "rgba(255,255,255,.1 )", + width: 1, + type: "solid" + } + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + }, + data: ['鍛ㄤ竴', '鍛ㄤ簩', '鍛ㄤ笁', '鍛ㄥ洓', '鍛ㄤ簲'] + }, + series: [ + { + name: '鐩存帴璁块棶', + type: 'bar', + stack: '鎬婚噺', + label: { + show: true, + position: 'insideRight' + }, + barWidth: "55%", + itemStyle: { + normal: { + color: "#2f89cf", + opacity: 1, + barBorderRadius: 5 + } + }, + data: [120, 302, 400, 200, 700] + } + ] +}; + +let chartRight1 = { + title: {}, + tooltip: { + trigger: "axis", + axisPointer: { + type: "cross", + label: { + backgroundColor: "#6a7985" + } + } + }, + + color: ["#ffab6f", "#09b916", "#83cddc"], //鍥句緥棰滆壊 + legend: { + top: "0%", + icon: "roundRect", + data: ["閿�鍞鍗�", "閫�璐ц鍗�", "鎶樻墸璁㈠崟"], + textStyle: { + color: "rgba(255,255,255,.5)", + fontSize: "12" + } + }, + toolbox: { + feature: {} + }, + grid: { + left: "10", + top: "20", + right: "10", + bottom: "10", + containLabel: true + }, + xAxis: [ + { + type: "category", + boundaryGap: false, + axisLabel: { + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: 12 + } + }, + axisLine: { + lineStyle: { + color: "rgba(255,255,255,.2)" + } + }, + data: [ + "2020.06.15", + "2020.06.16", + "2020.06.17", + "2020.06.18", + "2020.06.19", + "2020.06.20", + "2020.06.21", + "2020.06.22" + ] + } + ], + yAxis: [ + { + type: "value", + axisTick: { show: false }, + minInterval: 60, + axisLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + }, + axisLabel: { + textStyle: { + color: "rgba(255,255,255,.6)", + fontSize: 12 + } + }, + + splitLine: { + lineStyle: { + color: "rgba(255,255,255,.1)" + } + } + } + ], + series: [ + { + name: "閿�鍞鍗�", + type: "line", + smooth: true, + lineStyle: { + color: "#45d4ba", + width: 1 + }, //绾挎潯鐨勬牱寮� + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#83cddc" + }, + { + offset: 1, + color: "#bfdffbb5" + } + ]) + }, + data: [5, 22, 150, 54, 1, 230, 4, 1] + }, + { + name: "閫�璐ц鍗�", + type: "line", + + smooth: true, + lineStyle: { + color: "#04a710", + width: 1 + }, // + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#0cbf22" + }, + { + offset: 1, + color: "#b8f7d1b5" + } + ]) + }, + data: [10, 150, 1, 250, 20, 100, 10, 150] + }, + { + name: "鎶樻墸璁㈠崟", + type: "line", + + lineStyle: { + color: "#0864c3", + width: 1 + }, //绾挎潯鐨勬牱寮� + smooth: true, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: "#29d7ff" + }, + { + offset: 1, + color: "#34ccef85" + } + ]) + }, + data: [100, 2, 260, 1, 200, 30, 101, 40] + } + ] +}; + +var gauge = { + series: [{ + radius: '90%', + type: 'gauge', + startAngle: 180, + endAngle: 0, + min: 0, + max: 1, + splitNumber: 8, + axisLine: { + lineStyle: { + width: 3, + color: [ + [0.25, '#FF6E76'], + [0.5, '#FDDD60'], + [0.75, '#58D9F9'], + [1, '#7CFFB2'] + ] + } + }, + pointer: { + icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z', + length: '12%', + width: 20, + offsetCenter: [0, '-60%'], + itemStyle: { + color: 'auto' + } + }, + axisTick: { + length: 12, + lineStyle: { + color: 'auto', + width: 2 + } + }, + splitLine: { + length: 20, + lineStyle: { + color: 'auto', + width: 5 + } + }, + axisLabel: { + color: '#464646', + fontSize: 20, + distance: -60, + formatter: function (value) { + if (value === 0.875) { + return '浼�'; + } + else if (value === 0.625) { + return '涓�'; + } + else if (value === 0.375) { + return '鑹�'; + } + else if (value === 0.125) { + return '宸�'; + } + } + }, + title: { + offsetCenter: [0, '-20%'], + fontSize: 20 + }, + detail: { + fontSize: 30, + offsetCenter: [0, '0%'], + valueAnimation: true, + formatter: function (value) { + return Math.round(value * 100) + '鍒�'; + }, + color: 'auto' + }, + data: [{ + value: 0.70, + name: '鎴愮哗璇勫畾' + }] + }] +} +export { chartLeft1, chartLeft2, chartLeft3, chartRight1, gauge } diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png" new file mode 100644 index 0000000..a2e45f6 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png" Binary files differ diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less" new file mode 100644 index 0000000..3f6cffd --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less" @@ -0,0 +1,197 @@ + +.big-data-container { + position: absolute; + overflow: hidden; + height: 100%; + width: 100%; + background-color: #1400a8; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 1200 800'%3E%3Cdefs%3E%3CradialGradient id='a' cx='0' cy='800' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%230e0077'/%3E%3Cstop offset='1' stop-color='%230e0077' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='b' cx='1200' cy='800' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%2314057c'/%3E%3Cstop offset='1' stop-color='%2314057c' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='c' cx='600' cy='0' r='600' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%230d0524'/%3E%3Cstop offset='1' stop-color='%230d0524' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='d' cx='600' cy='800' r='600' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%231400a8'/%3E%3Cstop offset='1' stop-color='%231400a8' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='e' cx='0' cy='0' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23000000'/%3E%3Cstop offset='1' stop-color='%23000000' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='f' cx='1200' cy='0' r='800' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23130733'/%3E%3Cstop offset='1' stop-color='%23130733' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect fill='url(%23a)' width='1200' height='800'/%3E%3Crect fill='url(%23b)' width='1200' height='800'/%3E%3Crect fill='url(%23c)' width='1200' height='800'/%3E%3Crect fill='url(%23d)' width='1200' height='800'/%3E%3Crect fill='url(%23e)' width='1200' height='800'/%3E%3Crect fill='url(%23f)' width='1200' height='800'/%3E%3C/svg%3E"); + background-attachment: fixed; + background-size: cover; + .head { + height: 75px; + /* height: 1.05rem; */ + background: url(./head_bg.png) no-repeat center center; + background-size: 100% 100%; + position: relative; + z-index: 100; + } +} + +.head h1 { + margin: 0; + color: #fff; + text-align: center; + /* font-size: .4rem; */ + /* line-height: .8rem; */ + line-height: 71px; +} + +.data-container { + /* margin: 5px 15px; + height:100%; */ + + margin: 0px 15px; + position: absolute; + left: 0; + right: 0; + top: 76px; + bottom: 0; +} + +.data-container > div { + float: left; + /* border: 1px solid white; */ + height: 100%; +} + +.data-center { + padding: 0 0.9rem; + width: 40%; + display: flex; + flex-direction: column; + // .center-top{ + // height: 210px; + // background: red; + // } +.chart-center{ + flex: 1; +} +} +.chart-center{ + width: 100%; +display: flex; +// background: white; +} +.data-left, +.data-right { + width: 30%; + display: flex; + + flex-direction: column; +} + +.data-left-item, +.data-right-item,.center-top,.center-top-num,.chart-center { + border: 1px solid rgba(25, 186, 139, 0.17); + padding: 0 0.2rem 0.4rem 0.15rem; + background: rgba(255, 255, 255, 0.04); + background-size: 100% auto; + position: relative; + margin-bottom: 0.15rem; + z-index: 10; +} + +.data-foot-line { + position: absolute; + bottom: 0; + width: 100%; + left: 0; +} + +.data-foot-line:before, +.data-foot-line:after { + position: absolute; + width: 10px; + height:10px; + content: ""; + border-bottom: 2px solid #02a6b5; + bottom: 0; +} + +.boxall:before, +.data-foot-line:before { + border-left: 2px solid #02a6b5; + left: 0; +} + +.boxall:after, +.data-foot-line:after { + border-right: 2px solid #02a6b5; + right: 0; +} + +.boxall:before, +.boxall:after { + position: absolute; + width: 10px; + height: 10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left-item:before, +.data-right-item:before, +.center-top-num:before, +.center-top:before{ + border-left: 2px solid #02a6b5; + left: 0; + position: absolute; + width: 10px; + height:10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left-item:after, +.data-right-item:after, +.center-top-num:after, +.center-top:after { + border-right: 2px solid #02a6b5; + right: 0; + position: absolute; + width: 10px; + height: 10px; + content: ""; + border-top: 2px solid #02a6b5; + top: 0; +} + +.data-left, +.data-right { + /* display: flex; */ +} + +.data-left > .data-left-item, +.data-right > .data-right-item { + flex: 1; + margin-bottom: 0.9rem; +} + +.data-center .title, +.data-left > .data-left-item .title, +.data-right > .data-right-item .title { + /* font-size: .2rem; */ + font-size: 1rem; + padding: 7px 0; + color: #fff; + text-align: center; + /* line-height: .5rem; */ +} + +.data-center .chart-center{ + width: 100%; +} + +.center-top-num{ + height: 80px; + padding-top: 7px; + margin-bottom: 0.8rem; + display: flex; + .item{ + flex: 1; + text-align: center; + } + .text{ + color: #fcf0d8; + font-size: 14px; + } + .num{ + font-size: 34px; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + font-weight: bold; + color: #67caca; + } +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue" new file mode 100644 index 0000000..3b0695a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue" @@ -0,0 +1,101 @@ +<template> + <div class="m-charts"> + <el-tabs + v-model="name" + @tab-click="tabClick" + type="border-card" + style="height: 100%; width: 100%; box-shadow: none" + > + <el-tab-pane name="bar"> + <template #label> + <span><i class="el-icon-date"></i> 鏌辩姸鍥� </span> + </template> + <div + :style="{ height: heigth + 'px', width: width + 'px' }" + id="bar-0001" + ></div> + </el-tab-pane> + <el-tab-pane name="pie" :lazy="false" label="娑堟伅涓績"> + <template #label> + <span><i class="el-icon-date"></i> 楗肩姸鍥� </span> + </template> + <div + :style="{ height: heigth + 'px', width: width + 'px' }" + id="pie-0001" + ></div> + </el-tab-pane> + <el-tab-pane name="line" :lazy="false" label="瑙掕壊绠$悊"> + <template #label> + <span><i class="el-icon-date"></i> 鎶樼嚎鍥� </span> + </template> + <div + :style="{ height: heigth + 'px', width: width + 'px' }" + id="line-0001" + ></div> + </el-tab-pane> + </el-tabs> + </div> +</template> +<script> +let echarts = require("echarts"); +import options from "./chartOptions"; +let $bar; +let $pie; +let $line; +export default { + mounted() { + $bar = echarts.init(document.getElementById("bar-0001")); + $bar.setOption(this.options.bar); + }, + created() { + this.heigth = document.documentElement.clientHeight - 190; + this.width = document.documentElement.clientWidth - 240; + }, + methods: { + tabClick(name) { + if (name.props.name == "pie") { + if (!$pie) { + $pie = echarts.init(document.getElementById("pie-0001")); + $pie.setOption(this.options.pie); + + } + } else if (name.props.name == "line") { + if (!$line) { + $line = echarts.init(document.getElementById("line-0001")); + $line.setOption(this.options.line); + + } + } + }, + }, + data() { + return { + name: "bar", + heigth: 450, + width: 1000, + options: options, + }; + }, +}; +</script> +<style lang="less" scoped> +.m-charts { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #f1f1f1; + margin: auto 0; + padding: 12px; + .m-tabs { + background: white; + } +} +.m-charts ::v-deep(.el-tabs__content) { + height: calc(100% - 45px); +} +.m-charts ::v-deep(.el-tab-pane) { + height: 100%; +} +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js" new file mode 100644 index 0000000..2ec6bda --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js" @@ -0,0 +1,212 @@ +let options= { + bar:{ + // title: { + // text: 'World Population' + // }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + // legend: {}, + grid: { + top:10, + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + xAxis: { + type: 'value', + boundaryGap: [0, 0.01] + }, + yAxis: { + type: 'category', + data: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World'] + }, + series: [ + { + name: '2011', + type: 'bar', + data: [18203, 23489, 29034, 14970, 31744, 60230] + }, + { + name: '2012', + type: 'bar', + data: [19325, 23438, 31000, 11594, 24141, 6807] + } + ] + }, + pie: { + tooltip: { + trigger: "item", + formatter: "{a} <br/>{b} : {c} ({d}%)" + }, + legend: { + top: 20, + // orient: "vertical", + // right: 300, + // top: 200, + // bottom: 20, + data: [ + "鍥句緥1", + "鍥句緥2", + "鍥句緥3", + "鍥句緥4", + "鍥句緥5", + "鍥句緥6", + "鍥句緥7" + ] + }, + series: [ + { + name: "鍥句緥1", + type: "pie", + radius: ['40%', '70%'], + selectedMode: "single", + itemStyle: { + borderRadius: 6, + borderColor: '#fff', + borderWidth: 2 + }, + data: [ + { + value: 2563, + name: "鍥句緥1", + itemStyle: { + color: "rgb(45, 140, 240)" + } + }, + { + value: 727, + name: "鍥句緥2", + itemStyle: { + color: "rgb(92, 173, 255)" + } + }, + { + value: 2182, + name: "鍥句緥3", + itemStyle: { + color: "rgb(25, 190, 107)" + } + }, + { + value: 1419, + name: "鍥句緥4", + itemStyle: { + color: "#00e5ff" + } + }, + { + value: 984, + name: "鍥句緥5", + itemStyle: { + color: "#ff80ab" + } + }, + { + value: 870, + name: "鍥句緥6", + itemStyle: { + color: "rgb(237, 64, 20)" + } + }, + { + value: 1670, + name: "鍥句緥7", + itemStyle: { + color: "#ffb445" + } + } + ] + } + ] + }, + line: { + legend: { + data: ["閭欢钀ラ攢", "鑱旂洘骞垮憡"] + }, + grid: { + left: "3%", + right: "4%", + bottom: "3%", + containLabel: true + }, + toolbox: { + feature: { + saveAsImage: {} + } + }, + xAxis: { + type: "category", + boundaryGap: false, + data: [ + "1鏈�", + "2鏈�", + "3鏈�", + "4鏈�", + "5鏈�", + "6鏈�", + "7鏈�", + "8鏈�", + "9鏈�", + "10鏈�", + "11鏈�", + "12鏈�" + ] + }, + yAxis: { + type: "value" + }, + series: [ + { + name: "閭欢钀ラ攢", + type: "line", + stack: "鎬婚噺", + itemStyle: { + color: "rgb(25, 190, 107)" + }, + smooth: true, + data: [ + 7.0, + 6.9, + 9.5, + 12.5, + 18.2, + 21.5, + 22.5, + 23.3, + 18.3, + 13.9, + 9.6 + ] + }, + { + name: "鑱旂洘骞垮憡", + type: "line", + stack: "鎬婚噺", + smooth: true, + itemStyle: { + color: "rgb(92, 173, 255)" + }, + data: [ + 7.0, + 6.9, + 9.5, + 14.5, + 18.2, + 21.5, + 22.5, + 21.3, + 18.3, + 13.9, + 9.6 + ] + } + ] + } + } + + export default options; \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue" new file mode 100644 index 0000000..e94823a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue" @@ -0,0 +1,386 @@ +<template> + <div class="home-contianer"> + <div> + <div class="order-title"> + <h2>璁㈠崟缁熻</h2> + </div> + <div + data-v-542f4644 + class="ivu-row" + style="padding: 15px; background: white" + > + <div + v-for="item in topColor" + :key="item.name" + class="ivu-col ivu-col-span-6" + style="padding-left: 8px; padding-right: 8px" + > + <div + data-v-542f4644 + class="ivu-card" + :style="{ background: item.background }" + > + <div class="icon-left"> + <i :class="item.icon" /> + </div> + <div class="ivu-card-body"> + <div class="demo-color-name">{{ item.name }}</div> + <div class="demo-color-desc">#{{ item.qty }}</div> + </div> + </div> + </div> + </div> + + <div class="numbers"> + + <div class="item" v-for="index in 8" :key="index"> + <div class="number"> + <!-- {{value}} --> + {{index * 1000}} + </div> + <div>Order total</div> + </div> + </div> + + <div class="order-title"> + <h2>璁㈠崟鏌ヨ</h2> + </div> + + <div class="order-range"> + <div + class="order-item" + v-for="(item, index) in totalRange" + :key="index" + > + <div class="total"> + <div class="number"> + {{item.qty}} + </div> + </div> + <div class="name">{{ titleLeft + item.name }}</div> + <div class="date"> + {{ beginDate.replace(/-/g, ".") }} -- + {{ endDate.replace(/-/g, ".") }} + </div> + </div> + </div> + </div> + </div> +</template> +<script> + + +export default { + data() { + return { + beginDate: "", + endDate: "", + n: 90, + topColor: [ + { + name: "璁㈠崟鏁�", + desc: "#205", + background: "rgb(25, 190, 107)", + icon: "el-icon-shopping-cart-full", + qty: 6000, + key: "total", + }, + { + name: "宸蹭粯娆�", + desc: "#412", + background: "rgb(45, 183, 245)", + icon: "el-icon-wallet", + qty: 7100, + key: "total", + }, + { + name: "寰呭彂璐�", + desc: "#412", + background: "#f2b458", + icon: "el-icon-shopping-cart-1", + qty: 500, + key: "hasPay", + }, + { + name: "閰嶉�佷腑", + desc: "#412", + background: "rgb(84, 110, 122)", + icon: "el-icon-shopping-cart-2", + qty: 800, + key: "notShip", + }, + { + name: "宸插畬鎴�", + desc: "#412", + background: "rgb(45, 183, 245)", + icon: "el-icon-set-up", + qty: 1880, + key: "completed", + }, + { + name: "閫�璐ц鍗�", + desc: "#12", + background: "rgb(237, 64, 20)", + icon: "el-icon-data-analysis", + qty: 2290, + key: "refund", + }, + ], + totalRange: [ + { + name: "璁㈠崟鏁�", + desc: "#205", + background: "rgb(25, 190, 107)", + icon: "ios-cart", + qty: 1290, + key: "total", + }, + { + name: "宸蹭粯娆�", + desc: "#412", + background: "rgb(45, 183, 245)", + icon: "ios-cash", + qty: 3450, + key: "total", + }, + { + name: "寰呭彂璐�", + desc: "#412", + background: "rgb(255, 153, 0)", + icon: "md-bus", + qty: 200, + key: "hasPay", + }, + { + name: "閰嶉�佷腑", + desc: "#412", + background: " rgb(84, 110, 122)", + icon: "md-pin", + qty: 7000, + key: "notShip", + }, + { + name: "浜ゆ槗瀹屾垚", + desc: "#412", + background: "rgb(45, 183, 245)", + icon: "ios-help-buoy", + qty: 8900, + key: "completed", + }, + { + name: "閫�璐ц鍗�", + desc: "#12", + background: "rgb(237, 64, 20)", + icon: "ios-navigate", + qty: 2450, + key: "refund", + }, + ], + value1: "1", + titleLeft: "", + dateNow: "", + }; + }, + methods: { + getDate() { + var date = new Date(); + var year = date.getFullYear(); + var month = date.getMonth() + 1; + var day = date.getDate(); + var hour = date.getHours(); + var minutes = date.getMinutes(); + var second = date.getSeconds(); + this.beginDate = + year + + "-" + + (month < 10 ? "0" + month : month) + + "-" + + (day < 10 ? "0" + day : day); + this.endDate = this.beginDate; + this.dateNow = this.beginDate; + }, + search() { + if (this.dateNow == this.beginDate && this.dateNow == this.endDate) { + this.titleLeft = "浠婃棩"; + } else { + this.titleLeft = "褰撴湡"; + } + }, + }, + created() { + this.getDate(); + }, + mounted() {}, +}; +</script> +<style scoped> +.home-contianer { + background: #efefef; + width: 100%; + height: 100%; + /* padding: 20px; */ +} + +.ivu-card-body { + text-align: center; + padding: 20px 5px; + /* padding-left: 80px; */ + font-size: 16px; +} +.demo-color-name { + color: #fff; + font-size: 14px; +} +.demo-color-desc { + color: white; + /* opacity: 0.7; */ + font-size: 20px; + margin-top: 2px; +} +.ivu-card { + box-shadow: 0 3px 13px rgba(117, 114, 114, 0.47); + display: flex; + position: relative; + padding-top: 10px; + border-radius: 5px; +} +.ivu-card .icon-left { + width: 85px; +} +.ivu-card .ivu-card-body { + flex: 1; +} +.ivu-card .icon-left { + text-align: center; + border-right: 1px solid; + padding: 8px 0px; + height: 100%; + + font-size: 50px; + color: white; +} +.ivu-row { + border-bottom: 2px dotted #eee; + padding: 15px; + margin-bottom: 15px; + display: flex; +} + +.ivu-row > div { + flex: 1; +} + +.h5-desc { + padding-top: 10px; +} +</style> + +<style lang="less" scoped> +.jn-day-total { + display: flex; + padding: 15px; + background: white; + .date-text { + line-height: 36px; + padding: 0 15px; + } + .date { + margin-right: 20px; + } + .btn { + margin-left: 10px; + } +} +.order-title { + h2 { + padding: 7px 15px; + font-weight: 500; + background: white; + border-bottom: 1px dotted #d4d4d4; + } +} + +.order-range { + padding: 0 15px; + background: white; + background: white; + display: flex; + // flex-direction: row-reverse; +} + +.order-range .order-item { + box-shadow: 0 3px 13px rgba(117, 114, 114, 0.47); + flex: 1; + border-radius: 6px; + font-size: 14px; + text-align: center; + border: 1px solid #e6e6e6; + margin: 7px; +} + +.order-range .total { + color: white; + font-size: 50px; + font-weight: bold; + line-height: 100px; + background: #55ce80; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", + "Microsoft YaHei", "寰蒋闆呴粦", Arial, sans-serif; +} +.order-range .number { + transition: transform 0.8s; +} +.order-range .number:hover { + cursor: pointer; + transform: scale(1.2); +} +.order-range .name { + font-size: 20px; + padding: 10px; +} + +.order-range .date { + padding: 1px 0 20px 0; + color: #9e9e9e; + font-size: 13px; +} +</style> + + +<style lang="less" scoped> +.numbers { + margin-bottom: 15px; + border-radius: 5px; + border: 1px solid #eaeaea; + background: white; + display: flex; + + padding: 20px 0px; + .item { + flex: 1; + text-align: center; + border-right: 1px solid #e5e5e5; + } + .item > 銆�div:first-child { + word-break: break-all; + color: #282727; + font-size: 30px; + // padding-bottom: 12px; + } + .item > 銆�div:last-child { + font-size: 13px; + color: #777; + } + .item:last-child { + border-right: none; + } + .number { + cursor: pointer; + transition: transform 0.8s; + } + .number:hover { + transform: scale(1.2); + color: #03c10b !important; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue" new file mode 100644 index 0000000..25d4f10 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue" @@ -0,0 +1,118 @@ +<template> + <div class="c-container"> + <div class="item"> + <div class="left"> + <VolHeader + icon="md-apps" + text="鏌辩姸鍥�" + style="padding-left: 10px; margin-bottom: 5px" + ></VolHeader> + <div style="height: calc(100% - 45px)" :id="bar"></div> + </div> + <div class="right"> + <VolHeader + icon="md-apps" + text="鍩虹琛ㄥ崟" + style="padding-left: 10px; margin-bottom: 20px" + ></VolHeader> + <VolForm + style="padding-right: 30px" + ref="myform1" + :loadKey="true" + :label-width="80" + :formFields="formFields1" + :formRules="formRules1" + ></VolForm> + </div> + </div> + + <div class="item"> + <div class="left"> + <VolHeader + icon="md-apps" + text="琛ㄥ崟灞炴�у瓧娈靛彧璇�" + style="padding-left: 10px; margin-bottom: 20px" + ></VolHeader> + <VolForm + style="padding-right: 30px" + ref="myform1" + :loadKey="true" + :formFields="formFields2" + :formRules="formRules2" + ></VolForm> + </div> + <div class="right"> + <VolHeader + icon="md-apps" + text="楗肩姸鍥捐〃" + style="padding-left: 10px" + ></VolHeader> + <div style="height: calc(100% - 30px)" :id="pie"></div> + </div> + </div> + </div> +</template> +<script> +import VolHeader from '@/components/basic/VolHeader.vue'; +import VolForm from '@/components/basic/VolForm.vue'; +let echarts = require('echarts'); +import options from './chartOptions'; +import { + formFields1, + formRules1, + formFields2, + formRules2 +} from './formOptions'; +export default { + components: { VolForm, VolHeader }, + data() { + return { + formFields1: formFields1, + formRules1: formRules1, + formFields2: formFields2, + formRules2: formRules2, + bar: 'b-' + ~~(Math.random(10000, 100000) * 100000), + pie: 'p-' + ~~(Math.random(10000, 100000) * 100000), + options: options + }; + }, + mounted() { + let $bar = echarts.init(document.getElementById(this.bar)); + $bar.setOption(this.options.bar); + + this.options.pie.legend.top = 50; + this.options.pie.legend.right = 80; + this.options.pie.legend.orient = 'vertical'; + let $pie = echarts.init(document.getElementById(this.pie)); + $pie.setOption(this.options.pie); + } +}; +</script> +<style lang="less" scoped> +.c-container { + position: absolute; + height: 100%; + width: 100%; + background: #f1f1f1; + display: flex; + flex-direction: column; + padding: 3px; + .item { + flex:1; + height:0; + display: flex; + > div { + flex: 1; + width: 0; + // margin: 10px; + background: #fff; + } + .left { + margin: 4px; + } + .right { + margin: 4px; + } + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js" new file mode 100644 index 0000000..8e17aa9 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js" @@ -0,0 +1,148 @@ +let formFields1 = { + Variety: "", + AgeRange: "", + DateRange: [], + City: "", + AvgPrice: 8.88, + Date: "", + IsTop: "杩樻病鎯冲ソ..." +} +let formRules1 = [ + //涓ゅ垪鐨勮〃鍗曪紝formRules鏁版嵁鏍煎紡涓�:[[{},{}]] + [ + { + link:true, + dataKey: "city", + title: "鍩庡競", + required: true, + field: "City", + data: [], + type: "select" + }, + { + title: "澶氶�夋棩鏈�", + range: true, //璁剧疆涓簍rue鍙互閫夋嫨寮�濮嬩笌缁撴潫鏃ユ湡 + required: false, + field: "DateRange", + // colSize: 4,//璁剧疆瀹藉害涓�1/3 + type: "date" + } + ], + [ + { + dataKey: "age", //鍚庡彴涓嬫媺妗嗗搴旂殑鏁版嵁瀛楀吀缂栧彿 + data: [], //loadKey璁剧疆涓簍rue,浼氭牴鎹甦ataKey浠庡悗鍙扮殑涓嬫媺妗嗘暟鎹簮涓嚜鍔ㄥ姞杞芥暟鎹� + title: "涓嬫媺", + required: true, //璁剧疆涓哄繀閫夐」 + field: "AgeRange", + type: "select" + }, + { + title: "鏃ユ湡", + required: true, + field: "Date", + placeholder: "浣犲彲浠ヨ缃甤olSize灞炴�у喅瀹氭爣绛剧殑闀垮害锛屽彲閫夊��12/8/6/4", + // colSize: 8,//璁剧疆瀹藉害涓�2/3 + type: "datetime" + } + ], + [ + { + title: "娴嬭瘯", + dataKey: "age", + placeholder: "姝ゅ鏁版嵁婧愪负鎵嬪姩缁戝畾", + //濡傛灉杩欓噷缁戝畾浜哾ata鏁版嵁锛屽悗鍙颁笉浼氬姞杞芥鏁版嵁婧� + data: [{ key: "1", value: "娴嬭瘯1" }, { key: "2", value: "娴嬭瘯2" }], + required: false, + field: "Variety", + type: "select" + }, + { + type: "decimal", + title: "浠锋牸", + required: true, + placeholder: "浣犲彲浠ヨ嚜宸卞畾涔塸laceholder鏄剧ず鐨勬枃瀛�", + field: "AvgPrice" + } + ], + [ + { + title: "澶囨敞", + required: true, + field: "IsTop", + colSize: 12, //璁剧疆12锛屾鍒楀崰100%瀹藉害 + type: "textarea" + } + ] +] + +let formFields2 = { + Variety: "涓�娆℃�х敤鍝�", + City: "鍖椾含甯�", + DateRange: "2019-09-01", + AvgPrice: 8.88, + Variety1: "", + DateRange1: "2019-09-02", + AvgPrice1: 7.72, + Address:"鍖椾含甯傛捣娣�鍖�001鍙�", + IsChange: 1 +} +let formRules2 = [ + //涓ゅ垪鐨勮〃鍗曪紝formRules鏁版嵁鏍煎紡涓�:[[{},{}]] + [ + { + title: "鍟嗗搧绫诲瀷", + dataKey: "age", + //濡傛灉杩欓噷缁戝畾浜哾ata鏁版嵁锛屽悗鍙颁笉浼氬姞杞芥鏁版嵁婧� + data: [{ key: "1", value: "1" }, { key: "2", value: "2" }], + field: "Variety", + disabled: true, + type: "select" + }, + { + dataKey: "city", + title: "鎵�鍦ㄥ煄甯�", + field: "City", + disabled: true, + type: "select", + data: [] + }], + [ + { + title: "閿�鍞棩鏈�", + field: "DateRange", + disabled: true, + }, + { + title: "閿�鍞环鏍�", + field: "AvgPrice", + disabled: true + }], + [ + { + title: "鐢熶骇鏃ユ湡", + field: "DateRange1", + disabled: true, + }, + { + title: "涓棿浠锋牸", + field: "AvgPrice1", + disabled: true + }], + [ + { + title: "閿�鍞湴鍧�", + field: "Address", + disabled: true, + }, + { + title: "鏄惁鎴愪氦", + field: "IsChange", + dataKey: "enable",//杩欓噷璁剧疆浜嗘暟鎹瓧鍏告簮鐨勭紪鍙蜂細鑷姩浠庡悗鍙板姞杞芥暟鎹簮鐨刱ey/value + data: [], + disabled: true + }] +] + + +export { formFields1, formRules1, formFields2, formRules2 } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js" new file mode 100644 index 0000000..3eaf63d --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js" @@ -0,0 +1,248 @@ +var chart1 = { + title: { + text: "妗嗘灦闆嗘垚鍙婂紑鍙戠幆澧�", + left: 'center', + padding: 15, + }, + tooltip: { + trigger: 'item' + }, + legend: { + top: 'bottom', + icon: "circle", // 杩欎釜瀛楁鎺у埗褰㈢姸 绫诲瀷鍖呮嫭 circle锛宺ect 锛宺oundRect锛宼riangle锛宒iamond锛宲in锛宎rrow锛宯one + itemWidth: 10, // 璁剧疆瀹藉害 + itemHeight: 10, // 璁剧疆楂樺害 + itemGap: 7,// 璁剧疆闂磋窛 + padding: [0, 0, 15, 0] //鍥句緥璺濈 + }, + series: [ + { + name: '妗嗘灦', + type: 'pie', + radius: ['40%', '65%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2 + }, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: '40', + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: [ + { value: 1048, name: '.NetCore' }, + { value: 735, name: 'Dapper' }, + + { value: 735, name: 'EntityFramework' }, + // { value: 735, name: 'JWT' }, + { value: 735, name: 'Redis' }, + { value: 735, name: 'Vue3.0' }, + { value: 580, name: 'Vuex' }, + { value: 484, name: 'Element plus' } + ] + } + ] +} + +var chart2 = { + title: { + text: '杩欓噷鏄椤�' + }, + tooltip: { + trigger: 'axis', + axisPointer: { // 鍧愭爣杞存寚绀哄櫒锛屽潗鏍囪酱瑙﹀彂鏈夋晥 + type: 'shadow' // 榛樿涓虹洿绾匡紝鍙�変负锛�'line' | 'shadow' + } + }, + legend: { + data: ['2.0涓嬭浇閲�', '3.0涓嬭浇閲�'], + padding: [0, 0, 15, 0] //鍥句緥璺濈 + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + top:'13%', + containLabel: true + }, + xAxis: [ + { + type: 'category', + data: [ '2015', '2016', '2017', '2018', '2019', '2020', '2021'] + } + ], + yAxis: [ + { + type: 'value' + } + ], + series: [ + { + name: '2.0涓嬭浇閲�', + type: 'bar', + showBackground: true, + backgroundStyle: { + color: 'rgba(180, 180, 180, 0.2)' + }, + itemStyle: { + + normal: { + barBorderRadius: [4, 4, 0, 0] + } + }, + data: [ 730, 620, 420, 932, 701, 834, 890] + }, + { + name: '3.0涓嬭浇閲�', + type: 'bar', + + showBackground: true, + backgroundStyle: { + color: 'rgba(180, 180, 180, 0.2)' + }, + data: [230, 210, 120, 132, 101, 134, 90] + } + ] +} +var chart3 = { + title: { + text: '妗嗘灦鏀寔鍔熻兘(Vue2.0銆乂ue3.0鐗堟湰)', + left: 'center' + }, + tooltip: { + trigger: 'item' + }, + legend: { + top: 'bottom', + icon: "circle", // 杩欎釜瀛楁鎺у埗褰㈢姸 绫诲瀷鍖呮嫭 circle锛宺ect 锛宺oundRect锛宼riangle锛宒iamond锛宲in锛宎rrow锛宯one + itemWidth: 10, // 璁剧疆瀹藉害 + itemHeight: 10, // 璁剧疆楂樺害 + itemGap: 7,// 璁剧疆闂磋窛 + padding: [0, 0, 10, 0] //鍥句緥璺濈 + }, + series: [ + { + + name: '妗嗘灦鏀寔鍔熻兘(Vue2.0銆乂ue3鐗堟湰)', + type: 'pie', + radius: '55%', + data: [ + { value: 748, name: '澶氱鎴�' }, + { value: 435, name: '澶氳鑹�' }, + { value: 580, name: '澶氭暟鎹簱' }, + { value: 280, name: '涓讳粠鍒嗗簱' }, + { value: 284, name: '鍥介檯鍖�' }, + { value: 300, name: 'App/H5寮�鍙�' }, + { value: 200, name: 'Redis' }, + { value: 600, name: 'Sqlserver' }, + { value: 400, name: 'Mysql' }, + { value: 100, name: 'Oracle' }, + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] +} +var chart4={ + tooltip: { + trigger: 'axis', + axisPointer: { + // Use axis to trigger tooltip + type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow' + } + }, + legend: {}, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + top:'13%', + containLabel: true + }, + xAxis: { + type: 'value' + }, + yAxis: { + type: 'category', + data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + }, + series: [ + { + name: 'Direct', + type: 'bar', + stack: 'total', + label: { + show: true + }, + emphasis: { + focus: 'series' + }, + data: [320, 302, 301, 334, 390, 330, 320] + }, + { + name: 'Mail Ad', + type: 'bar', + stack: 'total', + label: { + show: true + }, + emphasis: { + focus: 'series' + }, + data: [120, 132, 101, 134, 90, 230, 210] + }, + { + name: 'Affiliate Ad', + type: 'bar', + stack: 'total', + label: { + show: true + }, + emphasis: { + focus: 'series' + }, + data: [220, 182, 191, 234, 290, 330, 310] + }, + { + name: 'Video Ad', + type: 'bar', + stack: 'total', + label: { + show: true + }, + emphasis: { + focus: 'series' + }, + data: [150, 212, 201, 154, 190, 330, 410] + }, + { + name: 'Search Engine', + type: 'bar', + stack: 'total', + label: { + show: true + }, + emphasis: { + focus: 'series' + }, + data: [820, 832, 901, 934, 1290, 1330, 1320] + } + ] + } +export { chart1, chart2, chart3,chart4 } \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/Message.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/Message.vue" new file mode 100644 index 0000000..2c77447 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/Message.vue" @@ -0,0 +1,45 @@ +<template> + <div class="message-container"> + <div class="item" v-for="(item, index) in list" :key="index"> + <div class="title">{{ item.title }}({{ item.date }})</div> + <div class="content">{{ item.message }}</div> + </div> + </div> +</template> + +<script> +export default { + props: { + list: { + type: Array, + default: () => { + return []; + } + } + }, + created() { + if (!this.list.length) { + this.list.push({ + title: '娑堟伅娴嬭瘯鏍囬', + message: '娑堟伅娴嬭瘯鍐呭娑堟伅娴嬭瘯鍐呭娑堟伅娴嬭瘯鍐呭娑堟伅娴嬭瘯鍐呭', + date: '2022-05-02 03:10' + }); + } + } +}; +</script> +<style scoped lang="less"> +.message-container { + .title { + padding-bottom: 10px; + } + .item { + border-bottom: 1px solid #eee; + padding: 10px 20px; + } + .content { + color: #7e7e7e; + font-size: 13px; + } +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js" new file mode 100644 index 0000000..0f18350 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js" @@ -0,0 +1,28 @@ +import * as signalR from '@microsoft/signalr'; +import { ElNotification } from 'element-plus'; + +export default function (http, receive) { + let connection; + http.post('api/User/GetCurrentUserInfo').then((result) => { + connection = new signalR.HubConnectionBuilder() + .withAutomaticReconnect() + .withUrl(`${http.ipAddress}message?userName=${result.data.userName}`) + //.withUrl(`${http.ipAddress}message`) + .build(); + + connection.start().catch((err) => console.log(ex.message)); + //鑷姩閲嶈繛鎴愬姛鍚庣殑澶勭悊 + connection.onreconnected((connectionId) => { + console.log(connectionId); + }); + connection.on('ReceiveHomePageMessage', function (data) { + console.log(data) + ElNotification.success({ + title:data.title, + message: data.message + '', + type: 'info' + }); + receive && receive(data); + }); + }); +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/index.less" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/index.less" new file mode 100644 index 0000000..c872e91 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/index/index.less" @@ -0,0 +1,644 @@ +.vol-aside { + height: 100%; + position: absolute; + float: left; + overflow: hidden; +} + +.vol-menu { + border: 0 !important; +} + +.vol-aside .tac { + text-align: left; +} + +.vol-aside .header { + text-align: center; + position: absolute; + height: 60px; + position: relative; + line-height: 60px; +} + +.vol-aside .vol-menu { + position: absolute; + width: 100%; + top: 60px; + bottom: 0; + background: white; + border-right: 1px solid #e3e3e3; +} + +.vol-aside .vol-menu ::v-deep(.ivu-menu) { + text-align: left; + position: unset; + width: 100% !important; +} + +.vol-aside .vol-menu ::v-deep(.is-horizontal) { + display: none !important; +} + +.vol-aside .vol-menu ::v-deep(.is-vertical) { + width: 2px; + right: -1px; +} + +.vol-container { + min-width: 800px; + right: 0; + display: inline-block; + position: absolute; + margin: 0; + box-sizing: border-box; + height: 100%; +} + +.vol-container .vol-path { + position: relative; + width: 100%; + display: inline-block; + border-bottom: 1px solid #eee; +} + +.vol-container .vol-path span { + position: relative; + margin-right: 10px; + color: #969696; +} + +.vol-header { + height: 61px; + width: 100%; + position: relative; + display: flex; + border-bottom: 1px solid #eee; +} + +.vol-main { + border-left: 1px solid #eee; + position: absolute; + width: 100%; + bottom: 0; + top: 95px; + margin: 0; + overflow: auto; +} + +.header { + padding: 5px; +} + +.header img { + height: 100%; + margin-right: 25px; +} + +.header-info { + padding-right: 20px; + display: inline-block; + // position: absolute; + height: 100%; +} + +.header-info > div { + float: left; + height: 100%; +} + +.user-header { + background: white; + height: 52px; + width: 52px; + border-radius: 50%; + margin-right: 0px; + top: 4px; + left: 7px; + position: relative; + border: 1px solid #dfdfdf; +} + +.project-name { + line-height: 60px; + padding: 0 50px 0 20px; + color: #fff; + font-size: 16px; + letter-spacing: 1px; +} + +.header-text { + vertical-align: middle; + height: 100%; + // position: absolute; + flex: 1; + text-align: left; + font-size: 15px; + left: 21px; + line-height: 60px; + letter-spacing: 1px; +} + +.vol-header .user { + text-align: left; + padding: 0 12px; + position: relative; + height: 100%; + display: flex; + flex-direction: column; + // height: 60px; + justify-content: center; + span:last-child { + font-size: 12px; + } +} + +.vol-header .settings { + padding-top: 10px; + color: #d4d2d2; +} + +.vol-header .user span { + position: relative; +} + +.header-info:hover { + cursor: pointer; +} + +.header-navigation { + cursor: pointer; + box-shadow: none; + border-bottom: 1px solid #eee; + height: 34px; + /* overflow: hidden; */ + line-height: 35px; + display: block; + margin: 0; + padding: 0; + outline: 0; + list-style: none; + position: relative; + z-index: 900; + font-weight: initial; + margin-top: -1px; +} + +.el-tabs--border-card { + border: none; +} + +.header-navigation ::v-deep(.el-tabs__item) { + height: 34px; + font-size: 14px; + line-height: 34px; + padding-bottom: 6px; + color: #525252 !important; + position: relative; + margin: 0 4px; + border: 1px solid #e2e2e2; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + // border-bottom: 0px; +} + +.header-navigation ::v-deep(.el-tabs__item.is-active) { + color: #1a81ea !important; +} + +.header-navigation ::v-deep(.el-tabs__nav-prev), +.header-navigation ::v-deep(.el-tabs__nav-next) { + line-height: 35px; + padding-left: 4px; +} + +.vol-header .user span:first-child { + font-size: 15px; + font-weight: bolder; +} + +.h-link { + line-height: 59px; +} + +.h-link a { + font-size: 14px; + text-decoration: none; + padding: 0px 15px; + /* height: 60px; */ + display: inline-block; +} + +img[src=''], +img:not([src]) { + opacity: 0; +} + +//榛戣壊 +.vol-theme-dark { + .header { + background: #101010; + } + + .header-text { + color: white; + } + + .vol-header { + background-color: #272929; + } + + .h-link a:hover { + background: #0c0202; + } + + a { + color: #f2f2f2; + } + + .h-link a:hover { + color: #dfdfdf; + } + + .h-link .actived { + border-bottom: 2px solid white; + } + + .h-link .actived a { + color: white !important; + } + + .vol-header .user { + color: #ececec; + } + + .vol-header .settings { + color: #d4d2d2; + } + + .vol-aside .vol-menu { + background: black; + } +} + +.vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item) { + background: #2e333b; + color: white; +} + +.vol-theme-dark .vol-aside ::v-deep(.vol-menu .el-submenu) { + background: black; +} + +.vol-theme-dark .vol-aside ::v-deep(.vol-menu .el-sub-menu__title *) { + color: #d6d6d6; +} + +.vol-theme-dark .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item) { + color: #eee; + background: #1f1f1f; +} + +.vol-theme-dark .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item.is-active), +.vol-theme-dark .vol-aside ::v-deep(.menu-item-lv1) { + background: black; +} + +.vol-theme-dark .vol-aside ::v-deep(.menu-item-lv1) { + background: black; + color: #d6d6d6; +} + +.vol-theme-dark .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item:hover) { + background: black; +} + +.vol-theme-dark .vol-aside ::v-deep(.el-sub-menu__title) { + background-color: black; +} + +.vol-theme-dark .vol-aside ::v-deep(.el-sub-menu__title:hover) { + background-color: rgb(25, 25, 25); +} + +.vol-theme-dark .vol-aside ::v-deep(.el-sub-menu__title:hover *) { + color: white; +} + +.vol-theme-red, +.vol-theme-red2 { + .vol-header { + background-color: rgb(237, 64, 20); + } + + .header-text { + color: #dcdfe6; + } + + .h-link a:hover { + background: #d71212; + } + + .h-link .actived { + border-bottom: 2px solid white; + } + + .h-link a, + .h-link .actived a, + .vol-header .settings, + .vol-header .user { + color: white; + } + + .vol-header .header-text { + color: #fbfbfb; + } +} + +.vol-theme-red { + .header { + background-color: rgb(237, 64, 20); + } +} + +.vol-theme-red2 { + .header { + background-color: #a90000; + } +} + +.vol-theme-orange, +.vol-theme-orange2 { + .header-text { + color: #dcdfe6; + } + + .vol-header { + background-color: rgb(255, 153, 0); + } + + .h-link a:hover { + background: #c97901; + } + + .h-link .actived { + border-bottom: 2px solid white; + } + + .h-link a, + .h-link .actived a, + .vol-header .settings, + .vol-header .user { + color: white; + } + + .vol-header .header-text { + color: #fbfbfb; + } +} + +.vol-theme-orange { + .header { + background: rgb(255, 153, 0); + } +} + +.vol-theme-orange2 { + .header { + background-color: rgb(232, 141, 5); + } +} + +//缁胯壊 +.vol-theme-green, +.vol-theme-green2 { + .header-text { + color: #dcdfe6; + } + + .vol-header { + background-color: rgb(25, 190, 107); + } + + .h-link a:hover { + background: #329103; + } + + .h-link .actived { + border-bottom: 2px solid white; + } + + .h-link a, + .h-link .actived a, + .vol-header .settings, + .vol-header .user { + color: white; + } + + .vol-header .header-text { + color: #fbfbfb; + } +} + +.vol-theme-green { + .header { + background: rgb(25, 190, 107); + } +} + +.vol-theme-green2 { + .header { + background-color: rgb(1, 158, 79); + } +} + +//钃濊壊 +.vol-theme-blue, +.vol-theme-blue2 { + .header-text { + color: #dcdfe6; + } + + .vol-header { + background-color: rgb(45, 140, 240); + } + + .h-link a:hover { + background: #0170e3; + } + + .h-link .actived { + border-bottom: 2px solid white; + } + + .h-link a, + .h-link .actived a, + .vol-header .settings, + .vol-header .user { + color: white; + } + + .vol-header .header-text { + color: #fbfbfb; + } +} + +.vol-theme-blue { + .header { + background-color: rgb(45, 140, 240); + } +} + +.vol-theme-blue2 { + .header { + background-color: rgb(0, 104, 214); + } +} + +//鐧借壊 +.vol-theme-white { + .header { + background-color: #434956; + } + + .h-link a:hover { + background: #eeeeee; + } + + .h-link a { + color: #211f1f; + } + + .header-navigation { + // box-shadow: -7px 11px 10px -13px #678aa7; + border-bottom: 1px solid #eee; + height: 32px; + overflow: hidden; + line-height: 32px; + display: block; + margin: 0; + padding: 0; + outline: 0; + list-style: none; + position: relative; + z-index: 900; + font-weight: 400; + } + + .vol-aside .vol-menu { + background: #353941; + } +} + +.vol-theme-white .project-name { + color: #505050; +} + +.vol-theme-white .vol-aside::v-deep(.vol-el-menu-item .el-menu-item.is-active), +.vol-theme-white .vol-aside ::v-deep(.menu-item-lv1) { + background: #353941; +} + +.vol-theme-white .vol-aside ::v-deep(.menu-item-lv1) { + background: #353941; + color: #d6d6d6; +} + +.vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item:hover) { + background: #353941; +} + +.vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title) { + background-color: #353941; +} + +.vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title:hover) { + background-color: rgb(47, 46, 46); +} + +.vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title), +.vol-theme-white .vol-aside ::v-deep(.el-menu-item), +.vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title:hover *) { + color: #bababa; +} + +// .vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item) { +// background: #363e4f; +// color: white; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-menu .el-submenu), +// .vol-theme-white .vol-aside ::v-deep(.menu-item-lv1) { +// background: #515a6e; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-menu) { +// background: #515a6e; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-menu .el-sub-menu__title *), +// .vol-theme-white .vol-aside ::v-deep(.menu-item-lv1 *) { +// color: #d6d6d6; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item) { +// color: #eee; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item.is-active), +// .vol-theme-white .vol-aside ::v-deep(.menu-item-lv1.is-active) { +// background: #59647b; +// color: #fff; +// } +// .vol-theme-white .vol-aside ::v-deep(.vol-el-menu-item .el-menu-item:hover) { +// background: #6a758c; +// } +// .vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title:hover) { +// background-color: #525865; +// } +// .vol-theme-white .vol-aside ::v-deep(.el-sub-menu__title:hover *) { +// color: white; +// } + +// .vol-theme-red ::v-deep(.el-menu-item.is-active), +// .vol-theme-red2 ::v-deep(.el-menu-item.is-active) +// { +// background-color: #d71212; +// } +// .vol-theme-blue ::v-deep(.el-menu-item.is-active), +// .vol-theme-blue2 ::v-deep(.el-menu-item.is-active) +// { +// background-color: #2d8cf0; +// } +// .vol-theme-orange ::v-deep(.el-menu-item.is-active), +// .vol-theme-orange2 ::v-deep(.el-menu-item.is-active) +// { +// background-color: #ff9900; +// } + +// .vol-theme-green ::v-deep(.el-menu-item.is-active), +// .vol-theme-green2 ::v-deep(.el-menu-item.is-active) +// { +// background-color: #19be6b; +// } + +.theme-selector { + height: 100%; + padding-left: 16px; + + .item { + cursor: pointer; + width: 60px; + height: 60px; + border-radius: 5px; + margin-bottom: 17px; + border: 1px solid #d4d2d2; + float: left; + margin-right: 13px; + } +} + +.collapse-menu { + font-size: 21px; + color: #fff; + line-height: 60px; + position: absolute; + top: 0; + right: 5px; + cursor: pointer; +} diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue" new file mode 100644 index 0000000..6b86cae --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue" @@ -0,0 +1,365 @@ + + <template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> +<script> +import extend from "@/extension/quartzJob/deviceInfo.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "id", + footer: "Foots", + cnName: "璁惧淇℃伅", + name: "deviceInfo", + url: "/DeviceInfo/", + sortName: "createDate", + }); + const editFormFields = ref({ + deviceCode: "", + deviceName: "", + deviceType: "", + deviceStatus: "", + deviceIp: "", + devicePort: "", + devicePlcType: "", + deviceRemark: "", + }); + const editFormOptions = ref([ + [ + { + title: "璁惧缂栧彿", + required: true, + field: "deviceCode", + type: "string", + }, + { + title: "璁惧鍚嶇О", + required: true, + field: "deviceName", + type: "string", + }, + { + title: "璁惧绫诲瀷", + required: true, + field: "deviceType", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "璁惧鐘舵��", + required: true, + field: "deviceStatus", + type: "select", + dataKey: "deviceStatus", + data: [], + }, + ], + [ + { title: "璁惧IP", required: true, field: "deviceIp", type: "string" }, + { + title: "璁惧绔彛", + required: true, + field: "devicePort", + type: "string", + }, + { + title: "PLC绫诲瀷", + required: true, + field: "devicePlcType", + type: "select", + dataKey: "devicePlcType", + data: [], + }, + { + title: "澶囨敞", + field: "deviceRemark", + type: "string", + }, + ], + ]); + const searchFormFields = ref({ + deviceCode: "", + deviceType: "", + deviceStatus: "", + }); + const searchFormOptions = ref([ + [ + { title: "璁惧缂栧彿", field: "deviceCode" }, + { + title: "璁惧绫诲瀷", + field: "deviceType", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "璁惧鐘舵��", + field: "deviceStatus", + type: "select", + dataKey: "deviceStatus", + data: [], + }, + ], + ]); + const columns = ref([ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "deviceCode", + title: "璁惧缂栧彿", + type: "string", + width: 90, + align: "left", + }, + { + field: "deviceName", + title: "璁惧鍚嶇О", + type: "string", + link: true, + width: 150, + align: "left", + }, + { + field: "deviceType", + title: "璁惧绫诲瀷", + type: "string", + width: 150, + align: "left", + bind: { key: "deviceType", data: [] }, + }, + { + field: "deviceStatus", + title: "璁惧鐘舵��", + type: "string", + width: 90, + align: "left", + bind: { key: "deviceStatus", data: [] }, + }, + { + field: "deviceIp", + title: "璁惧IP", + type: "string", + width: 90, + align: "left", + }, + { + field: "devicePort", + title: "璁惧绔彛", + type: "int", + width: 120, + align: "left", + }, + { + field: "devicePlcType", + title: "PLC绫诲瀷", + type: "string", + width: 120, + align: "left", + bind: { key: "devicePlcType", data: [] }, + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "deviceRemark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + }, + ]); + const detail = ref({ + cnName: "璁惧鍗忚淇℃伅", + table: "DeviceProtocol", + columns: [ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + require: true, + align: "left", + }, + { + field: "deviceId", + title: "璁惧涓婚敭", + type: "string", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "deviceChildCode", + title: "璁惧瀛愮紪鍙�", + type: "string", + edit: { type: "" }, + width: 150, + require: true, + align: "left", + sortable: true, + }, + { + field: "deviceProDataBlock", + title: "鍗忚鏁版嵁鍧�", + type: "string", + width: 100, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "deviceProOffset", + title: "鍋忕Щ閲�", + type: "decimal", + width: 90, + edit: { type: "number" }, + require: true, + align: "left", + }, + { + field: "deviceProDataType", + title: "鏁版嵁绫诲瀷", + type: "string", + width: 90, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "deviceProDataLength", + title: "鏁版嵁闀垮害", + type: "int", + width: 90, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "deviceProParamName", + title: "鍙傛暟鍚嶇О", + type: "string", + width: 150, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "deviceProParamType", + title: "鍙傛暟绫诲瀷", + type: "string", + width: 150, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "deviceProParamDes", + title: "鍙傛暟璇存槑", + type: "string", + width: 130, + require: true, + align: "left", + edit: { type: "" }, + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + sortable: true, + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + ], + sortName: "CreateDate", + key: "id", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue" new file mode 100644 index 0000000..6d3ea40 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue" @@ -0,0 +1,229 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> + <script> +import extend from "@/extension/quartzJob/deviceProtocol.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "id", + footer: "Foots", + cnName: "璁惧鍗忚淇℃伅", + name: "deviceProtocol", + url: "/DeviceProtocol/", + sortName: "id", + }); + const editFormFields = ref({ + deviceCode: "", + deviceName: "", + deviceType: "", + deviceStatus: "", + deviceIp: "", + devicePort: "", + devicePlcType: "", + deviceRemark: "", + }); + const editFormOptions = ref([ + [ + { + title: "璁惧缂栧彿", + required: true, + field: "deviceCode", + type: "string", + }, + { + title: "璁惧鍚嶇О", + required: true, + field: "deviceName", + type: "string", + }, + { + title: "璁惧绫诲瀷", + required: true, + field: "deviceType", + type: "string", + }, + { + title: "璁惧鐘舵��", + required: true, + field: "deviceStatus", + type: "string", + }, + ], + [ + { title: "璁惧IP", required: true, field: "deviceIp", type: "string" }, + { + title: "璁惧绔彛", + required: true, + field: "devicePort", + type: "string", + }, + { + title: "PLC绫诲瀷", + required: true, + field: "devicePlcType", + type: "string", + }, + { + title: "澶囨敞", + field: "deviceRemark", + type: "string", + }, + ], + ]); + const searchFormFields = ref({ + deviceCode: "", + deviceType: "", + deviceStatus: "", + }); + const searchFormOptions = ref([ + [ + { title: "璁惧缂栧彿", field: "deviceCode" }, + { title: "璁惧绫诲瀷", field: "deviceType" }, + { title: "璁惧鐘舵��", field: "deviceStatus" }, + ], + ]); + const columns = ref([ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "deviceId", + title: "璁惧涓婚敭", + type: "string", + width: 90, + align: "left", + }, + { + field: "deviceChildCode", + title: "璁惧瀛愮紪鍙�", + type: "string", + width: 150, + align: "left", + }, + { + field: "deviceProDataBlock", + title: "鍗忚鏁版嵁鍧�", + type: "string", + width: 150, + align: "left", + }, + { + field: "deviceProOffset", + title: "鍋忕Щ閲�", + type: "decimal", + width: 90, + align: "left", + }, + { + field: "deviceProDataType", + title: "鏁版嵁绫诲瀷", + type: "string", + width: 90, + align: "left", + }, + { + field: "deviceProDataLength", + title: "鏁版嵁闀垮害", + type: "int", + width: 120, + align: "left", + }, + { + field: "deviceProParamName", + title: "鍙傛暟鍚嶇О", + type: "string", + width: 200, + align: "left", + }, + { + field: "deviceProParamType", + title: "鍙傛暟绫诲瀷", + type: "string", + width: 180, + align: "left", + }, + { + field: "deviceProParamDes", + title: "鍙傛暟璇存槑", + type: "string", + width: 120, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "deviceProRemark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + }, + ]); + const detail = ref({ + cnName: "", + table: "", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue" new file mode 100644 index 0000000..6dd1be7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue" @@ -0,0 +1,207 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> + <script> +import extend from "@/extension/quartzJob/deviceProtocolDetail.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "id", + footer: "Foots", + cnName: "璁惧鍗忚鏄庣粏淇℃伅", + name: "deviceProtocolDetail", + url: "/DeviceProtocolDetail/", + sortName: "createDate", + }); + const editFormFields = ref({ + deviceType: "", + deviceProParamName: "", + protocolDetailType: "", + protocalDetailValue: "", + protocolDetailDes: "", + }); + const editFormOptions = ref([ + [ + { + title: "璁惧绫诲瀷", + required: true, + field: "deviceType", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "鍙傛暟鍚嶇О", + required: true, + field: "deviceProParamName", + type: "string", + }, + { + title: "鏄庣粏绫诲瀷", + required: true, + field: "protocolDetailType", + type: "string", + }, + { + title: "鏄庣粏鍙栧��", + required: true, + field: "protocalDetailValue", + type: "string", + }, + ], + [ + { + title: "鏄庣粏璇存槑", + field: "protocolDetailDes", + type: "textarea", + }, + ], + ]); + const searchFormFields = ref({ + deviceType: "", + deviceProParamName: "", + protocolDetailType: "", + }); + const searchFormOptions = ref([ + [ + { + title: "璁惧绫诲瀷", + field: "deviceType", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "鍙傛暟鍚嶇О", + field: "deviceProParamName", + type: "like", + }, + { + title: "鏄庣粏绫诲瀷", + field: "protocolDetailType", + type: "like", + }, + { + title: "鏄庣粏鍙栧��", + field: "protocalDetailValue", + type: "like", + }, + ], + ]); + const columns = ref([ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "deviceType", + title: "璁惧绫诲瀷", + type: "string", + width: 180, + align: "left", + bind: { key: "deviceType", data: [] }, + }, + { + field: "deviceProParamName", + title: "璁惧鍗忚鍙傛暟鍚嶇О", + type: "string", + width: 180, + align: "left", + }, + { + field: "protocolDetailType", + title: "璁惧鍗忚鏄庣粏绫诲瀷", + type: "string", + width: 150, + align: "left", + }, + { + field: "protocalDetailValue", + title: "璁惧鍗忚鏄庣粏鍙栧��", + type: "string", + width: 150, + align: "left", + }, + { + field: "protocolDetailDes", + title: "璁惧鍗忚鏄庣粏璇存槑", + type: "string", + width: 350, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + }, + ]); + const detail = ref({ + cnName: "", + table: "", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue" new file mode 100644 index 0000000..3e88503 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue" @@ -0,0 +1,247 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> + <script> +import extend from "@/extension/quartzJob/dispatchInfo.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "id", + footer: "Foots", + cnName: "璋冨害鏈嶅姟閰嶇疆淇℃伅", + name: "dispatchInfo", + url: "/DispatchInfo/", + sortName: "createDate", + }); + const editFormFields = ref({ + name: "", + jobGroup: "", + assemblyName: "", + className: "", + intervalSecond: "", + beginTime: "", + endTime: "", + remark: "", + }); + const editFormOptions = ref([ + [ + { + title: "浠诲姟鍚嶇О", + required: true, + field: "name", + type: "string", + }, + { + title: "浠诲姟鍒嗙粍", + required: true, + field: "jobGroup", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "绋嬪簭闆嗗悕绉�", + required: true, + field: "assemblyName", + type: "string", + type: "select", + dataKey: "jobAssembly", + data: [], + }, + { + title: "浠诲姟鎵�鍦ㄧ被", + required: true, + field: "className", + type: "string", + type: "select", + dataKey: "jobClassName", + data: [], + }, + ], + [ + { + title: "闂撮殧鏃堕棿", + required: true, + field: "intervalSecond", + type: "number", + }, + { + title: "寮�濮嬫椂闂�", + field: "beginTime", + type: "datetime", + }, + { + title: "缁撴潫鏃堕棿", + field: "endTime", + type: "datetime", + }, + { + title: "澶囨敞", + field: "remark", + type: "string", + }, + ], + ]); + const searchFormFields = ref({ + name: "", + jobGroup: "", + assemblyName: "", + className: "", + }); + const searchFormOptions = ref([ + [ + { + title: "浠诲姟鍚嶇О", + field: "name", + type: "like", + }, + { + title: "浠诲姟鍒嗙粍", + field: "jobGroup", + type: "select", + dataKey: "deviceType", + data: [], + }, + { + title: "绋嬪簭闆嗗悕绉�", + field: "assemblyName", + type: "like", + }, + { + title: "浠诲姟鎵�鍦ㄧ被", + field: "className", + type: "like", + }, + ], + ]); + const columns = ref([ + { + field: "id", + title: "Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "name", + title: "浠诲姟鍚嶇О", + type: "string", + width: 90, + align: "left", + }, + { + field: "jobGroup", + title: "浠诲姟鍒嗙粍", + type: "string", + width: 180, + align: "left", + bind: { key: "deviceType", data: [] }, + }, + { + field: "assemblyName", + title: "绋嬪簭闆嗗悕绉�", + type: "string", + width: 180, + align: "left", + }, + { + field: "className", + title: "浠诲姟鎵�鍦ㄧ被", + type: "int", + width: 200, + align: "left", + }, + { + field: "intervalSecond", + title: "鎵ц闂撮殧鏃堕棿", + type: "int", + width: 120, + align: "left", + }, + { + field: "beginTime", + title: "寮�濮嬫椂闂�", + type: "datetime", + width: 150, + align: "left", + }, + { + field: "endTime", + title: "缁撴潫鏃堕棿", + type: "int", + width: 150, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + }, + ]); + const detail = ref({ + cnName: "", + table: "", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue" new file mode 100644 index 0000000..dd62254 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue" @@ -0,0 +1,359 @@ +<template> + <div class="role-container"> + <div class="role-tree-left flex-col"> + <div class="title"><i class="el-icon-user"></i>瑙掕壊鍒楄〃</div> + <el-scrollbar class="el-role-list"> + <el-tree + :data="tree" + @node-click="nodeClick" + node-key="id" + :default-expanded-keys="openKeys" + :expand-on-click-node="false" + style="padding: 5px 0; margin-right: 2px" + > + <template #default="{ data }"> + <div class="action-group"> + <div class="action-text"> + {{ data.roleName }} + </div> + </div> + </template> + </el-tree> + </el-scrollbar> + </div> + <div class="role-tree-right flex-col"> + <div class="title"> + <div><i class="el-icon-folder-opened"></i>鑿滃崟鏉冮檺</div> + <el-button type="primary" @click="save">淇濆瓨</el-button> + </div> + <el-scrollbar class="el-role-list"> + <el-tree + @check-change="leftCheckChange" + @check="nodeCheck" + :data="roleTree" + :show-checkbox="false" + style="padding: 15px" + node-key="id" + default-expand-all + :expand-on-click-node="false" + > + <template #default="{ data }"> + <div class="action-group"> + <div + class="action-text" + :style="{ width: (4 - data.lv) * 18 + 150 + 'px' }" + > + <el-checkbox v-model="data.leftCk" @change="allChange(data)">{{ + // data.text + (data.isApp ? "(app)" : "") + data.text + }}</el-checkbox> + </div> + <div class="action-item"> + <el-checkbox + v-for="(item, index) in data.actions" + :key="index" + v-model="item.checked" + @change="actionChange(data, item.checked)" + >{{ item.text }}</el-checkbox + > + </div> + </div> + </template> + </el-tree> + </el-scrollbar> + </div> + </div> +</template> + +<script> +import { defineComponent, ref, reactive, getCurrentInstance } from "vue"; +import http from "@/../src/api/http.js"; +export default defineComponent({ + setup() { + const selectId = ref(-1); + const checked = ref(false); + const tree = reactive([]); + const list = reactive([]); + const roles = reactive([]); + const roleList = reactive([]); + const roleTree = reactive([]); + const openKeys = reactive([]); + + const leftCheckChange = (node, selected) => { + node.actions.forEach((x, index) => { + x.checked = selected; + }); + }; + const nodeCheck = (node, data) => { + let rootData = roleList.find((x) => { + return x.id === node.pid; + }); + if (rootData && rootData.actions.length) { + rootData.actions[0].checked = + node.actions.some((x) => { + return x.checked; + }) || + data.halfCheckedNodes.some((x) => { + return x.id === node.pid; + }); + } + }; + + const allChange = (data) => { + data.actions.forEach((item) => { + item.checked = data.leftCk; + }); + if (!data.children) { + return; + } + setChildrenChecked(data, data.leftCk); + }; + const setChildrenChecked = (data, ck) => { + data.children.forEach((item) => { + item.leftCk = ck; + item.actions.forEach((c) => { + c.checked = ck; + }); + if (item.children) { + setChildrenChecked(item, ck); + } + }); + }; + const actionChange = (data, ck) => { + ck = + data.actions.filter((x) => { + return x.checked; + }).length == data.actions.length; + data.leftCk = ck; + }; + + const load = () => { + const url = "api/Sys_Role/getUserChildRoles"; + http.post(url, {}, true).then((result) => { + + if (!result.status) return; + list.splice(0); + list.push(...result.data); + list.forEach((x) => { + if (x.parentId == 0) { + x.lv = 1; + x.children = []; + tree.push(x); + getTree(x.id, x); + } + }); + openKeys.push(tree[0].id); + selectId.value = openKeys[0]; + }); + }; + const getTree = (id, data) => { + list.forEach((x) => { + if (x.parentId == id) { + x.lv = data.lv + 1; + if (!data.children) data.children = []; + data.children.push(x); + getTree(x.id, x); + } + }); + }; + const nodeClick = (node, selected) => { + selectId.value = node.id; + getUserRole(node); + }; + const getUserRole = (item) => { + selectId.value = item.id; + roleList.forEach((x) => { + x.actions.forEach((a) => { + a.checked = false; + }); + }); + let url = `/api/Sys_Role/getUserTreePermission?roleId=${item.id}`; + http.post(url, {}, true).then((result) => { + if (!result.status) return; + result.data.forEach((item) => { + if (item.actions.length == 0) return; + let sourceItem = roleList.find((f) => f.id == item.id); + if (!sourceItem) return; + item.actions.forEach((actions) => { + sourceItem.actions.forEach((soure) => { + if (soure.value == actions.value) { + soure.checked = true; + } + }); + }); + }); + }); + }; + + const getRoleTree = (id, data, isRootId) => { + roleList.forEach((x) => { + if (x.pid == id) { + x.lv = data.lv + 1; + if (isRootId) { + x.rootId = id; + } + if (!data.children) data.children = []; + data.children.push(x); + getRoleTree(x.id, x, isRootId); + } + }); + }; + + const getCurrentTreePermission = () => { + let url = "/api/Sys_Role/getCurrentTreePermission"; + http.post(url, {}, true).then((result) => { + if (!result.status) return; + roleList.splice(0); + roles.splice(0); + roleList.push(...result.data.tree); + roles.push(...result.data.roles); + roleList.forEach((x) => { + if (x.pid == 0) { + x.lv = 1; + x.children = []; + roleTree.push(x); + getRoleTree(x.id, x); + } + }); + }); + }; + let $message = + getCurrentInstance().appContext.config.globalProperties.$message; + const save = () => { + if (selectId.value <= 0) { + return $message.error("璇烽�夋嫨瑙掕壊!"); + } + let userPermissions = []; + roleList.forEach((x) => { + let checkedPermission = x.actions.filter((f) => { + return f.checked; + }); + if (checkedPermission.length > 0) { + let actions = checkedPermission.map((m) => { + return { text: m.text, value: m.value }; + }); + userPermissions.push({ + id: x.id, + actions: actions, + }); + } + }); + let url = `api/Sys_Role/SavePermission?roleId=${selectId.value}`; + http.post(url, userPermissions, true).then((result) => { + $message[result.status ? "success" : "error"](result.message); + }); + }; + + load(); + getCurrentTreePermission(); + return { + list, + nodeClick, + checked, + tree, + selectId, + openKeys, + getUserRole, + roles, + roleList, + getCurrentTreePermission, + leftCheckChange, + nodeCheck, + roleTree, + allChange, + actionChange, + save, + }; + }, +}); +</script> +<style lang="less" scoped> +.role-container { + position: absolute; + background: #f6f6f6; + height: 100%; + width: 100%; + padding: 10px; + display: flex; + .flex-col { + display: flex; + flex-direction: column; + } + .role-tree-left { + border: 1px solid #f2f2f2; + background: #fff; + width: 230px; + margin-right: 10px; + .title { + i { + margin-left: 10px; + } + } + } + .role-tree-right { + background: #fff; + border: 1px solid #f2f2f2; + width: 0; + flex: 1; + .title { + display: flex; + i { + margin-left: 10px; + } + div { + flex: 1; + } + } + .action-group { + display: flex; + // line-height: 32px; + justify-content: center; + align-items: center; + label { + float: left; + } + .action-text { + line-height: 33px; + label { + margin-right: 5px; + } + } + } + } + .title { + padding: 10px; + background: rgb(246 250 255); + font-weight: bold; + font-size: 14px; + letter-spacing: 2px; + } + .el-role-list { + flex: 1; + height: 0; + overflow-x: hidden; + } +} +.role-tree-left ::v-deep(.el-tree-node__content) { + cursor: pointer; + height: auto; + padding: 5px; + margin: 2px 10px; + font-size: 15px; +} +.role-tree-left ::v-deep(.el-tree-node__content:hover) { + background: #f4f4f4; + border-radius: 20px; +} +.role-tree-left ::v-deep(.is-current > .el-tree-node__content:first-child) { + background: #f2f2f2; + border-radius: 20px; +} +.role-tree-right ::v-deep(.el-tree-node__content) { + margin-bottom: 5px; + height: auto; +} +.role-tree-right ::v-deep(.el-checkbox__label) { + position: relative; + top: 2px; +} +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue" new file mode 100644 index 0000000..9ae4e6b --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue" @@ -0,0 +1,152 @@ +<template> + <div class="com-el-tree"> + + <div class="m-title"><i class="el-icon-warning-outline"></i>瑙掕壊鍒楄〃</div> + + <el-scrollbar style="height: 100%; width: 200px" class="el-role-tree"> + <el-tree + :data="tree" + @node-click="nodeClick" + node-key="id" + :default-expanded-keys="openKeys" + :expand-on-click-node="false" + style="padding: 5px 0; margin-right: 2px" + > + <template #default="{ node, data }"> + <div class="action-group"> + <div + class="action-text" + :class="{ + actived: data.id == selectId, + 'node-text': data.parentId !== 0, + }" + :style="{ width: (4 - data.lv) * 18 + 150 + 'px' }" + > + <Icon + v-if="data.parentId !== 0" + :type="data.id == selectId ? 'ios-paper' : 'ios-paper-outline'" + /> + {{ data.roleName }} + </div> + </div> + </template> + </el-tree> + </el-scrollbar> + </div> +</template> + +<script> +export default { + props: { + // roles: { + // type: Object, + // default: () => { + // return []; + // } + // }, + onChange: { + type: Function, + default: (treeId) => {}, + }, + }, + data() { + return { + selectId: -1, + checked: false, + openKeys: [], + data: [], + tree: [], + }; + }, + created() { + this.load(); + }, + methods: { + load() { + this.http.post("/api/Sys_Role/getUserChildRoles", {}, true).then((result) => { + if (!result.status) return this.$message.error(result.message); + this.data.splice(0); + this.data = result.data; + this.data.forEach((x) => { + if (x.parentId == 0) { + x.lv = 1; + x.children = []; + this.tree.push(x); + this.getTree(x.id, x); + } + }); + this.openKeys.push(this.tree[0].id); + this.selectId = this.openKeys[0]; + //榛樿鍔犺浇绗竴涓爲褰㈣彍鍗曚笅闈㈢殑鏁版嵁 + + //this.onChange(this.selectId); + }); + }, + getTree(id, data) { + this.data.forEach((x) => { + if (x.parentId == id) { + x.lv = data.lv + 1; + if (!data.children) data.children = []; + data.children.push(x); + this.getTree(x.id, x); + } + }); + }, + nodeClick(node, selected) { + // console.log(node); + this.selectId = node.id; + //缂撳瓨褰撳墠閫変腑鐨勮妭鐐� + // this.$store.getters.data().treeDemo1.treeId = node.id; + this.onChange(node); + }, + }, +}; +</script> +<style lang="less" scoped> +.com-el-tree { + //2020.06.03澧炲姞宸︿晶tree鍥哄畾瀹藉害 + width: 200px; + display: flex; + flex-direction: column; + height: 100%; + border-radius: 3px; + background: white; + .el-role-tree { + flex: 1; + // border-right: 1px solid #eee; + } + .actived { + } + .action-text { + font-size: 14px; + } +} +.role-list { + color: white; + line-height: 40px; + padding: 0 13px; + font-size: 16px; + top: 2px; + width: 200px; +} +.m-title { + line-height: 30px; + font-size: 15px; + background: #66b1ff0f; + font-weight: bold; + padding: 6px 16px; + border-bottom: 1px solid #eee; + i { + padding-right: 5px; + } +} +.com-el-tree ::v-dee(.el-tree-node) { + padding: 3px 0; +} +.com-el-tree ::v-dee(.el-scrollbar .el-scrollbar__thumb) { + width: 0 !important; +} +</style> +<style scoped> + +</style> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue" new file mode 100644 index 0000000..5ceafd6 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue" @@ -0,0 +1,325 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/Sys_Dictionary.js姝ゅ缂栧啓 + --> +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> +<script> +import extend from "@/extension/system/Sys_Dictionary.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "dicId", + footer: "Foots", + cnName: "瀛楀吀鏁版嵁", + name: "Sys_Dictionary", + url: "/Sys_Dictionary/", + sortName: "dicId", + }); + const editFormFields = ref({ + dicNo: "", + dicName: "", + parentId: "", + orderNo: "", + enable: "", + dbSql: "", + remark: "", + systemType: 1, + }); + const editFormOptions = ref([ + [ + { title: "瀛楀吀缂栧彿", required: true, field: "dicNo" }, + { title: "瀛楀吀鍚嶇О", required: true, field: "dicName" }, + { title: "鐖剁骇ID", required: true, field: "parentId", type: "number" }, + ], + [ + { title: "鎺掑簭鍙�", field: "orderNo", type: "number" }, + { + dataKey: "enable", + data: [], + title: "鏄惁鍚敤", + required: true, + field: "enable", + type: "select", + }, + ], + [ + { title: "sql璇彞", field: "dbSql", colSize: 8, type: "textarea" }, + { title: "澶囨敞", field: "remark", type: "textarea" }, + ], + ]); + const searchFormFields = ref({ + dicNo: "", + dicName: "", + parentId: "", + enable: "", + createDate: "", + modifyDate: "", + }); + const searchFormOptions = ref([ + [ + { title: "瀛楀吀缂栧彿", field: "dicNo" }, + { title: "瀛楀吀鍚嶇О", field: "dicName", type: "textarea" }, + { title: "鐖剁骇ID", field: "parentId", type: "number" }, + ], + [ + { + dataKey: "enable", + data: [], + title: "鏄惁鍚敤", + field: "enable", + type: "select", + }, + { title: "鍒涘缓鏃堕棿", field: "createDate", type: "datetime" }, + { title: "淇敼鏃堕棿", field: "modifyDate", type: "datetime" }, + ], + ]); + const columns = ref([ + { + field: "dicId", + title: "瀛楀吀ID", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "dicNo", + title: "瀛楀吀缂栧彿", + type: "string", + width: 90, + require: true, + align: "left", + sort: true, + }, + { + field: "dicName", + title: "瀛楀吀鍚嶇О", + type: "string", + link: true, + width: 140, + require: true, + align: "left", + }, + { + field: "parentId", + title: "鐖剁骇ID", + type: "int", + width: 90, + require: true, + align: "left", + }, + { + field: "config", + title: "閰嶇疆椤�", + type: "string", + width: 300, + hidden: true, + align: "left", + }, + { + field: "dbSql", + title: "sql璇彞", + type: "string", + width: 200, + align: "left", + }, + { + field: "dBServer", + title: "DBServer", + type: "string", + width: 90, + hidden: true, + align: "left", + }, + { + field: "orderNo", + title: "鎺掑簭鍙�", + type: "int", + width: 90, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 90, + align: "left", + }, + { + field: "enable", + title: "鏄惁鍚敤", + type: "byte", + bind: { key: "enable", data: [] }, + width: 90, + require: true, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 130, + hidden: true, + readonly: true, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 150, + readonly: true, + align: "left", + sort: true, + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 130, + hidden: true, + readonly: true, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 150, + readonly: true, + align: "left", + sort: true, + }, + ]); + const detail = ref({ + cnName: "瀛楀吀鏄庣粏", + table: "Sys_DictionaryList", + columns: [ + { + field: "dicList_ID", + title: "DicList_ID", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "dicId", + title: "鏁版嵁婧怚D", + type: "int", + width: 90, + readonly: true, + align: "left", + sort: true, + }, + { + field: "dicValue", + title: "鏁版嵁婧怴alue", + type: "string", + width: 90, + edit: { type: "text" }, + align: "left", + }, + { + field: "dicName", + title: "鏁版嵁婧怲ext", + type: "string", + width: 90, + edit: { type: "text" }, + align: "left", + }, + { + field: "orderNo", + title: "鎺掑簭鍙�", + type: "int", + width: 90, + edit: { type: "text" }, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 90, + edit: { type: "text" }, + align: "left", + }, + + { + field: "createID", + title: "CreateID", + type: "int", + width: 90, + hidden: true, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 130, + readonly: true, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 90, + readonly: true, + align: "left", + sort: true, + }, + + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 130, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 90, + align: "left", + sort: true, + }, + ], + sortName: "dicId", + key: "dicId", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue" new file mode 100644 index 0000000..02427fb --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue" @@ -0,0 +1,65 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/Sys_DictionaryList.js姝ゅ缂栧啓 + --> +<template> + <div> + <view-grid :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend"> + </view-grid> + </div> +</template> + +<script> + import extend from "@/extension/system/Sys_DictionaryList.js"; + var vueParam = { + data() { + return { + table: { + key: 'DicList_ID', + footer: "Foots", + cnName: '瀛楀吀鏄庣粏', + name: 'Sys_DictionaryList', + url: "/Sys_DictionaryList/", + sortName: "DicList_ID" + }, + extend: extend, + editFormFields: {"DicValue":"","DicName":"","OrderNo":"","Remark":"","Enable":""}, + editFormOptions: [[{"columnType":"string","title":"鏁版嵁婧怴alue","field":"DicValue","type":"text"}, + {"columnType":"string","title":"鏁版嵁婧怲ext","field":"DicName","type":"text"}, + {"columnType":"int","title":"鎺掑簭鍙�","field":"OrderNo","type":"text"}, + {"columnType":"int","title":"澶囨敞","field":"Remark","type":"text"}, + {"columnType":"byte","dataKey":"enable","title":"鏄惁鍙敤","field":"Enable","type":"switch"}]], + searchFormFields: {}, + searchFormOptions: [], + columns: [{field:'DicList_ID',title:'DicList_ID',type:'int',width:90,hidden:true,readonly:true,require:true,align:'left'}, + {field:'Dic_ID',title:'鏁版嵁婧怚D',type:'int',width:90,readonly:true,align:'left',sortable:true}, + {field:'DicValue',title:'鏁版嵁婧怴alue',type:'string',width:90,align:'left'}, + {field:'DicName',title:'鏁版嵁婧怲ext',type:'string',width:90,align:'left'}, + {field:'OrderNo',title:'鎺掑簭鍙�',type:'int',width:90,align:'left'}, + {field:'Remark',title:'澶囨敞',type:'int',width:90,align:'left'}, + {field:'Enable',title:'鏄惁鍙敤',type:'byte',bind:{ key:'enable',data:[]},width:90,align:'left'}, + {field:'CreateID',title:'CreateID',type:'int',width:90,hidden:true,align:'left'}, + {field:'Creator',title:'鍒涘缓浜�',type:'string',width:130,readonly:true,align:'left'}, + {field:'CreateDate',title:'鍒涘缓鏃堕棿',type:'datetime',width:90,readonly:true,align:'left',sortable:true}, + {field:'ModifyID',title:'ModifyID',type:'int',width:90,hidden:true,align:'left'}, + {field:'Modifier',title:'淇敼浜�',type:'string',width:130,align:'left'}, + {field:'ModifyDate',title:'淇敼鏃堕棿',type:'datetime',width:90,align:'left',sortable:true}], + detail: { + cnName:"#detailCnName", + columns: [], + sortName: "#detailSortName" + } + }; + } + }; + export default vueParam; +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue" new file mode 100644 index 0000000..9742628 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue" @@ -0,0 +1,70 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/Sys_Log.js姝ゅ缂栧啓 + --> +<template> + <view-grid ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend"> + </view-grid> +</template> +<script> + import extend from "@/extension/system/Sys_Log.js"; + import { ref, defineComponent } from "vue"; + export default defineComponent({ + setup() { + const table = ref({ + key: 'Id', + footer: "Foots", + cnName: '绯荤粺鏃ュ織', + name: 'Sys_Log', + url: "/Sys_Log/", + sortName: "Id" + }); + const editFormFields = ref({}); + const editFormOptions = ref([]); + const searchFormFields = ref({"BeginDate":"","Url":"","LogType":[],"Success":[],"UserIP":"","ServiceIP":"","Role_Id":""}); + const searchFormOptions = ref([[{"title":"璇锋眰鍦板潃","field":"Url","type":"text"},{"title":"鐢ㄦ埛IP","field":"UserIP","type":"text"},{"title":"鏈嶅姟鍣↖P","field":"ServiceIP","type":"text"}],[{"title":"寮�濮嬫椂闂�","field":"BeginDate","type":"datetime"},{"dataKey":"restatus","data":[],"title":"鍝嶅簲鐘舵��","field":"Success","type":"selectList"},{"dataKey":"roles","data":[],"title":"瑙掕壊ID","field":"Role_Id","type":"select"}],[{"dataKey":"log","data":[],"title":"鏃ュ織绫诲瀷","field":"LogType","colSize":12,"type":"checkbox"}]]); + const columns = ref([{field:'Id',title:'Id',type:'int',width:90,hidden:true,readonly:true,require:true,align:'left'}, + {field:'BeginDate',title:'寮�濮嬫椂闂�',type:'datetime',width:140,align:'left',sortable:true}, + {field:'UserName',title:'鐢ㄦ埛鍚嶇О',type:'string',width:90,align:'left'}, + {field:'Url',title:'璇锋眰鍦板潃',type:'string',width:110,align:'left'}, + {field:'LogType',title:'鏃ュ織绫诲瀷',type:'string',bind:{ key:'log',data:[]},width:80,align:'left'}, + {field:'Success',title:'鍝嶅簲鐘舵��',type:'int',bind:{ key:'restatus',data:[]},width:80,align:'left'}, + {field:'ElapsedTime',title:'鏃堕暱',type:'int',width:60,align:'left'}, + {field:'RequestParameter',title:'璇锋眰鍙傛暟',type:'string',width:70,align:'left'}, + {field:'ResponseParameter',title:'鍝嶅簲鍙傛暟',type:'string',width:70,align:'left'}, + {field:'ExceptionInfo',title:'寮傚父淇℃伅',type:'string',width:70,align:'left'}, + {field:'UserIP',title:'鐢ㄦ埛IP',type:'string',width:90,align:'left'}, + {field:'ServiceIP',title:'鏈嶅姟鍣↖P',type:'string',width:90,hidden:true,align:'left'}, + {field:'BrowserType',title:'娴忚鍣ㄧ被鍨�',type:'string',width:90,align:'left'}, + {field:'User_Id',title:'鐢ㄦ埛ID',type:'int',width:90,hidden:true,align:'left'}, + {field:'Role_Id',title:'瑙掕壊ID',type:'int',bind:{ key:'roles',data:[]},width:90,hidden:true,align:'left'}, + {field:'EndDate',title:'缁撴潫鏃堕棿',type:'datetime',width:150,hidden:true,align:'left',sortable:true}]); + const detail = ref({ + cnName: "#detailCnName", + columns: [], + sortName: "", + key: "" + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, + }); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue" new file mode 100644 index 0000000..93feed5 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue" @@ -0,0 +1,618 @@ +<template> + <div class="menu-container"> + <!-- <el-input/> --> + <vol-box :width="940" :mask="true" :height="500" title="鍥炬爣鍒楄〃" v-model="model"> + <Icons :onSelect="onSelect"></Icons> + <template #footer> + <el-button type="primary" size="mini" @click="model = false">纭� 璁�</el-button> + </template> + </vol-box> + <vol-box :width="600" :mask="true" :height="270" title="鍏朵粬鏉冮檺" v-model="actionModel"> + <vol-form ref="actionForm" :formRules="actionOptions" :formFields="actionFields"> + <template #header> + <div> + <el-alert show-icon type="success"> + 閰嶇疆鐨勫叾浠栨潈闄� + <br />1銆佹坊鍔犳柊鐨勬潈闄愬悗璇峰湪vue椤圭洰涓璫onfig鏂囦欢澶逛笅buttns.js娣诲姞姝ゆ潈闄愮殑鎸夐挳銆� + <br />2銆佸鏋滄潈闄愬彧鍦ㄦ煇灏戞暟鍑犱釜鍔熻兘涓娇鐢�,鍦╲ue鐨勫搴旈〉闈㈡墿灞昬xtension鏂囦欢鎵惧埌瀵瑰簲js,娣诲姞鍒癳l-buttons瀵硅薄涓�,鏍煎紡鍚宑onfig鏂囦欢澶逛笅buttns.js涓�鏍枫�� + </el-alert> + </div> + </template> + </vol-form> + <template #footer> + <el-button type="primary" size="mini" @click="otherAction">纭� 璁�</el-button> + </template> + </vol-box> + + <!-- v-if="tree.length" --> + <div class="menu-left"> + <div class="m-title"><i class="el-icon-warning-outline"></i>鑿滃崟鍒楄〃</div> + <el-scrollbar style="height: 100%; width: 200px"> + <VolMenu :onSelect="getTreeItem" :list="tree" :isCollapse="false"></VolMenu> + </el-scrollbar> + </div> + <div class="menu-right"> + <el-scrollbar style="height: 100%"> + <el-alert title="鑿滃崟閰嶇疆璇存槑" type="warning" :closable="false" show-icon> + <div> + 1銆佸鏋滄槸鐢ㄤ唬鐮佺敓鍣ㄧ敓鎴愮殑Vue椤甸潰,Url涓篤ue椤圭洰涓璼rc->router->viewGrid.js瀵瑰簲琛ㄥ悕鐨刾ath灞炴�� + </div> + <div style="padding-top: 5px"> + 2銆� 濡傛灉鍙槸寤轰竴绾ц彍鍗曟垨绌鸿彍鍗晆rl涓嶇敤濉啓,銆愯鍥�/琛ㄥ悕銆戝~鍐�.鎴栬��/ + </div> + </el-alert> + <div style="padding: 0px 30px 0 20px"> + <vol-form class="form-content" ref="form" :formRules="options" :formFields="fields"> + </vol-form> + <div> + <div class="auth-group"> + <label style="width: 100px">鏉冮檺鎸夐挳锛�</label> + <div class="ck"> + <el-checkbox-group v-model="actions"> + <el-checkbox v-for="(item, index) in action" :key="index" :label="item.value">{{ item.text + "(" + + item.value + ")" }}</el-checkbox> + </el-checkbox-group> + </div> + </div> + </div> + <div style="padding-left: 100px"> + <el-button @click="handleCheckAll" size="mini" type="success" plain><i class="el-icon-check"></i>鍏� + 閫�</el-button> + <el-button @click="actionModel = true" size="mini" type="primary" plain><i + class="el-icon-plus"></i>鍏朵粬鏉冮檺</el-button> + </div> + <div class="m-btn"> + <el-button type="primary" @click="save"><i class="el-icon-check"></i>淇濆瓨</el-button> + <el-button type="success" @click="add"><i class="el-icon-plus"></i>鏂板缓</el-button> + <el-button type="warning" @click="addChild"><i class="el-icon-plus"></i>娣诲姞瀛愮骇</el-button> + <el-button type="primary" plain @click="addBrother"><i class="el-icon-circle-plus"></i> 娣诲姞鍚岀骇</el-button> + <el-button type="warning" plain @click="delMenu"><i class="el-icon-delete"></i> 鍒犻櫎鑿滃崟</el-button> + </div> + </div> + </el-scrollbar> + </div> + </div> +</template> +<script> +import VolForm from "@/components/basic/VolForm.vue"; +import VolBox from "@/components/basic/VolBox.vue"; +import Icons from "@/components/basic/Icons.vue"; +import VolMenu from "@/components/basic/VolElementMenu.vue"; +import { + defineComponent, + reactive, + ref, + toRefs, + onMounted, + h, + resolveComponent, +} from "vue"; + +import http from "@/api/http"; +export default defineComponent({ + components: { + VolForm: VolForm, + VolBox: VolBox, + Icons: Icons, + VolMenu, + }, + methods: { + otherAction() { + this.$refs.actionForm.validate(() => { + let exist = this.action.some((x) => { + return ( + x.text == this.actionFields.name || + x.value == this.actionFields.value + ); + }); + if (exist) { + return this.$message.error("鏉冮檺鍚嶇О鎴栨潈闄愬�煎凡瀛樺湪"); + } + this.actionModel = false; + this.action.push({ + text: this.actionFields.name, + value: this.actionFields.value, + }); + }); + }, + handleCheckAll() { + if (this.actions == this.action.length) { + this.checkAll = false; + } else { + this.checkAll = !this.checkAll; + } + if (this.checkAll) { + this.actions = this.action.map((x) => { + return x.value; + }); + } else { + this.actions = []; + } + }, + checkAllGroupChange(data) { + if (data.length === this.action.length) { + this.checkAll = true; + } else if (data.length > 0) { + this.checkAll = false; + } else { + this.checkAll = false; + } + }, + add(obj) { + this.$refs.form.reset( + Object.assign({ enable: 1 }, obj || { parentId: 0 }) + ); + this.icon = ""; + // this.actions = []; + //2020.08.07鏂板缓鑿滃崟鏃讹紝榛樿閫変腑鏌ヨ鎸夐挳鏉冮檺 + this.actions = ["Search"]; + }, + addChild() { + if (!this.isSelect()) return; + this.add({ parentId: this.fields.menuId }); + }, + addBrother() { + if (!this.isSelect()) return; + this.add({ parentId: this.fields.parentId }); + }, + delMenu() { + //2020.08.07澧炲姞鑿滃崟鍒犻櫎鍔熻兘 + if (this.fields.menuId == 0) { + return this.$Message.error("璇烽�夋嫨鑿滃崟"); + } + + let tigger = false; + this.$confirm( + "纭瑕佸垹闄ゃ��" + this.fields.menuName + "銆戣彍鍗曞悧锛�", + "璀﹀憡", + { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + center: true, + } + ).then(() => { + if (tigger) return; + tigger = true; + let menuId = this.fields.menuId; + this.http + .post("/api/Sys_Menu/delMenu?menuId=" + menuId, {}, "姝e湪鍒犻櫎鏁版嵁....") + .then((x) => { + if (!x.status) return this.$Message.error(x.message); + this.$refs.form.reset(); + this.$Message.info(x.message); + this.initTree(); + }); + }); + }, + save() { + this.$refs.form.validate(() => { + this.fields.auth = ""; + if (this.actions) { + this.fields.auth = this.action.filter((x) => { + return this.actions.indexOf(x.value) != -1; + }); + } + if ( + this.fields.auth && + this.fields.auth instanceof Array && + this.fields.auth.length > 0 + ) { + this.fields.auth = JSON.stringify(this.fields.auth); + } else { + this.fields.auth = ""; + } + this.http.post("/api/Sys_Menu/save", this.fields, true).then((x) => { + if (!x.status) { + this.$Message.error(x.message); + return; + } + + this.$Message.info(x.message); + if (this.fields.menuId) { + this.tree.forEach((t) => { + if (t.id == this.fields.menuId) { + t.name = this.fields.menuName; + t.orderNo = this.fields.orderNo; + t.parentId = this.fields.parentId; + } + }); + return; + } + this.fields.menuId = x.data.menuId; + this.fields.createDate = x.data.createDate; + this.tree.push({ + id: x.data.menuId, + name: this.fields.menuName, + orderNo: this.fields.orderNo, + parentId: this.fields.parentId, + }); + console.log(this.tree) + }); + }); + }, + isSelect() { + let id = this.fields.menuId; + if (!id) { + this.$message.error("璇烽�夋嫨鑺傜偣"); + return false; + } + return true; + }, + onSelect(icon) { + this.fields.icon = icon; + this.$message.info(icon); + }, + onOpenChange(node) { + if (node.length == 0) return; + this.getTreeItem(node[node.length > 1 ? node.length - 1 : 0]); + } + }, + created(){ + + }, + setup() { + const tree = ref([]); + const actionValues = ref([]); + const action = ref([ + { text: "鏌ヨ", value: "Search" }, + { text: "鏂板缓", value: "Add" }, + { text: "鍒犻櫎", value: "Delete" }, + { text: "缂栬緫", value: "Update" }, + { text: "瀵煎叆", value: "Import" }, + { text: "瀵煎嚭", value: "Export" }, + // { text: "涓婁紶", value: "Upload" }, + // { text: "瀹℃牳", value: "Audit" }, + ]); + const actions = ref([]); + actionValues.value = action.value.map((x) => { + return x.value; + }); + const initTree = () => { + http.post("/api/Sys_Menu/getMenu", {}, true).then((x) => { + x.forEach(item => { + item.icon = 'el-icon-menu'; + }) + + tree.value = x; + }); + }; + onMounted(() => { + initTree(); + }); + const actionModel = ref(false); + const checkAll = ref(false); + const model = ref(false); + + const fields = ref({ + menuId: 0, + parentId: 0, + menuName: "", + tableName: "", + url: "", + auth: "", + icon: "", + orderNo: "", + enable: 1, + createDate: "", + creator: "", + modifyDate: "", + }); + + const actionFields = ref({ + name: "", + value: "", + }); + const actionOptions = ref([ + [ + { + title: "鏉冮檺鍚嶇О", + field: "name", + placeholder: "鏉冮檺鍚嶇О,濡傦細鏂板", + required: true, + }, + ], + [ + { + title: "鏉� 闄� 鍊�", + field: "value", + placeholder: "鏉冮檺鍊�,濡傦細Add", + required: true, + }, + ], + ]); + + const options = ref([ + [ + { + title: "鑿� 鍗� ID", + field: "menuId", + placeholder: "鑿滃崟ID", + min: 0, + disabled: true, + }, + { + title: "鐖� 绾� ID", + required: true, + type: "number", + min: 0, + field: "parentId", + // min: 0, max: 50 + }, + { + title: "鑿滃崟鍚嶇О", + field: "menuName", + required: true, + }, + ], + [ + { + title: "瑙嗗浘/琛ㄥ悕", + field: "tableName", + placeholder: "涓庝唬鐮佺敓鎴愬櫒浣跨敤鐨勫悕绉扮浉鍚�", + required: true, + }, + { + title: "(璺敱)Url", + field: "url", + placeholder: "瑙�:涓婇潰鑿滃崟閰嶇疆璇存槑", + }, + { + title: "鎺掑簭鍙�", + field: "orderNo", + type: "number", + min: 0, + placeholder: "鍊艰秺澶ф樉绀鸿秺闈犲墠", + required: true, + }, + ], + [ + { + title: "鏄惁鍚敤", + field: "enable", + required: true, + type: "select", + colSize: 4, + data: [ + { key: 1, value: "鍚敤" }, + { key: 2, value: "鍚敤涓嶆樉绀�" }, + { key: 0, value: "绂佺敤" }, + ], + }, + // { + // // 2022.03.26澧炵Щ鍔ㄧ鍔犺彍鍗曠被鍨� + // title: "鑿滃崟绫诲瀷", + // field: "menuType", + // required: true, + // type: "select", + // colSize: 4, + // data: [ + // { key: 1, value: "WCS鑿滃崟" }, + // { key: 0, value: "WMS鑿滃崟" }, + // { key: 99, value: "鍏辩敤" } + // ], + // }, + { + title: "鍥炬爣Icon", + field: "icon", + render: (h) => { + return h("div", {}, [ + h("i", { + style: { + "font-size": "25px", + margin: "0px 9px", + position: "relative", + top: "4px", + }, + class: [fields.value.icon], + }), + h( + resolveComponent("el-button"), + { + size: "small", + style: { padding: "0px 9px" }, + type: "primary", + plain: true, + onClick: () => { + model.value = true; + }, + }, + "閫夋嫨鍥炬爣" + ), + ]); + }, + }, + ], + ]); + const refForm = ref(); + const getTreeItem = (node) => { + http.post("api/Sys_Menu/getTreeItem?menuId=" + node, {}, true).then((x) => { + try { + fields.value.icon = x.icon; + if (x.auth) { + x.auth = JSON.parse(x.auth); + action.value.splice(8, action.value.length); + + actions.value = x.auth.map((element) => { + if (actionValues.value.indexOf(element.value) == -1) { + action.value.push(element); + } + return element.value; + }); + } else { + action.value.splice(8, action.value.length); + x.auth = []; + fields.value.icon = ""; + actions.value = []; + } + } catch (error) { + console.log("鑿滃崟鍔熻兘鏉冮檺杞崲鎴怞SON澶辫触:" + x.auth); + x.auth = []; + // this.icon = ""; + actions.value = []; + } + refForm.value.reset(x); + }); + }; + return { + tree, + initTree, + action, + actions, + actionValues, + actionModel, + checkAll, + model, + fields, + actionFields, + actionOptions, + options, + form: refForm, + getTreeItem + }; + }, + data() { + return {}; + }, +}); +</script> + +<style lang="less" scoped> +.on-icon { + line-height: 20px; + position: relative; + + .remove { + display: none; + color: red; + right: 7px; + position: absolute; + top: -14px; + font-size: 13px; + } +} + +.on-icon:hover { + cursor: pointer; + + .remove { + display: block; + } +} + +.action { + width: 100%; + display: flex; + + margin-bottom: 15px; + + .ivu-checkbox-wrapper { + margin-right: 20px; + } + + .ck { + line-height: 33px; + display: inline-block; + display: flex; + + label:first-child { + min-width: 58px; + float: left; + margin-top: 1px; + } + + >div { + float: left; + } + } +} + +.menu-container { + display: flex; + position: absolute; + width: 100%; + height: 100%; + padding: 8px; + background: #f7f7f7; + + .menu-left { + height: 100%; + width: 201px; + border: 1px solid #eee; + display: flex; + background: white; + flex-direction: column; + + .module-name { + border-radius: 0px; + /* height: 5%; */ + line-height: 21px; + margin-bottom: 0; + } + } + + .menu-right { + flex: 1; + border-radius: 3px; + border: 1px solid #eee; + background: white; + margin-left: 9px; + margin-right: 3px; + } +} + +.m-btn { + margin-top: 20px; + text-align: center; +} + +.m-title { + line-height: 40px; + font-size: 15px; + background: #66b1ff0f; + font-weight: bold; + padding: 6px 16px; + border-bottom: 1px solid #eee; + + i { + padding-right: 5px; + } +} + +.form-content { + margin-top: 30px; +} + +.menu-left ::v-deep(.el-scrollbar__bar.is-vertical) { + width: 2px; +} + +.auth-group { + display: flex; + + label { + display: inline-block; + width: 100px; + text-align: right; + color: #797979; + font-size: 14px; + } + + .ck { + flex: 1; + } + + .el-checkbox { + min-width: 105px; + width: auto !important; + margin-right: 5px; + display: inline-block; + padding-bottom: 10px; + } +} + +.auth-group ::v-deep(.el-checkbox__label) { + padding-left: 4px; +} +</style> + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue" new file mode 100644 index 0000000..538b0a7 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue" @@ -0,0 +1,217 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/Sys_Role.js姝ゅ缂栧啓 + --> +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> +<script> +import extend from "@/extension/system/Sys_Role.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "roleId", + footer: "Foots", + cnName: "瑙掕壊绠$悊", + name: "Sys_Role", + url: "/Sys_Role/", + sortName: "roleId", + }); + const editFormFields = ref({ + parentId: [], + roleName: "", + deptId: "", + enable: "", + // creater: "", + // createDate: "", + // modifier: "", + // modifyDate: "", + }); + const editFormOptions = ref([ + [ + { + dataKey: "tree_roles", + data: [], + title: "鐖剁骇ID", + required: true, + field: "parentId", + type: "cascader", + }, + { title: "瑙掕壊鍚嶇О", required: true, field: "roleName" }, + ], + [ + { title: "閮ㄩ棬ID", field: "deptId" }, + { + dataKey: "enable", + data: [], + title: "鏄惁鍚敤", + field: "enable", + type: "switch", + }, + ], + // [ + // { title: "鍒涘缓浜�", field: "creater", disabled: true }, + // { + // title: "鍒涘缓鏃堕棿", + // field: "createDate", + // disabled: true, + // type: "datetime", + // }, + // ], + // [ + // { title: "淇敼浜�", field: "modifier", disabled: true }, + // { title: "淇敼鏃堕棿", field: "modifyDate", disabled: true }, + // ], + ]); + const searchFormFields = ref({ + roleName: "", + deptId: "", + enable: "", + createDate: "", + modifyDate: "", + }); + const searchFormOptions = ref([ + [ + { title: "瑙掕壊鍚嶇О", field: "roleName", type: "text" }, + { title: "閮ㄩ棬ID", field: "deptId", type: "text" }, + { + dataKey: "enable", + data: [], + title: "鏄惁鍚敤", + field: "enable", + type: "select", + }, + ], + [ + { title: "鍒涘缓鏃堕棿", field: "createDate", type: "datetime" }, + { title: "淇敼鏃堕棿", field: "modifyDate", type: "datetime" }, + ], + ]); + const columns = ref([ + { + field: "roleId", + title: "瑙掕壊ID", + type: "int", + width: 70, + readonly: true, + require: true, + align: "left", + sortable: true, + }, + { + field: "parentId", + title: "鐖剁骇ID", + type: "int", + bind: { key: "tree_roles", data: [] }, + width: 70, + require: true, + align: "left", + }, + { + field: "roleName", + title: "瑙掕壊鍚嶇О", + type: "string", + link: true, + width: 90, + require: true, + align: "left", + }, + { + field: "deptId", + title: "閮ㄩ棬ID", + type: "int", + width: 90, + // hidden: true, + align: "left", + }, + // { + // field: "deptName", + // title: "閮ㄩ棬鍚嶇О", + // type: "string", + // width: 90, + // align: "left", + // }, + { + field: "enable", + title: "鏄惁鍚敤", + type: "byte", + bind: { key: "enable", data: [] }, + width: 90, + align: "left", + }, + { + field: "orderNo", + title: "鎺掑簭", + type: "int", + width: 90, + hidden: true, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 130, + readonly: true, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 90, + readonly: true, + align: "left", + sortable: true, + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 130, + readonly: true, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 90, + readonly: true, + align: "left", + sortable: true, + }, + ]); + const detail = ref({ + cnName: "#detailCnName", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue" new file mode 100644 index 0000000..19ed48a --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue" @@ -0,0 +1,72 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/Sys_Role.js姝ゅ缂栧啓 + --> +<template> + <view-grid ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend"> + </view-grid> +</template> +<script> + import extend from "@/extension/system/Sys_Role1.js"; + import { ref, defineComponent } from "vue"; + export default defineComponent({ + setup() { + const table = ref({ + key: 'Role_Id', + footer: "Foots", + cnName: '瑙掕壊绠$悊', + name: 'Sys_Role', + url: "/Sys_Role/", + sortName: "Role_Id" + }); + const editFormFields = ref({"ParentId":[],"RoleName":"","DeptName":"","Enable":"","Creator":"","CreateDate":"","Modifier":"","ModifyDate":""}); + const editFormOptions = ref([[{"dataKey":"tree_roles","data":[],"title":"鐖剁骇ID","required":true,"field":"ParentId","type":"cascader"}, + {"title":"瑙掕壊鍚嶇О","required":true,"field":"RoleName"}], + [{"title":"閮ㄩ棬鍚嶇О","field":"DeptName"}, + {"dataKey":"enable","data":[],"title":"鏄惁鍚敤","field":"Enable","type":"switch"}], + [{"title":"鍒涘缓浜�","field":"Creator","disabled":true}, + {"title":"鍒涘缓鏃堕棿","field":"CreateDate","disabled":true,"type":"datetime"}], + [{"title":"淇敼浜�","field":"Modifier","disabled":true}, + {"title":"淇敼鏃堕棿","field":"ModifyDate","disabled":true}]]); + const searchFormFields = ref({"RoleName":"","DeptName":"","Enable":"","CreateDate":"","ModifyDate":""}); + const searchFormOptions = ref([[{"title":"瑙掕壊鍚嶇О","field":"RoleName","type":"text"},{"title":"閮ㄩ棬鍚嶇О","field":"DeptName","type":"text"},{"dataKey":"enable","data":[],"title":"鏄惁鍚敤","field":"Enable","type":"select"}],[{"title":"鍒涘缓鏃堕棿","field":"CreateDate","type":"datetime"},{"title":"淇敼鏃堕棿","field":"ModifyDate","type":"datetime"}]]); + const columns = ref([{field:'Role_Id',title:'瑙掕壊ID',type:'int',width:70,readonly:true,require:true,align:'left',sortable:true}, + // {field:'ParentId',title:'鐖剁骇ID',type:'int',bind:{ key:'tree_roles',data:[]},width:70,require:true,align:'left'}, + {field:'RoleName',title:'瑙掕壊鍚嶇О',type:'string',link:true,width:90,require:true,align:'left'}, + {field:'Dept_Id',title:'閮ㄩ棬ID',type:'int',width:90,hidden:true,align:'left'}, + {field:'DeptName',title:'閮ㄩ棬鍚嶇О',type:'string',width:90,align:'left'}, + {field:'Enable',title:'鏄惁鍚敤',type:'byte',bind:{ key:'enable',data:[]},width:90,align:'left'}, + {field:'OrderNo',title:'鎺掑簭',type:'int',width:90,hidden:true,align:'left'}, + {field:'Creator',title:'鍒涘缓浜�',type:'string',width:130,readonly:true,align:'left'}, + {field:'CreateDate',title:'鍒涘缓鏃堕棿',type:'datetime',width:90,readonly:true,align:'left',sortable:true}, + {field:'Modifier',title:'淇敼浜�',type:'string',width:130,readonly:true,align:'left'}, + {field:'ModifyDate',title:'淇敼鏃堕棿',type:'datetime',width:90,readonly:true,align:'left',sortable:true}]); + const detail = ref({ + cnName: "#detailCnName", + columns: [], + sortName: "", + key: "" + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, + }); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue" new file mode 100644 index 0000000..21530cd --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue" @@ -0,0 +1,369 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> +<script> +import extend from "@/extension/system/Sys_User.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "user_Id", + footer: "Foots", + cnName: "鐢ㄦ埛绠$悊", + name: "Sys_User", + url: "/User/", + sortName: "user_Id", + }); + const editFormFields = ref({ + systemType: "0", + userName: "", + userTrueName: "", + role_Id: [], + deptIds: "", + enable: "", + gender: "", + remark: "", + headImageUrl: "", + }); + const editFormOptions = ref([ + [{ title: "甯愬彿", required: true, field: "userName"}], + [{ title: "濮撳悕", required: true, field: "userTrueName", type: "text" }], + [ + { + dataKey: "tree_roles", + data: [], + title: "瑙掕壊", + required: true, + field: "role_Id", + type: "cascader", + }, + ], + [ + { + dataKey: "缁勭粐鏈烘瀯", + data: [], + title: "缁勭粐鏋勬灦", + field: "deptIds", + colSize: 12, + type: "treeSelect", + }, + ], + [ + { + dataKey: "enable", + data: [], + title: "鏄惁鍙敤", + required: true, + field: "enable", + type: "select", + }, + ], + [ + { + dataKey: "gender", + data: [], + title: "鎬у埆", + field: "gender", + type: "select", + }, + ], + [{ title: "澶囨敞", field: "remark", colSize: 12, type: "textarea" }], + [{ title: "澶村儚", field: "headImageUrl", type: "img" }], + ]); + const searchFormFields = ref({ + systemType: "0", + userName: "", + userTrueName: "", + gender: "", + deptName: "", + role_Id: [], + token: "", + appType: [], + createDate: "", + isRegregisterPhone: "", + phoneNo: "", + enable: "", + lastLoginDate: "", + address: "", + email: "", + }); + const searchFormOptions = ref([ + [ + { title: "甯愬彿", field: "userName" }, + { title: "濮撳悕", field: "userTrueName" }, + { + dataKey: "gender", + data: [], + title: "鎬у埆", + field: "gender", + type: "select", + }, + ], + [ + { + dataKey: "enable", + data: [], + title: "鏄惁鍙敤", + field: "enable", + type: "select", + }, + { title: "娉ㄥ唽鏃堕棿", field: "createDate", type: "datetime" }, + ], + ]); + const columns = ref([ + { + field: "user_Id", + title: "User_Id", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "userName", + title: "甯愬彿", + type: "string", + link: true, + width: 120, + readonly: true, + require: true, + align: "left", + sort: true, + }, + { + field: "userTrueName", + title: "濮撳悕", + type: "string", + width: 120, + require: true, + align: "left", + }, + { + field: "gender", + title: "鎬у埆", + type: "int", + bind: { key: "gender", data: [] }, + width: 80, + align: "left", + }, + { + field: "headImageUrl", + title: "澶村儚", + type: "img", + width: 90, + align: "left", + }, + { + field: "dept_Id", + title: "涓嶇敤", + type: "int", + width: 90, + hidden: true, + align: "left", + }, + { + field: "deptName", + title: "涓嶇敤", + type: "string", + width: 150, + hidden: true, + align: "left", + }, + { + field: "role_Id", + title: "瑙掕壊", + type: "int", + bind: { key: "tree_roles", data: [] }, + width: 130, + require: true, + align: "left", + }, + { + field: "roleName", + title: "涓嶇敤", + type: "string", + width: 90, + hidden: true, + align: "left", + }, + { + field: "token", + title: "Token", + type: "string", + width: 180, + hidden: true, + align: "left", + }, + { + field: "appType", + title: "绫诲瀷", + type: "int", + bind: { key: "ut", data: [] }, + width: 150, + hidden: true, + align: "left", + }, + { + field: "deptIds", + title: "缁勭粐鏋勬灦", + type: "string", + bind: { key: "缁勭粐鏈烘瀯", data: [] }, + width: 140, + hidden: true, + align: "left", + }, + { + field: "createDate", + title: "娉ㄥ唽鏃堕棿", + type: "datetime", + width: 150, + readonly: true, + align: "left", + sort: true, + }, + { + field: "phoneNo", + title: "鎵嬫満鍙�", + type: "string", + width: 150, + hidden: true, + align: "left", + }, + { + field: "tel", + title: "Tel", + type: "string", + width: 90, + hidden: true, + align: "left", + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 130, + readonly: true, + align: "left", + }, + { + field: "enable", + title: "鏄惁鍙敤", + type: "byte", + bind: { key: "enable", data: [] }, + width: 90, + require: true, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 130, + hidden: true, + readonly: true, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 90, + hidden: true, + readonly: true, + align: "left", + sort: true, + }, + { + field: "auditStatus", + title: "瀹℃牳鐘舵��", + type: "int", + bind: { key: "audit", data: [] }, + width: 90, + hidden: true, + align: "left", + }, + { + field: "auditor", + title: "瀹℃牳浜�", + type: "string", + width: 90, + hidden: true, + align: "left", + }, + { + field: "auditDate", + title: "瀹℃牳鏃堕棿", + type: "datetime", + width: 150, + hidden: true, + align: "left", + sort: true, + }, + { + field: "lastModifyPwdDate", + title: "鏈�鍚庡瘑鐮佷慨鏀规椂闂�", + type: "datetime", + width: 150, + hidden: true, + align: "left", + sort: true, + }, + { + field: "address", + title: "鍦板潃", + type: "string", + width: 190, + hidden: true, + align: "left", + }, + { + field: "email", + title: "Email", + type: "string", + width: 140, + hidden: true, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 180, + hidden: true, + align: "left", + }, + ]); + const detail = ref({ + cnName: "#detailCnName", + table: "#detailTable", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue" new file mode 100644 index 0000000..32eb1d3 --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue" @@ -0,0 +1,317 @@ +<template> + <VolBox :width="500" :height="270" v-model="modifyOptions.model" title="淇敼瀵嗙爜"> + <div style="padding:10px;20px;"> + <VolForm ref="pwd" :formRules="modifyOptions.data" :formFields="modifyOptions.fields"></VolForm> + </div> + <template #footer> + <div style="text-align: center;"> + <el-button type="primary" size="mini" icon="md-checkmark-circle" long @click="savePwd">淇濆瓨</el-button> + </div> + </template> + </VolBox> + <div class="user-info"> + <div class="left"> + <div> + <img class="header-img" :src="userInfo.img" :onerror="errorImg" /> + <div class="text"> + <p class="name"> + <span style="font-size: 13px">{{ userInfo.userName }}</span> + </p> + <p class="date"> + <span>娉ㄥ唽鏃ユ湡锛歿{ userInfo.createDate }}</span> + </p> + <p> + <el-button type="error" @click="modifyPwd" size="small" icon="md-lock" long>淇敼瀵嗙爜</el-button> + <!-- <el-button + style="padding: 3px 16px" + @click="modifyImg" + type="primary" + size="mini" + ghost + >淇敼澶村儚</el-button + > --> + </p> + </div> + </div> + </div> + <div class="right"> + <vol-form ref="form" :load-key="true" :width="500" :formRules="editFormOptions" :formFields="editFormFields"> + <div class="footer"> + <!-- <el-button + style="margin-top: 2px" + type="primary" + size="small" + icon="md-checkmark-circle" + long + @click="modifyInfo" + >淇濆瓨</el-button + > --> + </div> + </vol-form> + </div> + + </div> +</template> +<script> +import VolForm from "@/components/basic/VolForm.vue"; +import VolBox from "@/components/basic/VolBox.vue"; +export default { + components: { + VolForm, + VolBox, + }, + methods: { + modifyImg() { + this.$message.info("淇敼澶村儚"); + }, + modifyEmail() { + this.$message.info("淇敼閭"); + }, + modifyPhone() { + this.$message.info("淇敼鐢佃瘽"); + }, + modifyPwd() { + this.modifyOptions.model = true; + }, + savePwd() { + if (!this.$refs.pwd.validate()) return; + if ( + this.modifyOptions.fields.newPwd != this.modifyOptions.fields.newPwd1 + ) { + return this.$message.error("涓ゆ瀵嗙爜涓嶄竴鑷�"); + } + let url = + "/api/User/modifyPwd?oldPwd=" + + this.modifyOptions.fields.oldPwd + + "&newPwd=" + + this.modifyOptions.fields.newPwd; + this.http.post(url, {}, true).then((x) => { + if (!x.status) { + return this.$message.error(x.message); + } + this.modifyOptions.model = false; + this.$Message.success(x.message); + this.$refs.pwd.reset(); + }); + }, + modifyInfo() { + this.$message.info("淇敼涓汉淇℃伅"); + }, + }, + created() { + this.http.post("/api/Sys_User/getCurrentUserInfo", {}, true).then((x) => { + if (!x.status) { + return this.$message(x.message); + } + x.data.createDate = (x.data.createDate || "").replace("T", " "); + x.data.gender = x.data.gender + ""; + this.$refs.form.reset(x.data); + this.userInfo.img = this.base.getImgSrc( + x.data.headImageUrl, + this.http.ipAddress + ); + this.userInfo.createDate = x.data.createDate; + this.userInfo.userName = x.data.userTrueName; + this.userInfo.phoneNo = x.data.phoneNo; + this.userInfo.email = x.data.email; + // this.editFormFields = x.data; + }); + }, + data() { + return { + errorImg: 'this.src="' + require("@/assets/imgs/error-img.png") + '"', + modifyOptions: { + model: false, + fields: { oldPwd: "", newPwd: "", newPwd1: "" }, + data: [ + [ + { + columnType: "string", + required: true, + title: "鏃у瘑鐮�", + field: "oldPwd", + }, + ], + [ + { + type: "password", + required: true, + title: "鏂板瘑鐮�", + field: "newPwd", + }, + ], + [ + { + type: "password", + required: true, + title: "纭瀵嗙爜", + field: "newPwd1", + }, + ], + ], + }, + binging: [{}], + userInfo: { + img: "", + createDate: "--", + userName: "--", + email: "", + phoneNo: "", + }, + editFormFields: { + roleName: "", + userName: "", + userTrueName: "", + address: "", + gender: "", + remark: "", + }, + editFormOptions: [ + [ + { + columnType: "string", + title: "鐢ㄦ埛鍚�", + field: "userName", + disabled: true, + }, + ], + [ + { + columnType: "string", + title: "瑙掕壊", + field: "roleName", + disabled: true, + type: "text", + }, + ], + [ + { + columnType: "string", + title: "鐪熷疄濮撳悕", + field: "userTrueName", + required: true, + disabled: true, + type: "text", + }, + ], + [ + { + columnType: "string", + title: "鍦板潃", + field: "address", + disabled: true, + type: "text", + }, + ], + [ + { + dataKey: "gender", + title: "鎬у埆", + field: "gender", + disabled: true, + data: [], + type: "select", + }, + ], + [ + { + columnType: "string", + title: "澶囨敞", + field: "remark", + disabled: true, + colSize: 12, + type: "textarea", + }, + ], + ], + }; + }, +}; +</script> +<style scoped> +.binding-group { + width: 100%; + padding-bottom: 20px; +} + +.binding-group>>>.ivu-cell-link { + text-align: left; +} + +.binding-group>>>.ivu-card-body { + padding: 0 16px; +} + +.binding-group>>>.ivu-cell-title { + line-height: 24px; + font-size: 12px; +} +</style> + +<style lang="less" scoped> +img[src=""], +img:not([src]) { + opacity: 0; +} + +.user-info { + box-shadow: #d6d6d6 0px 4px 21px; + position: absolute; + transform: translateY(-40%); + top: 40%; + /* position: relative; */ + margin: 0 auto; + left: 0; + width: 950px; + right: 0; + text-align: center; + padding: 0px; + padding: 20px; + + .text { + padding: 5px; + + .name { + font-weight: bolder; + font-size: 15px; + font-weight: 900; + } + + >p { + padding-top: 5px; + } + } + + .header-img { + height: 150px; + width: 150px; + border-radius: 50%; + margin-right: 0px; + top: 4px; + position: relative; + border: 3px solid #dfdfdf; + } + + >div { + float: left; + // height: 480px; + padding-top: 10px; + } + + .left { + width: 320px; + border-right: 1px solid #eee; + // box-shadow: #d6d6d6 7px 4px 20px; + // flex: 1; + } + + .right { + padding-left: 30px; + width: 570px; + // background: #fefefe; + // flex: 3; + } +} +</style> + + diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue" new file mode 100644 index 0000000..0ee20af --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue" @@ -0,0 +1,71 @@ +<!-- +*Author锛歫xx + *Contact锛�283591387@qq.com + *浠g爜鐢辨鏋剁敓鎴�,浠讳綍鏇存敼閮藉彲鑳藉鑷磋浠g爜鐢熸垚鍣ㄨ鐩� + *涓氬姟璇峰湪@/extension/system/system/Sys_Department.js姝ゅ缂栧啓 + --> +<template> + <view-grid ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend"> + </view-grid> +</template> +<script> + import extend from "@/extension/system/system/Sys_Department.js"; + import { ref, defineComponent } from "vue"; + export default defineComponent({ + setup() { + const table = ref({ + key: 'DepartmentId', + footer: "Foots", + cnName: '缁勭粐鏋舵瀯', + name: 'system/Sys_Department', + url: "/Sys_Department/", + sortName: "CreateDate" + }); + const editFormFields = ref({"DepartmentName":"","DepartmentCode":"","ParentId":[],"Remark":""}); + const editFormOptions = ref([[{"title":"缁勭粐鍚嶇О","required":true,"field":"DepartmentName"}], + [{"title":"缁勭粐缂栧彿","field":"DepartmentCode"}], + [{"dataKey":"缁勭粐鏈烘瀯","data":[],"title":"涓婄骇缁勭粐","field":"ParentId","type":"cascader"}], + [{"title":"澶囨敞","field":"Remark","type":"textarea"}]]); + const searchFormFields = ref({"DepartmentName":"","DepartmentCode":"","Creator":"","CreateDate":""}); + const searchFormOptions = ref([[{"title":"缁勭粐鍚嶇О","field":"DepartmentName","type":"like"},{"title":"缁勭粐缂栧彿","field":"DepartmentCode"},{"title":"鍒涘缓浜�","field":"Creator"},{"title":"鍒涘缓鏃堕棿","field":"CreateDate","type":"datetime"}]]); + const columns = ref([{field:'DepartmentId',title:'DepartmentId',type:'guid',width:110,hidden:true,readonly:true,require:true,align:'left'}, + {field:'DepartmentName',title:'缁勭粐鍚嶇О',type:'string',link:true,width:180,require:true,align:'left',sort:true}, + {field:'DepartmentCode',title:'缁勭粐缂栧彿',type:'string',width:90,align:'left'}, + {field:'ParentId',title:'涓婄骇缁勭粐',type:'guid',bind:{ key:'缁勭粐鏈烘瀯',data:[]},width:110,hidden:true,align:'left'}, + {field:'DepartmentType',title:'缁勭粐绫诲瀷',type:'string',width:110,hidden:true,align:'left'}, + {field:'Enable',title:'鏄惁鍙敤',type:'int',width:110,hidden:true,align:'left'}, + {field:'Remark',title:'澶囨敞',type:'string',width:100,align:'left'}, + {field:'CreateID',title:'CreateID',type:'int',width:100,hidden:true,align:'left'}, + {field:'Creator',title:'鍒涘缓浜�',type:'string',width:90,align:'left'}, + {field:'CreateDate',title:'鍒涘缓鏃堕棿',type:'datetime',width:145,align:'left',sort:true}, + {field:'ModifyID',title:'ModifyID',type:'int',width:100,hidden:true,align:'left'}, + {field:'Modifier',title:'淇敼浜�',type:'string',width:90,align:'left'}, + {field:'ModifyDate',title:'淇敼鏃堕棿',type:'datetime',width:140,align:'left',sort:true}]); + const detail = ref({ + cnName: "#detailCnName", + table: "#detailTable", + columns: [], + sortName: "", + key: "" + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, + }); +</script> diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/test.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/test.vue" new file mode 100644 index 0000000..64786be --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/system/test.vue" @@ -0,0 +1,18 @@ +<template> + +</template> + +<script> +export default { + props:{ + list: { + type: Array, + default: [], + }, + } +} +</script> + +<style> + +</style> \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue" new file mode 100644 index 0000000..1c1154f --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue" @@ -0,0 +1,236 @@ + +<template> + <view-grid + ref="grid" + :columns="columns" + :detail="detail" + :editFormFields="editFormFields" + :editFormOptions="editFormOptions" + :searchFormFields="searchFormFields" + :searchFormOptions="searchFormOptions" + :table="table" + :extend="extend" + > + </view-grid> +</template> + <script> +import extend from "@/extension/taskinfo/task.js"; +import { ref, defineComponent } from "vue"; +export default defineComponent({ + setup() { + const table = ref({ + key: "taskId", + footer: "Foots", + cnName: "浠诲姟淇℃伅", + name: "task", + url: "/Task/", + sortName: "CreateDate", + }); + const editFormFields = ref({}); + const editFormOptions = ref([]); + const searchFormFields = ref({ + taskNum: "", + palletCode: "", + roadway: "", + sourceAddress: "", + targetAddress: "", + currentAddress: "", + nextAddress: "", + creater: "", + createDate: "", + }); + const searchFormOptions = ref([ + [ + { title: "浠诲姟鍙�", field: "taskNum", type: "int" }, + { title: "鎵樼洏缂栧彿", field: "palletCode", type: "like" }, + { + title: "浠诲姟绫诲瀷", + field: "taskType", + type: "selectList", + dataKey: "taskType", + data: [], + }, + { + title: "浠诲姟鐘舵��", + field: "taskState", + type: "selectList", + dataKey: "taskState", + data: [], + }, + ], + [ + { title: "璧峰鍦板潃", field: "sourceAddress", type: "like" }, + { title: "鐩爣鍦板潃", field: "targetAddress", type: "like" }, + { title: "褰撳墠浣嶇疆", field: "currentAddress", type: "like" }, + { title: "涓嬩竴浣嶇疆", field: "nextAddress", type: "like" }, + ], + [ + { title: "宸烽亾鍙�", field: "roadway", type: "like" }, + { title: "鍒涘缓浜�", field: "creater", type: "like" }, + { title: "鍒涘缓鏃堕棿", field: "createDate", type: "datetime" }, + ], + ]); + const columns = ref([ + { + field: "taskId", + title: "TaskId", + type: "int", + width: 90, + hidden: true, + readonly: true, + require: true, + align: "left", + }, + { + field: "taskNum", + title: "浠诲姟鍙�", + type: "int", + width: 90, + align: "left", + }, + { + field: "palletCode", + title: "鎵樼洏缂栧彿", + type: "string", + width: 200, + align: "left", + }, + { + field: "roadway", + title: "宸烽亾鍙�", + type: "string", + width: 90, + align: "left", + }, + { + field: "taskType", + title: "浠诲姟绫诲瀷", + type: "int", + width: 90, + align: "left", + bind: { key: "taskType", data: [] }, + }, + { + field: "taskState", + title: "浠诲姟鐘舵��", + type: "int", + width: 150, + align: "left", + bind: { key: "taskState", data: [] }, + }, + { + field: "sourceAddress", + title: "璧峰鍦板潃", + type: "int", + width: 120, + align: "left", + }, + { + field: "targetAddress", + title: "鐩爣鍦板潃", + type: "string", + width: 120, + align: "left", + }, + { + field: "currentAddress", + title: "褰撳墠浣嶇疆", + type: "string", + width: 120, + align: "left", + }, + { + field: "nextAddress", + title: "涓嬩竴浣嶇疆", + type: "string", + width: 120, + align: "left", + }, + { + field: "exceptionMessage", + title: "寮傚父淇℃伅", + type: "string", + width: 90, + align: "left", + hidden: true, + }, + { + field: "grade", + title: "浼樺厛绾�", + type: "int", + width: 80, + align: "left", + }, + { + field: "dispatchertime", + title: "浠诲姟涓嬪彂鏃堕棿", + type: "datetime", + width: 150, + align: "left", + }, + { + field: "wMSId", + title: "WMS浠诲姟涓婚敭", + type: "int", + width: 120, + align: "left", + hidden: true, + }, + { + field: "creater", + title: "鍒涘缓浜�", + type: "string", + width: 90, + align: "left", + }, + { + field: "createDate", + title: "鍒涘缓鏃堕棿", + type: "datetime", + width: 150, + align: "left", + }, + { + field: "modifier", + title: "淇敼浜�", + type: "string", + width: 100, + align: "left", + }, + { + field: "modifyDate", + title: "淇敼鏃堕棿", + type: "datetime", + width: 160, + align: "left", + }, + { + field: "remark", + title: "澶囨敞", + type: "string", + width: 100, + align: "left", + hidden: true, + }, + ]); + const detail = ref({ + cnName: "", + table: "", + columns: [], + sortName: "", + key: "", + }); + return { + table, + extend, + editFormFields, + editFormOptions, + searchFormFields, + searchFormOptions, + columns, + detail, + }; + }, +}); +</script> + \ No newline at end of file diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js" new file mode 100644 index 0000000..bc9939b --- /dev/null +++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js" @@ -0,0 +1,13 @@ +import { expect } from 'chai' +import { shallowMount } from '@vue/test-utils' +import HelloWorld from '@/components/HelloWorld.vue' + +describe('HelloWorld.vue', () => { + it('renders props.msg when passed', () => { + const msg = 'new message' + const wrapper = shallowMount(HelloWorld, { + props: { msg } + }) + expect(wrapper.text()).to.include(msg) + }) +}) -- Gitblit v1.9.3