z8018
2025-02-18 a29db61231d32f26e4204af876aa6e427a6f190e
前端代码提交
已修改6个文件
已添加168个文件
68889 ■■■■■ 文件已修改
项目代码/WCS/WIDESEAWCS_Client/.eslintrc.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/.gitignore 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/README.md 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/babel.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/config/buttons.js 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/package-lock.json 13742 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/package.json 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/public/index.html 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/public/static/login_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/public/wcslogo.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/public/webconfig.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/public/wms_d.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/App.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/api/http.js 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/api/permission.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/api/useTest.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/css/common.less 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/element-icon/icon.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/script/common.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/script/extend.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/Audit.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/Icons.vue 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/QuickSearch.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/RouterLoading.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/UploadExcel.vue 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/AuditHis.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.less 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGrid.vue 806 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridAudit.vue 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.js 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/ViewGridCustomColumn.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/index.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/methods.js 1684 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/props.js 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolBox.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenu.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolElementMenuChild.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolForm.vue 1487 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolForm/VolFormRender.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/DownloadForm.js 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormDraggable.vue 1159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/VolFormPreview.vue 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/formTemplate.js 664 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/options.js 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/templateCode.js 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolHeader.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolTable.vue 1873 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolTable/VolTableRender.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/basic/VolUpload.vue 880 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/editor/VolWangEditor.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/redirect/401.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/redirect/404.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/redirect/Message.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/redirect/RedirectError.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/redirect/coding.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/data_A.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/force-directed.js 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/index.css 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js 15711 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/mixins.js 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/node.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/node_filter.vue 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/node_form.vue 350 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/node_menu.vue 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/panel.vue 612 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/components/workflow/utils.js 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/addrouters.vue 351 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/basicinfo/extend/routerview.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.js 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/quartzJob/extend/importDevicePro.vue 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User/Sys_UserGridHeader.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.js 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/taskinfo/extend/taskExecuteDetail.vue 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/extend/GetLocationStatus.vue 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/locationInfo.js 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfo.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfoDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfoDetail_Hty.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfo_Hty.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/main.js 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/router/charts.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/router/index.js 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/router/redirect.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/router/viewGird.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/store/index.js 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/uitils/common.js 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/Home.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/Index.vue 819 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/Login.vue 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/builder/builderData.js 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue 658 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js 551 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/index/Message.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/index/index.less 644 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue 359 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue 672 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue 369 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/system/test.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/locationInfo.vue 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfo.vue 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfoDetail.vue 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfoDetail_Hty.vue 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfo_Hty.vue 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/vue.config.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Client/yarn.lock 10203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v17/DocumentLayout.backup.json 146 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v17/DocumentLayout.json 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/System/Sys_Log.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/CustomProfile.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/wwwroot/WIDESEAWCS_DB.DBSeed.Json/Dt_DeviceInfo.tsv 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/wwwroot/WIDESEAWCS_DB.DBSeed.Json/Dt_DispatchInfo.tsv 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/.eslintrc.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended'
  ],
  parserOptions: {
    parser: 'babel-eslint'
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
  },
  overrides: [
    {
      files: [
        '**/__tests__/*.{j,t}s?(x)',
        '**/tests/unit/**/*.spec.{j,t}s?(x)'
      ],
      env: {
        mocha: true
      }
    }
  ]
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/.gitignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
# vol.vue3
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your unit tests
```
npm run test:unit
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
### npm run serve启动异常:
    ä½¿ç”¨cmd输入node -v查看版本,如果是18.+版本,请将package.json中第五行scripts中的内容替换为:
"scripts": {
    "serve": " SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
    "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "lint": "vue-cli-service lint"
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/babel.config.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ]
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/config/buttons.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,143 @@
let buttons = [{
    name: "查 è¯¢",
    value: 'Search',
    icon: 'el-icon-search',
    class: '',
    type: 'primary',
    onClick: function () {
        this.search();
    }
},
{
    name: "新 å»º",
    icon: 'el-icon-plus',
    value: 'Add',
    class: '',
    //  plain:true,
    type: 'success',
    // plain:true,
    onClick: function () {
        this.add();
    }
},
{
    name: "编 è¾‘",
    icon: 'el-icon-edit',
    value: 'Update',
    // plain:true,
    class: '',
    type: 'primary',
    onClick: function () {
        this.edit();
    }
},
{
    name: "任务完成",
    icon: '',
    class: '',
    value: 'TaskCompleted',
    type: 'primary',
    onClick: function () {
    }
},
{
    name: "任务取消",
    icon: '',
    class: '',
    value: 'TaskCancel',
    type: 'danger',
    onClick: function () {
    }
},
{
    name: "任务恢复",
    icon: '',
    class: '',
    value: 'TaskRecovery',
    type: 'danger',
    onClick: function () {
    }
},
{
    name: "查看完整路由",
    icon: '',
    class: '',
    value: 'ViewAllRouter',
    type: 'info',
    onClick: function () {
    }
},
{
    name: "路由配置",
    icon: '',
    class: '',
    value: 'AddRouters',
    type: 'success',
    onClick: function () {
    }
},
{
    name: "回滚到上一步",
    icon: '',
    class: '',
    value: 'Previous',
    type: 'danger',
    onClick: function () {
    }
},
{
    name: "跳转到下一步",
    icon: '',
    class: '',
    value: 'Next',
    type: 'warning',
    onClick: function () {
    }
},
{
    name: "删 é™¤",
    icon: 'el-icon-delete',
    class: '',
    value: 'Delete',
    type: 'danger',
    onClick: function () {
        this.del();
    }
},
{
    name: "审 æ ¸",
    icon: 'el-icon-check',
    class: '',
    value: 'Audit',
    plain: true,
    type: 'primary',
    onClick: function () {
        this.audit();
    }
},
{
    name: "导 å…¥",
    icon: 'el-icon-top',
    class: '',
    type: 'success',
    plain: true,
    value: 'Import',
    onClick: function () {
        this.import();
    }
},
{
    name: "导 å‡º",
    icon: 'el-icon-bottom',
    type: 'success',
    plain: true,
    value: 'Export',
    onClick: function () {
        this.export();
    }
}
]
export default buttons
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/package-lock.json
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
{
  "name": "wideseawcs",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "server": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.1.0",
    "@microsoft/signalr": "^6.0.4",
    "ali-oss": "^6.17.1",
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "echarts": "^5.0.2",
    "element-plus": "^2.2.14",
    "less": "^4.1.1",
    "vue": "^3.2.37",
    "vue-draggable-next": "^2.0.1",
    "vue-router": "^4.0.0-0",
    "vuex": "^4.0.0-0",
    "wangeditor": "^4.7.6"
  },
  "devDependencies": {
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-unit-mocha": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/test-utils": "^2.0.0-0",
    "babel-eslint": "^10.1.0",
    "chai": "^4.1.2",
    "cross-env": "^7.0.3",
    "less": "^4.1.1",
    "less-loader": "^7.3.0",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "@vue/standard"
    ],
    "rules": {
      "indent": [
        1,
        4
      ]
    },
    "parserOptions": {
      "parser": "babel-eslint"
    }
  },
  "eslintIgnore": [
    "*"
  ]
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/public/index.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
<!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>
  <script src="webconfig.js"></script>
</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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/public/static/login_bg.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/public/wcslogo.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/public/webconfig.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
window.webConfig = {
    "webApiDevelopment": "http://localhost:9291/",
    "webApiProduction": "http://localhost:9291/",
    "webApiDebug": "http://localhost:9291/",
    "webSocketUrl": "ws://localhost:9260/",
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/public/wms_d.png
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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 = window.webConfig.webApiProduction;
}
else if (process.env.NODE_ENV == 'debug') {
    axios.defaults.baseURL = window.webConfig.webApiDebug;
}
else if (process.env.NODE_ENV == 'production') {
    axios.defaults.baseURL = window.webConfig.webApiProduction;
}
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 : '正在处理.....',
        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')) { // éžIE下载
            const elink = document.createElement('a')
            elink.download = fileName
            elink.style.display = 'none'
            elink.href = URL.createObjectURL(blob)
            document.body.appendChild(elink)
            elink.click()
            URL.revokeObjectURL(elink.href) // é‡Šæ”¾URL å¯¹è±¡
            document.body.removeChild(elink)
        } else { // IE10+下载
            navigator.msSaveBlob(blob, fileName)
        }
        callback&&callback();
    })
}
function createXHR () {
    if (XMLHttpRequest) {
        return new XMLHttpRequest();
    }
    if (ActiveXObject) {
        if (typeof arguments.callee.activeXString != "string") {
            var versions = [
                "MSXML2.XMLHttp.6.0",
                "MSXML2.XMLHttp",
                "MSXML2.XMLHttp.3.0"
            ];
            for (var i = 0; i < versions.length; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (e) {
                    console.log(e);
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    }
}
function redirect (responseText, message) {
    try {
        let responseData = typeof responseText == 'string' ? JSON.parse(responseText) : responseText;
        if ((responseData.hasOwnProperty('code') && responseData.code == 401)
            || (responseData.data && responseData.data.code == 401)) {
            closeLoading();
            toLogin();
        } else {
            if (message) {
                Message.error({
                    showClose: true,
                    message: message,
                    type: 'error'
                });
            }
        }
    } catch (error) {
        console.log(error);
        Message.error({
            showClose: true,
            message: responseText,
            type: 'error'
        });
    }
}
function toLogin () {
    //  const vueinstance=  getCurrentInstance();
    if (window.location.hash) {
        window.location.href = window.location.origin + '/#/login'
        return
    }
    window.location.href = window.location.origin + '/login'
    //  router.push({ path: '/login', params: { r: Math.random() } });
}
//动态刷新token
function replaceToken () {
    ajax({
        url: "/api/User/replaceToken",
        param: {},
        json: true,
        success: function (x) {
            if (x.status) {
                let userInfo = store.getters.getUserInfo();
                userInfo.token = x.data;
                store.commit('setUserInfo', userInfo);
            } else {
                console.log(x.message);
                toLogin();
            }
        },
        errror: function (ex) {
            console.log(ex);
            toLogin();
        },
        type: "post",
        async: false
    });
}
function ajax (param) {
    let httpParam =
        Object.assign({
            url: '', headers: {},
            param: {}, json: true,
            success: function () { },
            errror: function () { },
            type: 'post', async: true
        }, param);
    httpParam.url = axios.defaults.baseURL + httpParam.url.replace(/\/?/, '');
    httpParam.headers[_Authorization] = getToken();
    var xhr = createXHR();
    xhr.onreadystatechange = function () {
        if (xhr.status == 403 || xhr.status == 401) {
            redirect(xhr.responseText);
            return;
        }
        checkResponse(xhr);
        if (xhr.readyState == 4 && xhr.status == 200) {
            httpParam.success(httpParam.json ? JSON.parse(xhr.responseText) : xhr.responseText);
            return;
        }
        if (xhr.status != 0 && xhr.readyState != 1) {
            httpParam.errror(xhr);
        }
    };
    //初始化请求
    xhr.open(
        httpParam.type,
        httpParam.url,
        httpParam.async
    );
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    for (const key in httpParam.headers) {
        xhr.setRequestHeader(key, httpParam.headers[key]);
    }
    let dataStr = '';
    for (const key in httpParam.param) {
        dataStr += key + "=" + httpParam.param[key];
    }
    try {
        xhr.send(dataStr);
    } catch (error) {
        toLogin();
    }
}
ajax.post = function (url, param, success, errror) {
    ajax({ url: url, param: param, success: success, error: errror, type: 'post' })
}
ajax.get = function (url, param, success, errror) {
    ajax({ url: url, param: param, success: success, error: errror, type: 'get' })
}
export default { post, get,download, ajax, ipAddress }
ÏîÄ¿´úÂë/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;
ÏîÄ¿´úÂë/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 }
ÏîÄ¿´úÂë/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;
  }
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.ttf
Binary files differ
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/element-icon/fonts/element-icons.woff
Binary files differ
ÏîÄ¿´úÂë/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)}}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/error-img.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/error.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/favicon.ico
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/01.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/02.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/03.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/04.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/05.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/06.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/h5/07.jpg
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/log.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/logo.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/wcs_logo.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_d.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/imgs/wms_x.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/logo.png
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/script/common.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2 @@
var test1 = function () { alert(11) }
export { test1 }
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/script/extend.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
//对vue参数进行扩展
var extend = function (param) {
    console.log(param)
 }
export { extend }
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/assets/script/testFormExtend.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
//对vue参数进行扩展
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 }
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/AsyncLoading.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
<template>
  <div style="text-align: center;font-size: 16px;padding: 20px;">正在加载资源...</div>
</template>
<script>
export default {
  created() {
    //  console.log('loading')
  }
};
</script>
ÏîÄ¿´úÂë/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;">备&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; æ³¨ï¼š</label>
    <el-input
      v-model="auditParam.reason"
      type="textarea"
      style="margin-right: 13px;"
      :autosize="{ minRows: 4, maxRows: 10 }"
      placeholder="审核备注..."
    ></el-input>
  </div>
</template>
<script>
export default {
  props: {
    auditParam: {
      type: Object,
      default: () => {
        return {
          auditParam: {
            rows: 0,
            model: false,
            status: -1,
            reason: "",
            data: [], //[{ text: "通过", status: 1 }, { text: "拒绝", status: 2 }]
          },
        };
      },
    },
  },
};
</script>
<style lang="less" scoped>
.item{
  margin-top: 20px;
  display: flex;
  > label{
    width: 86px;
  }
}
</style>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/Empty.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
<template>
  <div></div>
</template>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/ErrorMsg.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
<template>
    <div id="test"></div>
</template>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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) {
      //见https://day.js.org/docs/zh-CN/display/format
      return item.type == "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss";
    },
    getDateOptions(date, item) {
      if ((!item.min && !item.max) || !date) {
        return false;
      }
      if (item.min && item.min.indexOf(" ") == -1) {
        //不设置时分秒,后面会自动加上 08:00
        item.min = item.min + " 00:00:000";
      }
      return (
        this.compareDate(date, item.min) || !this.compareDate(date, item.max)
      );
    },
    dateRangeClear(field) {
      this.searchFormFields[field]=[undefined,undefined];
    },
  },
  created() {
    this.singleSearch.dateType = this.singleSearch.type + "range";
    if (
      this.singleSearch.type == "date" ||
      this.singleSearch.type == "datetime"
    ) {
      var _dateVal = this.searchFormFields[this.singleSearch.field];
      if (
        typeof this.singleSearch.range == "boolean" &&
        !this.singleSearch.range
      ) {
        this.searchFormFields[this.singleSearch.field] = "";
        this.singleSearch.dateType = this.singleSearch.type;
        return this.singleSearch.dateType;
      } else if (!(_dateVal instanceof Array)) {
        this.searchFormFields[this.singleSearch.field] = ["", ""];
      } else if (_dateVal.length != 2) {
        _dateVal.splice(0);
        _dateVal.push(...["", ""]);
      }
    }
  },
};
</script>
<style lang="less" scoped>
.date-range{
  position: relative;
  > i{
    display: none;
    height: 27px;
    line-height: 27px;
    right: 1px;
    top: 3px;
    font-size: 13px;
    color: #b4adad;
    position: absolute;
    padding: 0 6px 0 3px;
    background: #ffff;
    cursor: pointer;
  }
}
.date-range:hover > i{
  display: inline-block;
}
</style>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
//目前只支持单个Excel上传,其他功能开发中...
export default {
  components: {},
  props: {
    url: {
      type: String,
      default: ''
    },
    template: {
      //下载模板配置
      type: Object,
      default: () => {
        return {
          url: '', //模板下载路径,如果没有模板路径,则不显示下载模板功能
          fileName: '未定义文件名' //下载模板的文件名
        };
      }
    },
    importExcelBefore: {
      type: Function,
      default: (file) => {
        return true;
      }
    }
  },
  data() {
    return {
      maxSize: 100,
      model: true,
      file: null,
      loadingStatus: false,
      message: '',
      resultClass: ''
    };
  },
  methods: {
    clearMsg() {
      this.message = '';
    },
    reset() {
      this.file = null;
      this.message = '';
      this.resultClass = '';
    },
    getFileType() {
      let fileName =
        this.file.name
          .split('.')
          .pop()
          .toLocaleLowerCase() || '';
      if (['numbers', 'csv', 'xls', 'xlsx'].indexOf(fileName) == -1) {
        this.$Message.error('只能选择excel文件');
        return false;
      }
      return true;
    },
    beforeUpload(file) {
      this.file = file;
      if (!this.getFileType()) {
        return false;
      }
      return false;
    },
    upload() {
      let _url = this.url;
      if (!_url) {
        return this.$Message.error('没有配置好Url');
      }
      if (!this.file) {
        return this.$Message.error('请选择文件');
      }
      var formData = new FormData();
      formData.append('fileInput', this.file);
      if (!this.importExcelBefore(formData)) {
        return;
      }
      this.loadingStatus = true;
      this.http.post(_url, formData).then(
        (x) => {
          // this.$refs.uploadFile.clearFiles();
          this.loadingStatus = false;
          this.file = null;
          if (x.status) {
            this.$emit('importExcelAfter', x);
          }
          this.message = x.message;
          this.resultClass = x.status ? 'v-r-success' : 'v-r-error';
        },
        (error) => {
          this.loadingStatus = false;
        }
      );
    },
    dowloadTemplate() {
      let url = this.template.url;
      let xmlResquest = new XMLHttpRequest();
      xmlResquest.open('GET', url, true);
      xmlResquest.setRequestHeader('Content-type', 'application/json');
      xmlResquest.setRequestHeader(
        'Authorization',
        this.$store.getters.getToken()
      );
      let fileName = this.template.fileName + '.xlsx';
      let elink = this.$refs.template;
      xmlResquest.responseType = 'blob';
      let $_vue = this;
      this.loadingStatus = true;
      xmlResquest.onload = function(oEvent) {
        $_vue.loadingStatus = false;
        if (xmlResquest.response.type == 'application/json') {
          return $_vue.message.error('未找到下载文件');
        }
        let content = xmlResquest.response;
        elink.download = fileName;
        let blob = new Blob([content]);
        elink.href = URL.createObjectURL(blob);
        elink.click();
      };
      xmlResquest.send();
    }
  }
};
</script>
<style lang="less" scoped>
.upload-container {
  min-height: 270px !important;
  display: inline-block;
  width: 100%;
  padding: 10px;
  border: 1px dashed #989898;
  min-height: 250px;
  border-radius: 5px;
  .alert {
    margin-top: 12px;
  }
  .el-button-group > * {
    display: flex;
  }
  h3 {
    margin: 9px 0px;
  }
  .file-info > span {
    margin-right: 20px;
  }
  .v-r-message {
    margin-top: 10px;
    .title {
      margin-bottom: 2px;
    }
    > .text {
      font-size: 13px;
    }
    .v-r-success {
      color: #02b702;
    }
    .v-r-error {
      color: #dc0909;
    }
  }
}
</style>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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;
    }
  }
}
ÏîÄ¿´úÂë/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: [],
      //树形结构的主键字段,如果设置值默认会开启树形table;注意rowKey字段的值必须是唯一(2021.05.02)
      rowKey: undefined,
      fiexdSearchForm: false, //2020.09.011是否固定查询表单,true查询表单将固定显示在表单的最上面
      _inited: false,
      doubleEdit: false, //2021.03.19是否开启查询界面表格双击编辑
      single: false, //表是否单选
      const: _const, //增删改查导入导出等对应的action
      boxInit: false, //新建或编辑的弹出框初化状态,默认不做初始化,点击新建或编辑才初始化弹出框
      searchBoxShow: false, //高级查询(界面查询后的下拉框点击触发)
      singleSearch: {}, //快速查询字段
      exportHref: '',
      currentAction: _const.ADD, //当新建或编辑时,记录当前的状态:如当前操作是新建
      currentRow: {}, //当前编辑或查看数据的行
      closable: false,
      boxModel: false, //弹出新建、编辑框
      width: 700, //弹出框查看表数据结构
      labelWidth: 100, //高级查询的标签宽度
      viewModel: false, //查看表结构的弹出框
      viewColumns: [], //查看表结构的列数据
      viewColumnsClone: [],
      showCustom: true, //是否显示自定义配置列按钮2022.05.27
      // viewData: [], //查看表结构信息
      maxBtnLength: 8, //界面按钮最多显示的个数,超过的数量都显示在更多中
      buttons: [], //查询界面按钮  å¦‚需要其他操作按钮,可在表对应的.js中添加(如:Sys_User.js中buttons添加其他按钮)
      splitButtons: [],
      uploadfiled: [], //上传文件图片的字段
      boxButtons: [], //弹出框按钮 å¦‚需要其他操作按钮,可在表对应的.js中添加
      dicKeys: [], //当前界面所有的下拉框字典编号及数据源
      hasKeyField: [], //有字典数据源的字段
      keyValueType: { _dinit: false },
      url: '', //界面表查询的数据源的url
      hasDetail: false, //是否有从表(明细)表格数据
      initActivated: false,
      load: true, //是否默认加载表数据
      activatedLoad: false, //页面触发actived时是否刷新页面数据
      summary: false, //查询界面table是否显示合计
      //需要从远程绑定数据源的字典编号,如果字典数据源的查询结果较多,请在onInit中将字典编号添加进来
      //只对自定sql有效
      remoteKeys: [],
      columnIndex: 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: '', //从表加载数据的url
        pagination: { total: 0, size: 100, sortName: '' }, //从表分页配置数据
        height: 0, //默认从表高度
        textInline: true, //明细表行内容显示在一行上,如果需要换行显示,请设置为false
        doubleEdit: true, //使用双击编辑
        clickEdit: false, //是否开启点击单元格编辑,点击其他行时结束编辑
        currentReadonly: false, //当前用户没有编辑或新建权限时,表单只读(可用于判断用户是否有编辑或新建权限)
        //开启编辑时
        beginEdit: (row, column, index) => {
          return true;
        },
        //结束编辑前
        endEditBefore: (row, column, index) => {
          return true;
        },
        //结束编辑后
        endEditAfter: (row, column, index) => {
          return true;
        },
        columnIndex: false, //2020.11.01明细是否显示行号
        ck: true //2020.11.01明细是否显示checkbox
      },
      auditParam: {
        //审核对象
        rows: 0, //当前选中审核的行数
        model: false, //审核弹出框
        value: -1, //审核结果
        status: -1,
        reason: '', //审核原因
        height: 500,
        showViewButton: true,
        auditHis: [],
        showAction: false, //是否显示审批操作(当前节点为用户审批时显示)
        //审核选项(可自行再添加)
        data: [
          { text: '通过', value: 1 },
          { text: '拒绝', value: 2 },
          { text: '驳回', value: 3 }
        ]
      },
      upload: {
        //导入上传excel对象
        excel: false, //导入的弹出框是否显示
        url: '', //导入的路径,如果没有值,则不渲染导入功能
        template: {
          //下载模板对象
          url: '', //下载模板路径
          fileName: '' //模板下载的中文名
        },
        init: false //是否有导入权限,有才渲染导入组件
      },
      height: 0, //表高度
      tableHeight: 0, //查询页面table的高度
      tableMaxHeight: 0, //查询页面table的最大高度
      textInline: true, //table内容超出后是否不换行2020.01.16
      pagination: { total: 0, size: 30, sortName: '' }, //从分页配置数据
      boxOptions: {
        title: '', //弹出框显示的标题2022.08.01
        saveClose: true,
        labelWidth: 100,
        height: 0,
        width: 0,
        summary: false, //弹出框明细table是否显示合计
        draggable: 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>
ÏîÄ¿´úÂë/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文件的table参数
      type: Object,
      default: {
        key: '',
        cnName: '',
        name: '',
        url: ""
      }
    }
  },
  setup(props, { emit }) {
    const height = ref(500);
    const width = ref(820);
    const model = ref(false)
    const workFlowSteps = reactive([]);
    const hasFlow = ref(false)
    const 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>
ÏîÄ¿´úÂë/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);
    }
  }
};
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/detailMethods.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
//从表方法
let detailMethods = {
  //查询从表前先做内部处理
  loadInternalDetailTableBefore(param, callBack) {
    //加载明细表数据之前,需要设定查询的主表的ID
    //每次只要加载明细表格数据就重置删除明细的值
    if (this.detailOptions.delKeys.length > 0) {
      this.detailOptions.delKeys = [];
    }
    let key = this.table.key;
    if (this.currentRow && this.currentRow.hasOwnProperty(key)) {
      param.value = this.currentRow[key];
    }
    return this.loadDetailTableBefore(param, callBack);
  },
  detailRowOnChange(row) {
    this.detailRowChange(row);
  },
  detailRowChange(row) {
    //checkbox选中行事件
  },
  detailRowOnClick({ row, column, event }) {
    //明细表点击行事件2020.11.07
    this.detailRowClick({ row, column, event });
  },
  detailRowClick({ row, column, event }) {},
  resetDetailTable(row) {
    //编辑和查看明细时重置从表数据
    if (!this.detailOptions.columns || this.detailOptions.columns.length == 0) {
      return;
    }
    let key = this.table.key;
    let query = { value: row ? row[key] : this.currentRow[key] };
    this.$nextTick(() => {
      if (this.$refs.detail) {
        this.$refs.detail.reset();
        this.$refs.detail.load(query);
      }
    });
  },
  //从后面加载从表数据
  refreshRow() {
    this.resetDetailTable();
  },
  addRow() {
    this.$refs.detail.addRow({});
    this.$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){
    //明细表CheckBox æ˜¯å¦å¯ä»¥å‹¾é€‰
       return true;
  }
};
export default detailMethods;
ÏîÄ¿´úÂë/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
ÏîÄ¿´úÂë/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 = {
  //当添加扩展组件gridHeader/gridBody/gridFooter及明细modelHeader/modelBody/modelFooter时,
  //如果要获取父级Vue对象,请使用此方法进行回调
  parentCall(fun) {
    if (typeof fun != 'function') {
      return console.log('扩展组件需要传入一个回调方法才能获取父级Vue对象');
    }
    fun(this);
  },
  getCurrentAction() {
    if (this.currentReadonly) {
      return '';
    }
    return '--' + (this.currentAction == this.const.ADD ? '新增' : '编辑');
  },
  quickSearchKeyPress($event) {
    //查询字段为input时,按回车查询
    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权限按钮,source为扩展按钮
    if (!btns || !(source && source instanceof Array)) {
      return;
    }
    //source通过在表的扩展js文件中buttons对应按钮的属性index决定按钮所放位置
    source.forEach((x) => {
      //通过按钮的Index属性,放到指定的位置
      btns.splice(x.index == undefined ? btns.length : x.index, 0, x);
    });
    // if (this.extend.buttons.view) {
    //     this.extend.buttons.view.forEach((x) => {
    //         //通过按钮的Index属性,放到指定的位置
    //         this.buttons.splice(x.index == undefined ? this.buttons.length : x.index, 0, x);
    //     })
    // }
  },
  initBoxButtons() {
    //初始化ViewGird与弹出框/明细表按钮
    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 = {};
    }
    //查询界面扩展按钮(扩展按钮可自行通过设置按钮的Index属性显示到具体位置)
    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;
      //定义下载模板的Url路径
      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的key是数字,重置的值是字符串就无法绑定值
    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, '正在删除数据....').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);
    //设置远程查询表单的默认key/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 = {};
    //如果有switch标签,默认都设置为否
    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]);
    //设置远程查询表单的默认key/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, '正在导出数据....', { responseType: 'blob' })
      .then((content) => {
        const blob = new Blob([content]);
        if ('download' in document.createElement('a')) {
          // éžIE下载
          const elink = document.createElement('a');
          elink.download = fileName;
          elink.style.display = 'none';
          elink.href = URL.createObjectURL(blob);
          document.body.appendChild(elink);
          elink.click();
          URL.revokeObjectURL(elink.href);
          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(`表必须包括审核字段【AuditStatus】,并且是int类型`)
    }
    // 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优化表单类型为number时的默认值
          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增加编辑表单对checkbox的支持
        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修复查询表单与编辑表单type类型变成强一致性的问题
          //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数据源配置格式data.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数据源checkbox与select类型从编辑列中选取
      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] = {
              //默认从字典数据读出来的key都是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数据源使用的自定义sql并且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) {
    //是否忽略前缀/  èŽ·å–æ“ä½œçš„url
    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);
    //初始化编辑数据源,默认为一个空数组,如果要求必填设置type=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;
      });
    });
    //初始化datatable表数据源,默认为一个空数组,dicKeys为界面所有的数据字典编号
    this.initColumns(this.columns, this.dicKeys, keys);
    //2021.05.23默认开启查询页面所有字段排序,如果不需要排序,在onInited遍历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);
    }
    //初始化快速查询字段,默认使用代码生成器配置的第一个查询字段
    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');
      //计算弹出框整个table的宽度,根据宽度决定是否启用第一行显示的列为固定列
      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) {
    //选中行checkbox行事件
  },
  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),在onInit中设置了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 `审核值不正确:${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;
ÏîÄ¿´úÂë/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;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/ViewGrid/serviceFilter.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
let serviceFilter = {
  onInit () { //对应created
    console.log('Create执行前')
  },
  onInited () { //对应created,在onInit与onInited中间会初始化界面数据对象
    console.log('Create执行后')
  },
  mounted () {
    console.log('mounted');
  },
  searchBefore (param) { //查询ViewGird表数据前,param查询参数
    // console.log('表' + this.table.cnName + '触发loadTableBefore');
    return true;
  },
  //2020.10.30增加查询后返回所有的查询信息
  searchAfter (param, result) { //查询ViewGird表数据后param查询参数,result回返查询的结果
    // console.log('表' + this.table.cnName + '触发loadTableAfter');
    return true;
  },
  searchDetailBefore (param) {//查询从表表数据前,param查询参数
    //console.log(this.detailOptions.cnName + '触发loadDetailTableBefore');
    return true;
  },
  searchDetailAfter (param, data) {//查询从表后param查询参数,result回返查询的结果
    // console.log(this.detailOptions.cnName + '触发loadDetailTableAfter');
    return true;
  },
  delBefore (ids, rows) { //查询界面的表删除前 ids为删除的id数组,,rows删除的行
    return true;
  },
  delAfter (result) {//查询界面的表删除后
    return true;
  },
  delDetailRow (rows) { //弹出框删除明细表的行数据(只是对table操作,并没有操作后台)
    return true;
  },
  addBefore (formData) { //新建保存前formData为对象,包括明细表
    return true;
  },
  async addBeforeAsync (formData) { //异步处理,功能同上(2020.12.06)
    return true;
  },
  addAfter (result) {//新建保存后result返回的状态及表单对象
    return true;
  },
  updateBefore (formData) { //编辑保存前formData为对象,包括明细表、删除行的Id
    return true;
  },
  async updateBeforeAsync (formData) { //异步处理,功能同上(2020.12.06)
    return true;
  },
  updateAfter (result) {//编辑保存后result返回的状态及表单对象
    return true;
  },
  auditBefore (ids, rows) {//审核前
    return true;
  },
  auditAfter (result, rows) {// å®¡æ ¸åŽ
    return true;
  },
  resetAddFormBefore () { //重置新建表单前的内容
    return true;
  },
  resetAddFormAfter () { //重置新建表单后的内容
    return true;
  },
  resetUpdateFormBefore () { //重置编辑表单前的内容
    return true;
  },
  resetUpdateFormAfter () { //重置编辑表单后的内容
    return true;
  },
  modelOpenBefore (row) { //点击编辑/新建按钮弹出框前,可以在此处写逻辑,如,从后台获取数据
  },
  modelOpenAfter (row) {  //点击编辑/新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
  },
  importAfter (data) { //导入excel后刷新table表格数据
    this.search();
  },
  //2020.10.31添加导入前的方法
  importExcelBefore (formData) { //导入excel导入前
    //往formData写一些其他参数提交到后台,
    // formData.append("val2", "xxx");
    //后台按下面方法获取请求的参数
    // Core.Utilities.HttpContext.Current.Request("val2");
    return true;
  },
  reloadDicSource () { //重新加载字典绑定的数据源
    this.initDicKeys();
  },
  exportBefore (param) { //2020.06.25增加导出前处理
    return true;
  },
  onModelClose(iconClick){
    //iconClick=true为点击左中上角X触发的关闭事件
    //如果返回 false不会关闭弹出框
    //return false;
    this.boxModel=false;
  },
  selectable(row, index){
    //表CheckBox æ˜¯å¦å¯ä»¥å‹¾é€‰
       return true;
  }
}
export default serviceFilter;
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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);
          }
          // ç›®å‰åªæ”¯æŒselect单选远程搜索,remote远程从后台字典数据源进行搜索,url从指定的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; // é»˜è®¤åªèƒ½ä¸Šä¼ ä¸€ä¸ªæ–‡ä»¶ï¼Œå¯ä»¥åœ¨onInit中设置
      }
      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增加表单多选只转换字典
      // ç¼–辑多选table显示
      //2023.04.20修复只读为label时原数据被字典替换了的问题
      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) {
      // ç¬¬äºŒæ¬¡æ‰“开时,默认值成了undefined,待查viewgrid中重置代码
      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;
      }
      // å¼¹å‡ºæ¡†æˆ–初始化表单时给data设置数组默认值2
      // 2020.09.26修复远程搜索自定义url不起作用的问题
      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与swtich暂时不做校验
      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'
      // }
      //见https://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>
ÏîÄ¿´úÂë/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();
  }
};
ÏîÄ¿´úÂë/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')) { // éžIE下载
        const elink = document.createElement('a')
        elink.download = `code${new Date().valueOf()}.vue`;
        elink.style.display = 'none'
        elink.href = URL.createObjectURL(blob)
        document.body.appendChild(elink)
        elink.click()
        URL.revokeObjectURL(elink.href) // é‡Šæ”¾URL å¯¹è±¡
        document.body.removeChild(elink)
    } else {
        navigator.msSaveBlob(blob, fileName)
    }
}
ÏîÄ¿´úÂë/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"
          >设计器基于框架volform、voltable、volupload、volbox定制开发</a
        >
      </div>
      <div>
        <el-alert
          title="关于表单设计器"
          type="success"
          :show-icon="true"
          :closable="false"
        >
          <div>
            1、表单设计器基于draggable开发,为本框架自定义页面功能的补充,框架仍以可视化代码生成器为核心
          </div>
          <div>
            2、支持可视化设计1对1、1对多及表单下拉框自动绑定、table自动加载数据(分页、编辑)、自动上传文件、富文本编辑
          </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>
ÏîÄ¿´úÂë/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"
        >下载代码</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>
ÏîÄ¿´úÂë/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 }
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/basic/VolFormDraggable/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
import VolFormDraggable from './VolFormDraggable'
 export default VolFormDraggable;
ÏîÄ¿´úÂë/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 }
ÏîÄ¿´úÂë/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、新建一个vue页面,把此页面内容复制进去
// 2、router->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
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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
  >
    <!-- ä¸Šä¼ å›¾ç‰‡ã€excel或其他文件、文件数量、大小限制都可以,参照volupload组件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
  //没有原先的selection属性了,看issue上使用select/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: {
      // æ ‘形结构的主键字段,如果设置值默认会开启树形table;注意rowKey字段的值必须是唯一(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: {
      // è¡¨æ•°æ®æº,配置了url就不用传这个参数了
      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: {
      // ä¼ å…¥äº†url,是否默认加载表格数据
      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, // æ˜¯å¦æ˜¾ç¤ºåˆè®¡
      // ç›®å‰åªæ”¯æŒä»ŽåŽå°è¿”回的summaryData数据
      summaryData: [],
      summaryIndex: {},
      remoteColumns: [], // éœ€è¦æ¯æ¬¡åˆ·æ–°æˆ–分页后从后台加载字典数据源的列配置
      cellStyleColumns: {}, // æœ‰èƒŒæ™¯é¢œè‰²çš„配置
      fxRight: false, //是否有右边固定表头
      selectRows: [], //当前选中的行
      isChrome: false,
      //vol-table带数据源的单元格是否启用tag标签(下拉框等单元格以tag标签显示)
      //2023.04.02更新voltable与main.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移除强制固定行号与checkbox列
    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) {
      //这里在初始化的时候也会触发change事件
      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;
      }
      //正在编辑时,禁止出发rowClick事件
      if (this.edit.rowIndex == -1) {
        this.$emit("rowClick", { row, column, event });
      }
      // ç‚¹å‡»è¡Œäº‹ä»¶(2020.11.07)
      if (!this.doubleEdit) {
        return;
      }
      // ç‚¹å‡»å…¶ä»–行时,如果点击的行与正在编辑的行相同,保持编辑状态
      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事件
        //正在编辑时,禁止出发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 (
          //不能编辑的字段、switch,点击不开启启编辑功能
          !_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的属性index='true'"
        );
      }
      // if (indexArr.length == 0 || !this.key) {
      //   return this.$message.error(
      //     "请设置index=true属性或指columns的字段为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) {
        // åªæœ‰è®¾ç½®äº†å±žæ€§index才有索引行
        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("");
      }
      // å¦‚果有checkbox,应该算作是第一行
      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];
        }
      }
      // å°†selectionchange暴露出去
      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);
      }
      // ç¼–辑多选table显示
      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) {
      // ç¼–辑多选table显示
      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) {
      //见https://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>
ÏîÄ¿´úÂë/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();
  }
};
ÏîÄ¿´úÂë/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: {
      //上传的url
      type: String,
      default: ''
    },
    uploadBefore: {
      //返回false会中止执行
      //上传前
      type: Function,
      default: (files) => {
        return true;
      }
    },
    uploadAfter: {
      //返回false会中止执行
      //上传后
      type: Function,
      default: (result, files) => {
        return true;
      }
    },
    onChange: {
      //选择文件时  //返回false会中止执行
      type: Function,
      default: (files) => {
        return true;
      }
    },
    // clear: {
    //   //上传完成后是否清空文件列表
    //   type: Boolean,
    //   default: true
    // },
    fileList: {
      //是否显示选择的文件列表
      type: Boolean,
      default: true
    },
    fileClick: {
      //点击文件事件
      type: Function,
      default: (index, file, files) => {
        return true;
      }
    },
    removeBefore: {
      //移除文件事件
      type: Function,
      default: (index, file, files) => {
        return true;
      }
    },
    append: {
      //此属性已废弃,多文件上传,默认追加文件
      type: Boolean,
      default: false
    },
    compress: {
      //开启图片压缩,后面根据需要再完善
      type: Boolean,
      default: 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;
      //如果传入了FileInfo需要自行处理移除FileInfo
      if (!this.onChange(e.target.files)) {
        return;
      }
      for (let index = 0; index < e.target.files.length; index++) {
        const element = e.target.files[index];
        element.input = true;
      }
      if (!this.multiple) {
        this.files.splice(0);
      }
      this.files.push(...e.target.files);
      this.$refs.input.value = null;
      if (this.autoUpload && result) {
        this.upload(false);
      }
    },
    removeFile(index) {
      //如果传入了FileInfo需要自行处理移除FileInfo
      //t移除文件
      let removeFile = this.files[index];
      //删除的还没上传的文件
      if (removeFile.input) {
        this.files.splice(index, 1);
      } else {
        this.fileInfo.splice(index, 1);
      }
      if (!this.removeBefore(index, removeFile, this.fileInfo)) {
        return;
      }
    },
    clearFiles() {
      this.files.splice(0);
    },
    getFiles() {
      return this.files;
    },
    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 æˆ–Arrary ç¬¬äºŒä¸ªå‚数是文件名
      // ç¬¬ä¸‰ä¸ªå‚数是 è¦æ”¾åˆ°æ–‡ä»¶ä¸­çš„内容的 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(杭州)为例,Region填写为oss-cn-hangzhou。
          region: x.data.region,
          // ä»ŽSTS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
          accessKeyId: x.data.accessKeyId,
          accessKeySecret: x.data.accessKeySecret,
          // ä»ŽSTS服务获取的安全令牌(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('没有配置好Url');
      }
      if (!this.files || this.files.length == 0) {
        return this.$message.error('请选择文件');
      }
      //增加上传时自定义参数,后台使用获取Utilities.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 ? '正在上传文件' : '')
        .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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/editor/KindEditor.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
ÏîÄ¿´úÂë/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: {
      //上传图片的url
      type: String,
      default: "",
    },
    upload: {
      //上传方法
      type: Function,
      // (file, insertImgFn) => {}
      default: null,
    },
    uploadCount: {
      //最多可以上传(图片)的数量
      type: Number,
      default: 3,
    },
    modelValue: "",
    width: {
      type: String,
      default: "100%",
    },
    height: {
      type: Number,
      default: 250,
    },
    minWidth: {
      type: Number,
      default: 650,
    },
    minHeight: {
      type: Number,
      default: 100,
    },
  },
  name: "wang-editor",
  data() {
    return {
      lastHtml: "",
      change: false,
      editor: null,
      init: false,
    };
  },
  watch: {
    modelValue(newVal, val) {
      if (
        (newVal !== val &&
          this.lastHtml !== "" &&
          val === this.lastHtml &&
          this.editor.txt.html() === this.lastHtml) ||
        this.editor.txt.html() === ""
      ) {
        this.editor.txt.html(newVal);
      }
      this.lastHtml = newVal;
    },
  },
  destroyed() {
    this.editor = null;
  },
  mounted() {
    this.editor = null;
    let editor = new E(this.$refs.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("未配置url");
          return;
        }
        $this.http.post($this.url, formData, true).then((x) => {
          if (!x.status) {
            return $this.$message.error(x.message);
          }
          nameArr.forEach(m=>{
            insertImgFn($this.http.ipAddress + x.data + m);
          })
          // let imgs = nameArr
          //   .map((m) => {
          //     return $this.http.ipAddress + x.data + m;
          //   })
          //   .join(",");
          // insertImgFn(imgs);
        });
      }
    };
    editor.create();
    editor.txt.html(this.modelValue);
  },
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1,
h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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: "用例正在整理中",
      text: "详细用例在正准备中,目前可参考[测试完整示例]的使用方法"
    };
  }
};
</script>
ÏîÄ¿´úÂë/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
}
ÏîÄ¿´úÂë/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
}
ÏîÄ¿´úÂë/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;
}
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/components/workflow/jsplumb.js
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
ÏîÄ¿´úÂë/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、Flowchart,Bezier、Straight
                Connector: ['Bezier', {curviness: 100}],
                // Connector: ['Straight', {stub: 20, gap: 1}],
                // Connector: ['Flowchart', {stub: 30, gap: 1, alwaysRespectStubs: false, midpoint: 0.5, cornerRadius: 10}],
                // Connector: ['StateMachine', {margin: 5, curviness: 10, proximityLimit: 80}],
                // é¼ æ ‡ä¸èƒ½æ‹–动删除线
                ConnectionsDetachable: false,
                // åˆ é™¤çº¿çš„æ—¶å€™èŠ‚ç‚¹ä¸åˆ é™¤
                DeleteEndpointsOnDetach: false,
                /**
                 * è¿žçº¿çš„两端端点类型:圆形
                 * radius: åœ†çš„半径,越大圆越大
                 */
                // Endpoint: ['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}],
                /**
                 * è¿žçº¿çš„两端端点类型:矩形
                 * height: çŸ©å½¢çš„高
                 * width: çŸ©å½¢çš„宽
                 */
                // Endpoint: ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}],
                /**
                 * å›¾åƒç«¯ç‚¹
                 */
                // Endpoint: ['Image', {src: 'https://www.easyicon.net/api/resizeApi.php?id=1181776&size=32', cssClass: 'ef-img', hoverClass: 'ef-img-hover'}],
                /**
                 * ç©ºç™½ç«¯ç‚¹
                 */
                Endpoint: ['Blank', {Overlays: ''}],
                // Endpoints: [['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}], ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}]],
                /**
                 * è¿žçº¿çš„两端端点样式
                 * fill: é¢œè‰²å€¼ï¼Œå¦‚:#12aabb,为空不显示
                 * outlineWidth: å¤–边线宽度
                 */
                EndpointStyle: {fill: '#1879ffa1', outlineWidth: 1},
                // æ˜¯å¦æ‰“å¼€jsPlumb的内部日志记录
                LogEnabled: true,
                /**
                 * è¿žçº¿çš„æ ·å¼
                 */
                PaintStyle: {
                    // çº¿çš„颜色
                    stroke: '#E0E3E7',
                    // çº¿çš„粗细,值越大线越粗
                    strokeWidth: 1,
                    // è®¾ç½®å¤–边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
                    outlineStroke: 'transparent',
                    // çº¿å¤–边的宽,值越大,线的点击范围越大
                    outlineWidth: 10
                },
                DragOptions: {cursor: 'pointer', zIndex: 2000},
                /**
                 *  å åŠ  å‚考: https://www.jianshu.com/p/d9e9918fd928
                 */
                Overlays: [
                    // ç®­å¤´å åŠ 
                    ['Arrow', {
                        width: 10, // ç®­å¤´å°¾éƒ¨çš„宽度
                        length: 8, // ä»Žç®­å¤´çš„尾部到头部的距离
                        location: 1, // ä½ç½®ï¼Œå»ºè®®ä½¿ç”¨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、canvas
                RenderMode: 'svg',
                // é¼ æ ‡æ»‘过线的样式
                HoverPaintStyle: {stroke: '#b0b2b5', strokeWidth: 1},
                // æ»‘过锚点效果
                // EndpointHoverStyle: {fill: 'red'}
                Scope: 'jsPlumb_DefaultScope' // èŒƒå›´ï¼Œå…·æœ‰ç›¸åŒscope的点才可连接
            },
            /**
             * è¿žçº¿å‚æ•°
             */
            jsplumbConnectOptions: {
                isSource: true,
                isTarget: true,
                // åŠ¨æ€é”šç‚¹ã€æä¾›äº†4个方向 Continuous、AutoDefault
                anchor: 'Continuous',
                // è®¾ç½®è¿žçº¿ä¸Šé¢çš„label样式
                labelStyle: {
                    cssClass: 'flowLabel'
                },
                // ä¿®æ”¹äº†jsplumb æºç ï¼Œæ”¯æŒlabel ä¸ºç©ºä¼ å…¥è‡ªå®šä¹‰style
                emptyLabelStyle: {
                    cssClass: 'emptyFlowLabel'
                }
            },
            /**
             * æºç‚¹é…ç½®å‚æ•°
             */
            jsplumbSourceOptions: {
                // è®¾ç½®å¯ä»¥æ‹–拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
                filter: '.flow-node-drag',
                filterExclude: false,
                anchor: 'Continuous',
                // æ˜¯å¦å…è®¸è‡ªå·±è¿žæŽ¥è‡ªå·±
                allowLoopback: true,
                maxConnections: -1,
                onMaxConnections: function (info, e) {
                    console.log(`超过了最大值连线: ${info.maxConnections}`)
                }
            },
            // å‚考 https://www.cnblogs.com/mq0036/p/7942139.html
            jsplumbSourceOptions2: {
                // è®¾ç½®å¯ä»¥æ‹–拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
                filter: '.flow-node-drag',
                filterExclude: false,
                // anchor: 'Continuous',
                // æ˜¯å¦å…è®¸è‡ªå·±è¿žæŽ¥è‡ªå·±
                allowLoopback: true,
                connector: ['Flowchart', {curviness: 50}],
                connectorStyle: {
                    // çº¿çš„颜色
                    stroke: 'red',
                    // çº¿çš„粗细,值越大线越粗
                    strokeWidth: 1,
                    // è®¾ç½®å¤–边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
                    outlineStroke: 'transparent',
                    // çº¿å¤–边的宽,值越大,线的点击范围越大
                    outlineWidth: 10
                },
                connectorHoverStyle: {stroke: 'red', strokeWidth: 2}
            },
            jsplumbTargetOptions: {
                // è®¾ç½®å¯ä»¥æ‹–拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
                filter: '.flow-node-drag',
                filterExclude: false,
                // æ˜¯å¦å…è®¸è‡ªå·±è¿žæŽ¥è‡ªå·±
                anchor: 'Continuous',
                allowLoopback: true,
                dropOptions: {hoverClass: 'ef-drop-hover'}
            }
        }
    }
}
ÏîÄ¿´úÂë/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
            // æ·»åŠ è¯¥class可以推拽连线出来,viewOnly å¯ä»¥æŽ§åˆ¶èŠ‚ç‚¹æ˜¯å¦è¿è¡Œç¼–è¾‘
            nodeIcoClass['flow-node-drag'] = this.node.viewOnly ? false : true
            return nodeIcoClass
        }
    },
    methods: {
        // ç‚¹å‡»èŠ‚ç‚¹
        clickNode() {
            this.$emit('clickNode', this.node.id)
        },
        // é¼ æ ‡ç§»åŠ¨åŽæŠ¬èµ·
        changeNodeSite() {
            // é¿å…æŠ–动
            if (this.node.left == this.$refs.node.style.left && this.node.top == this.$refs.node.style.top) {
                return;
            }
            this.$emit('changeNodeSite', {
                nodeId: this.node.id,
                left: this.$refs.node.style.left,
                top: this.$refs.node.style.top,
            })
        }, delNode() {
            this.$emit("delNode");
        },
    }
}
</script>
<style  scoped>
/* .node-item{
    position: relative;
} */
.node-item:hover .el-icon-delete {
    display: inline-block !important;
}
.el-icon-delete {
    cursor: pointer;
    position: relative;
    top: -18px;
    padding-left: 5px;
    right: -16px;
    color: #f61313;
    height: 20px;
}
</style>
ÏîÄ¿´úÂë/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>自定义sql</label>
            <div><el-input type="textarea" v-model="customSql"></el-input></div>
        </div> -->
    </div>
</template>
<script>
let _this = this;
export default {
    props: {
        tableName: {
            type: String,
            default: ""
        },
        filters: {
            type: Array,
            default: () => {
                return []
            }
        },
        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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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',
                // ä¸ä½¿ç”¨H5原生的配置
                forceFallback: true,
                // æ‹–拽的时候样式
                // fallbackClass: 'flow-node-draggable'
            },
            // é»˜è®¤æ‰“开的左侧菜单的id
            defaultOpeneds: ['1', '2'],
            menuList: [
                {
                    id: '1',
                    type: 'group',
                    name: '开始节点',
                    ico: 'el-icon-video-play',
                    open: true,
                    children: [
                        {
                            id: '0',
                            type: 'start',
                            name: '流程开始',
                            ico: 'el-icon-time',
                            // è‡ªå®šä¹‰è¦†ç›–样式
                            style: {}
                        },
                        {
                            id: '1',
                            type: 'end',
                            name: '流程结束',
                            ico: 'el-icon-switch-button',
                            // è‡ªå®šä¹‰è¦†ç›–样式
                            style: {}
                        },   {
                            id: '2',
                            type: 'node',
                            name: '流程节点',
                            ico: 'el-icon-news',
                            // è‡ªå®šä¹‰è¦†ç›–样式
                            style: {}
                        }
                    ]
                }],
            nodeMenu: {}
        }
    },
    components: {
        draggable
    },
    created() {
        /**
         * ä»¥ä¸‹æ˜¯ä¸ºäº†è§£å†³åœ¨ç«ç‹æµè§ˆå™¨ä¸ŠæŽ¨æ‹½æ—¶å¼¹å‡ºtab页到搜索问题
         * @param event
         */
        if (this.isFirefox()) {
            document.body.ondrop = function (event) {
                // è§£å†³ç«ç‹æµè§ˆå™¨æ— æ³•获取鼠标拖拽结束的坐标问题
                mousePosition.left = event.layerX
                mousePosition.top = event.clientY - 50
                event.preventDefault();
                event.stopPropagation();
            }
        }
    },
    methods: {
        // æ ¹æ®ç±»åž‹èŽ·å–å·¦ä¾§èœå•å¯¹è±¡
        getMenuByType(type) {
            for (let i = 0; i < this.menuList.length; i++) {
                let children = this.menuList[i].children;
                for (let j = 0; j < children.length; j++) {
                    if (children[j].type === type) {
                        return children[j]
                    }
                }
            }
        },
        // æ‹–拽开始时触发
        move(evt, a, b, c) {
            var type = evt.item.attributes.type.nodeValue
            this.nodeMenu = this.getMenuByType(type)
        },
        // æ‹–拽结束时触发
        end(evt, e) {
            this.$emit('addNode', evt, this.nodeMenu, mousePosition)
        },
        // æ˜¯å¦æ˜¯ç«ç‹æµè§ˆå™¨
        isFirefox() {
            var userAgent = navigator.userAgent
            if (userAgent.indexOf("Firefox") > -1) {
                return true
            }
            return false
        }
    }
}
</script>
ÏîÄ¿´úÂë/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;">&nbsp;</div>
            </div>
            <!-- å³ä¾§è¡¨å• -->
            <div style="width: 400px;border-left: 1px solid #dce3e8;background-color: #FBFBFB">
                <el-scrollbar style="height: 100%;padding-bottom: 10px;">
                    <flow-node-form @delNode="deleteNode" ref="nodeForm" @setLineLabel="setLineLabel" :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 ã€line
                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>
ÏîÄ¿´úÂë/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)
}
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
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) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/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) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      return true;
    },
    searchAfter(result) {
      //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
      return true;
    },
    addBefore(formData) {
      //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
      return true;
    },
    updateBefore(formData) {
      //编辑保存前formData为对象,包括明细表、删除行的Id
      return true;
    },
    rowClick({ row, column, event }) {
      //查询界面点击行事件
      // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
    },
    modelOpenAfter(row) {
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
      //(3)this.editFormFields.字段='xxx';
      //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
      //看不懂就把输出看:console.log(this.editFormOptions)
    },
    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;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/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("没有配置好Url");
      }
      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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
import { h, resolveComponent } from 'vue';
let extension = {
    components: { //动态扩充组件或组件路径
        //表单header、content、footer对应位置扩充的组件
        //扩展组件引入方式
        gridHeader: '',
        gridBody: '',
        gridFooter: '',
        //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
        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 = "如果从数据库加载数据源,请按此格式配置sql语句:select orderType as key,orderName as value from order  å¦‚果需要根据用户信息加载数据源,请配置好此sql,再修改后台DictionaryHandler.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;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
let extension = {
    components: {//动态扩充组件或组件路径
        //表单header、content、footer对应位置扩充的组件
        gridHeader:'',
        gridbody:'',
        gridFooter: '',
        //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
        modelHeader: '',
        modelBody: '',
        modelFooter: ''
    },
    buttons: [],//扩展的按钮
    methods: {//事件扩展
        onInit() {
        },
        onInited() {
        }
    }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
import { h, resolveComponent } from 'vue';
let extension = {
  components: {
    //动态扩充组件或组件路径
    //表单header、content、footer对应位置扩充的组件
    gridHeader: "", //{ template: "<div>扩展组xxä»¶</div>" },
    gridBody: '',
    gridFooter: "",
    //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
    modelHeader: "",
    modelBody: "",
    modelFooter: ""
  },
  buttons: [], //扩展的按钮
  methods: {
    //事件扩展
    onInit() {
      console.log("sys_log")
      this.setFiexdSearchForm(true);
    },
    onInited() {
      this.height = this.height - 170;
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
let extension = {
  components: {//动态扩充组件或组件路径
    //表单header、content、footer对应位置扩充的组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
    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";
    },
    /***加载后台数据见Sys_RoleController.cs文件***/
    loadTreeChildren(tree, treeNode, resolve) { //加载子节点
      let url=`api/role/getTreeTableChildrenData?roleId=${tree.Role_Id}`;
      this.http.post(url,{}).then(result=>{
        resolve(result.rows)
      })
    },
      /***加载后台数据见Sys_RoleController.cs文件***/
    searchBefore(params){//判断加载根节点或子节点
      //没有查询条件,默认查询返回所有根节点数据
      if (!params.wheres.length) {
        params.value=1;
      }
      return true;
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
import { h, resolveComponent } from 'vue';
let extension = {
  components: {//动态扩充组件或组件路径
    //表单header、content、footer对应位置扩充的组件
    gridHeader: '',
    gridBody: {
      render () {
          return [
              h(resolveComponent('el-alert'), {
                  style: { 'margin-bottom': '12px' },
                  'show-icon': true, type: 'error',
                  closable: false, title: '关于TreeTable使用'
              }, ' treetable同样全部代码自动生成,页面生成后设置this.rowKe="xxx" tree主键字段,即可完成树形table配置,具体说明见Sys_Role1.js'),
          ]
      }
  },
    gridFooter: '',
    //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
    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";
    },
    /***加载后台数据见Sys_RoleController.cs文件***/
    loadTreeChildren(tree, treeNode, resolve) { //加载子节点
      let url=`api/role/getTreeTableChildrenData?roleId=${tree.Role_Id}`;
      this.http.post(url,{}).then(result=>{
        resolve(result.rows)
      })
    },
      /***加载后台数据见Sys_RoleController.cs文件***/
    searchBefore(params){//判断加载根节点或子节点
      //没有查询条件,默认查询返回所有根节点数据
      if (!params.wheres.length) {
        params.value=1;
      }
      return true;
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
import {  defineAsyncComponent } from "vue";
let extension = {
    components: { //动态扩充组件或组件路径
        //表单header、content、footer对应位置扩充的组件
        gridHeader: defineAsyncComponent(() =>
            import("./Sys_User/Sys_UserGridHeader.vue")),
        gridBody: '',
        gridFooter: '',
        //弹出框(修改、编辑、查看)header、content、footer对应位置扩充的组件
        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;
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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 ã€ä»£ç ç”Ÿæˆé¡µé¢ViewGrid】
**常用示例见:http://v2.volcore.xyz/document/vueDev
**后台操作见:http://v2.volcore.xyz/document/netCoreDev
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
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)
      })
    },
    /***加载后台数据见Sys_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) {
      //这里是动态addCurrnetRow属性记录当前点击的行数据,下面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) {
        //获取当前组织构架的所有父级id,用于设置新建时父级id的默认值
        //获取数据数据源
        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;
ÏîÄ¿´úÂë/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" v-if="false">切换视图</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="是否正常" 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: 2,
      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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
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) {
            //界面查询前,可以给param.wheres添加查询参数
            //返回false,则不会执行查询
            return true;
        },
        searchAfter(result) {
            //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
            return true;
        },
        addBefore(formData) {
            //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
            return true;
        },
        updateBefore(formData) {
            //编辑保存前formData为对象,包括明细表、删除行的Id
            return true;
        },
        rowClick({ row, column, event }) {
            //查询界面点击行事件
            // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
        },
        modelOpenAfter(row) {
            //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
            //(1)判断是编辑还是新建操作: this.currentAction=='Add';
            //(2)给弹出框设置默认值
            //(3)this.editFormFields.字段='xxx';
            //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
            //看不懂就把输出看:console.log(this.editFormOptions)
        }
    }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/extend/GetLocationStatus.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,292 @@
<template>
  <div>
    <vol-box
      v-model="showDetialBox"
      :lazy="true"
      width="1500px"
      :padding="15"
      title="货位状态变动记录"
    >
      <div class="box-table" style="margin-top: 1%">
        <el-table
          ref="singleTable"
          :data="tableData"
          style="width: 100%; height: 100%"
          highlight-current-row
          @row-click="handleRowClick"
          height="500px"
          @selection-change="handleSelectionChange"
        >
          >
          <!-- <el-table-column type="selection" width="55"> </el-table-column> -->
          <el-table-column
            label="序号"
            type="index"
            fixed="left"
            width="55"
            align="center"
          ></el-table-column>
          <el-table-column
            v-for="(item, index) in tableColumns.filter((x) => !x.hidden)"
            :key="index"
            :prop="item.field"
            :label="item.title"
            :width="item.width"
            align="center"
          >
            <template #default="scoped">
              <div v-if="item.type == 'icon'">
                <el-tooltip
                  class="item"
                  effect="dark"
                  :content="item.title"
                  placement="bottom"
                  ><el-button
                    type="text"
                    @click="tableButtonClick(scoped.row, item)"
                    ><i
                      :class="item.icon"
                      style="font-size: 22px"
                    ></i></el-button
                ></el-tooltip>
              </div>
              <div v-else-if="item.type == 'tag'">
                <el-tag size="small">
                  {{ getDictionary(scoped.row, item) }}
                </el-tag>
              </div>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </vol-box>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
export default {
  components: { VolBox },
  data() {
    return {
      showDetialBox: false,
      row: null,
      tableData: [],
      tableColumns: [
        {
          field: "locationId",
          title: "货位主键",
          type: "string",
          width: 90,
          align: "left",
        },
        {
          field: "locationCode",
          title: "货位编号",
          type: "string",
          align: "left",
        },
        {
          field: "beforeStatus",
          title: "变动前货位状态",
          type: "tag",
          width: 150,
          align: "left",
          bindKey: "locationStatusEnum",
        },
        {
          field: "afterStatus",
          title: "变动后货位状态",
          type: "tag",
          width: 150,
          align: "left",
          bindKey: "locationStatusEnum",
        },
        {
          field: "changeType",
          title: "变动类型",
          type: "tag",
          width: 100,
          align: "left",
          bindKey: "locationChangeType",
        },
        {
          field: "orderId",
          title: "单据主键",
          type: "string",
          width: 90,
          align: "left",
          hidden: true,
        },
        {
          field: "orderNo",
          title: "单据编号",
          type: "int",
          width: 160,
          align: "left",
        },
        {
          field: "orderDetailId",
          title: "单据明细主键",
          type: "string",
          width: 200,
          align: "left",
          hidden: true,
        },
        {
          field: "taskNum",
          title: "任务号",
          type: "string",
          width: 180,
          align: "left",
        },
        {
          field: "creater",
          title: "创建人",
          type: "string",
          width: 90,
          align: "left",
        },
        {
          field: "createDate",
          title: "创建时间",
          type: "datetime",
          width: 160,
          align: "left",
          sort: true,
        },
        {
          field: "modifier",
          title: "修改人",
          type: "string",
          width: 100,
          align: "left",
          hidden: true,
        },
        {
          field: "modifyDate",
          title: "修改时间",
          type: "datetime",
          width: 160,
          align: "left",
          hidden: true,
          sort: true,
        },
        {
          field: "remark",
          title: "备注",
          type: "string",
          width: 100,
          align: "left",
          hidden: true,
        },
      ],
      paginations: {
        sort: "CreateDate",
        order: "desc",
        Foots: "",
        total: 0,
        // 2020.08.29增加自定义分页条大小
        sizes: [30, 60, 100, 120],
        size: 30, // é»˜è®¤åˆ†é¡µå¤§å°
        Wheres: [],
        page: 1,
        rows: 30,
      },
      dictionaryList: null,
    };
  },
  methods: {
    open(row) {
      this.row = row;
      this.showDetialBox = true;
      this.getDetailData();
      this.getDictionaryData();
    },
    getDetailData() {
      this.http
        .post(
          "/api/LocationStatusChangeRecord/GetLocationState?id=" + this.row.id,
          {},
          true
        )
        .then((x) => {
          if (!x.status) return this.$message.error(x.message);
          this.tableData = x.data;
        });
    },
    getDictionaryData() {
      if (this.dictionaryList) {
        return;
      }
      var param = [];
      this.tableColumns.forEach((x) => {
        if (x.type == "tag" && x.bindKey != "") {
          param.push(x.bindKey);
        }
      });
      this.http
        .post("api/Sys_Dictionary/GetVueDictionary", param, "查询中")
        .then((x) => {
          if (x.length > 0) {
            this.dictionaryList = x;
          }
        });
    },
    getDictionary(row, column) {
      if (this.dictionaryList) {
        var item = this.dictionaryList.find((x) => x.dicNo == column.bindKey);
        if (item) {
          var dicItem = item.data.find((x) => x.key == row[column.field]);
          console.log(dicItem);
          if (dicItem) {
            return dicItem.value;
          } else {
            return row[column.field];
          }
        } else {
          return row[column.field];
        }
      }
    },
  },
  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: #e6a23c;
}
.el-table .success-row {
  background: #f0f9eb;
}
.el-table .error-row {
  background: #f56c6c;
}
</style>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/locationInfo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
import gridBody from './extend/GetLocationStatus.vue'
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: gridBody,
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
  buttons: { view: [], box: [], detail: [] }, //扩展的按钮
  methods: {
    //下面这些方法可以保留也可以删除
    onInit() {
      let EnableBtn = this.buttons.find(x => x.value == 'Enable');
      if (EnableBtn) {
        EnableBtn.onClick = function () {
          let rows = this.$refs.table.getSelected();
          if (rows.length == 0) return this.$error("请选择数据!");
          var keys = rows.map(x => { return x.id });
          this.http
            .post("api/LocationInfo/LocationEnableStatus", keys, "数据处理中")
            .then((x) => {
              if (!x.status) return this.$message.error(x.message);
              this.$message.success("操作成功");
              this.refresh();
            });
        }
      }
      let DisableBtn = this.buttons.find(x => x.value == 'Disable');
      if (DisableBtn) {
        DisableBtn.onClick = function () {
          let rows = this.$refs.table.getSelected();
          if (rows.length == 0) return this.$error("请选择数据!");
          var keys = rows.map(x => { return x.id });
          this.http
            .post("api/LocationInfo/LocationDisableStatus", keys, "数据处理中")
            .then((x) => {
              if (!x.status) return this.$message.error(x.message);
              this.$message.success("操作成功");
              this.refresh();
            });
        }
      }
      this.columns.push({
        field: '操作',
        title: '操作',
        width: 90,
        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) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      return true;
    },
    searchAfter(result) {
      //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
      return true;
    },
    addBefore(formData) {
      //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
      return true;
    },
    updateBefore(formData) {
      //编辑保存前formData为对象,包括明细表、删除行的Id
      return true;
    },
    rowClick({ row, column, event }) {
      //查询界面点击行事件
      this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
    },
    modelOpenAfter(row) {
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
      //(3)this.editFormFields.字段='xxx';
      //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
      //看不懂就把输出看:console.log(this.editFormOptions)
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfoDetail.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
      }
    }
  };
  export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfoDetail_Hty.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
/*****************************************************************************************
**  Author:jxx 2022
**  QQ:283591387
**完整文档见:http://v2.volcore.xyz/document/api ã€ä»£ç ç”Ÿæˆé¡µé¢ViewGrid】
**常用示例见:http://v2.volcore.xyz/document/vueDev
**后台操作见:http://v2.volcore.xyz/document/netCoreDev
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
  buttons: { view: [], box: [], detail: [] }, //扩展的按钮
  methods: {
     //下面这些方法可以保留也可以删除
    onInit() {  //框架初始化配置前,
        //示例:在按钮的最前面添加一个按钮
        //   this.buttons.unshift({  //也可以用push或者splice方法来修改buttons数组
        //     name: '按钮', //按钮名称
        //     icon: 'el-icon-document', //按钮图标vue2版本见iview文档icon,vue3版本见element ui文档icon(注意不是element puls文档)
        //     type: 'primary', //按钮样式vue2版本见iview文档button,vue3版本见element ui文档button
        //     onClick: function () {
        //       this.$Message.success('点击了按钮');
        //     }
        //   });
        //示例:设置修改新建、编辑弹出框字段标签的长度
        // this.boxOptions.labelWidth = 150;
    },
    onInited() {
      //框架初始化配置后
      //如果要配置明细表,在此方法操作
      //this.detailOptions.columns.forEach(column=>{ });
    },
    searchBefore(param) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      return true;
    },
    searchAfter(result) {
      //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
      return true;
    },
    addBefore(formData) {
      //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
      return true;
    },
    updateBefore(formData) {
      //编辑保存前formData为对象,包括明细表、删除行的Id
      return true;
    },
    rowClick({ row, column, event }) {
      //查询界面点击行事件
      // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
    },
    modelOpenAfter(row) {
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
      //(3)this.editFormFields.字段='xxx';
      //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
      //看不懂就把输出看:console.log(this.editFormOptions)
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/extension/wmsPart/stockInfo_Hty.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
/*****************************************************************************************
**  Author:jxx 2022
**  QQ:283591387
**完整文档见:http://v2.volcore.xyz/document/api ã€ä»£ç ç”Ÿæˆé¡µé¢ViewGrid】
**常用示例见:http://v2.volcore.xyz/document/vueDev
**后台操作见:http://v2.volcore.xyz/document/netCoreDev
*****************************************************************************************/
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
  buttons: { view: [], box: [], detail: [] }, //扩展的按钮
  methods: {
     //下面这些方法可以保留也可以删除
    onInit() {  //框架初始化配置前,
        //示例:在按钮的最前面添加一个按钮
        //   this.buttons.unshift({  //也可以用push或者splice方法来修改buttons数组
        //     name: '按钮', //按钮名称
        //     icon: 'el-icon-document', //按钮图标vue2版本见iview文档icon,vue3版本见element ui文档icon(注意不是element puls文档)
        //     type: 'primary', //按钮样式vue2版本见iview文档button,vue3版本见element ui文档button
        //     onClick: function () {
        //       this.$Message.success('点击了按钮');
        //     }
        //   });
        //示例:设置修改新建、编辑弹出框字段标签的长度
        // this.boxOptions.labelWidth = 150;
    },
    onInited() {
      //框架初始化配置后
      //如果要配置明细表,在此方法操作
      //this.detailOptions.columns.forEach(column=>{ });
    },
    searchBefore(param) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      return true;
    },
    searchAfter(result) {
      //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
      return true;
    },
    addBefore(formData) {
      //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
      return true;
    },
    updateBefore(formData) {
      //编辑保存前formData为对象,包括明细表、删除行的Id
      return true;
    },
    rowClick({ row, column, event }) {
      //查询界面点击行事件
      // this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
    },
    modelOpenAfter(row) {
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
      //(3)this.editFormFields.字段='xxx';
      //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
      //看不懂就把输出看:console.log(this.editFormOptions)
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/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, //是否开启signalR
    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,后台AliOSSController.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;
ÏîÄ¿´úÂë/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
ÏîÄ¿´úÂë/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
ÏîÄ¿´úÂë/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;
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/router/viewGird.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
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')
  }, {
    path: '/locationInfo',
    name: 'locationInfo',
    component: () => import('@/views/wmsPart/locationInfo.vue')
  }, {
    path: '/stockInfo',
    name: 'stockInfo',
    component: () => import('@/views/wmsPart/stockInfo.vue')
  }, {
    path: '/stockInfoDetail',
    name: 'stockInfoDetail',
    component: () => import('@/views/wmsPart/stockInfoDetail.vue')
  }, {
    path: '/stockInfo_Hty',
    name: 'stockInfo_Hty',
    component: () => import('@/views/wmsPart/stockInfo_Hty.vue')
  }, {
    path: '/stockInfoDetail_Hty',
    name: 'stockInfoDetail_Hty',
    component: () => import('@/views/wmsPart/stockInfoDetail_Hty.vue')
  }]
export default viewgird
ÏîÄ¿´úÂë/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);
    }
  }
})
ÏîÄ¿´úÂë/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];
  },
  //获取当前时间,time是否带时分秒
  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或http或ftp):// å¯æœ‰å¯æ— 
      "(([\\w_!~*'()\\.&=+$%-]+: )?[\\w_!~*'()\\.&=+$%-]+@)?" + // ftp的user@  å¯æœ‰å¯æ— 
      '(([0-9]{1,3}\\.){3}[0-9]{1,3}' + // IP形式的URL- 3位数字.3位数字.3位数字.3位数字
      '|' + // å…è®¸IP和DOMAIN(域名)
      '(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不区分大小写
    // å°†url做uri转码后再匹配,解除请求参数中的中文和空字符影响
    if (re.test(encodeURI(url))) {
      return true;
    }
    return false;
  },
  matchUrlIp(url, ip) {
    // url使用是否使用的当前ip
    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 ä»¥key/value传值
  // backGroundUrl åŽå°url,如果后台url直接从后台下载,其他全部通过点击a标签下载
  dowloadFile(url, fileName, header, backGroundUrl) {
    if (!url) return alert('此文件没有url不能下载');
    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、id与parentId这两个字段必须有
  // 2、树形tree需要注意Id与parentId循环依赖的问题
  // 3、callback每次生成一新的节点的时回调的方法
  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;
  },
  //获取所有子节点的id
  // 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);
    }
  });
}
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Client/src/views/Index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,819 @@
<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="collapse-menu"
          :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
        />
      </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">WMS</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 v-if="!item.icon">{{ item.text }}</span>
              <i v-else :class="item.icon"></i>
            </a>
          </div>
          <!--消息管理-->
          <div class="h-link" @click="messageModel = true">
            <a>
              <i class="el-icon-message-solid">
                <el-badge
                  :value="messageList.length"
                  :type="messageList.length > 0 ? 'danger' : 'success'"
                  class="item"
                  style="width: 10px"
                ></el-badge>
              </i>
            </a>
          </div>
          <div>
            <img class="user-header" :src="userImg" :onerror="errorImg" />
          </div>
          <div class="user">
            <span>{{ userTrueName }}</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 size="40%">
      <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,
  h
} from "vue";
import { useRouter, useRoute } from "vue-router";
import store from "../store/index";
import http from "@/../src/api/http.js";
import { ElNotification } from "element-plus";
export default defineComponent({
  components: {
    VolMenu,
    loading,
    Message
  },
  data() {
    return {
      allTabs: true,
      leftTabs: true,
      rightTabs: true,
      otherTabs: true,
      menuLeft: 0,
      menuTop: 0,
      client: null
      //  contextMenuVisible: false, // å³é”®å…³é—­æ˜¾/隐
    };
  },
  setup(props, context) {
    let client = ref(null);
    // èŽ·å–å…¨å±€å±žæ€§å’Œæ–¹æ³•
    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: "/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 userTrueName = 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 handleMessage = e => {
      let data = JSON.parse(e.data);
      messageList.push(data);
      ElNotification({
        title: data.title,
        message: h("i", { style: "color: teal" }, data.message),
        position: "bottom-right"
      });
    };
    const createSocket = url => {
      // åˆ›å»ºWebSocket连接
      //"ws://127.0.0.1:9295/admin"
      client = new WebSocket(url);
      client.onopen = function() {
        client.onmessage = handleMessage;
        store.commit("setWebsocket", client);
        console.log("WebSocket è¿žæŽ¥æˆåŠŸ");
      };
      client.onclose = function() {
        console.log("WebSocket è¿žæŽ¥å…³é—­");
        setTimeout(createSocket, 10000);
      };
      client.onerror = function() {};
    };
    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 == "#") {
        window.open("https://github.com/cq-panda/Vue.NetCore");
        return;
      }
      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时参数丢失的问题
        });
        //新打开的tab移至最后一个选项
        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正式版修改
      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;
      //记录当前选中的菜单index
      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": {
          // åˆ é™¤å…¶ä»–所有tab标签
          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;
        userTrueName.value = _userInfo.userTrueName;
        if (_userInfo.img) {
          userImg.value = _config.base.getImgSrc(_userInfo.img, http.ipAddress);
        }
      }
      createSocket(window.webConfig.webSocketUrl);
      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中设置是否开启signalR)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,
      userTrueName,
      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 ä¸ºå•个dom绑定鼠标右击事件
      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;
}
.item {
  margin-top: -20px;
  margin-right: 40px;
}
.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>
ÏîÄ¿´úÂë/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>正在登录...</span>
        </el-button>
      </div>
      <!-- è´¦å·ä¿¡æ¯ -->
      <!-- <div class="account-info">
        <p>演示账号:admin666 &nbsp; &nbsp;密码:123456</p>
        <p>本地账号:admin &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;密码:123456</p>
        <p><a href="https://jq.qq.com/?_wv=1027&k=Sqstuy0M" style="text-decoration: none"
            target="_blank">QQ3群:743852316</a>
          &nbsp; &nbsp;&nbsp; &nbsp;
          <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">京ICP备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, '正在登录....').then((result) => {
        if (!result.status) {
          loading.value = false;
          getVierificationCode();
          return $message.error(result.message);
        }
        $message.success('登录成功,正在跳转!');
        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>
ÏîÄ¿´úÂë/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>
ÏîÄ¿´úÂë/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列显示类型date(自动格式化)
{ "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: '放在【代码生成配置】列表的文件夹ID下,如果填入【0】就是一级目录' }],
      [{
        "title": "项目类库",
        "field": "namespace",
        "placeholder": "代码生成后的所在类库(可以自己提前在后台项目中创建一个.netcore类库)",
        "type": "select",
        "required": true,
        data: []
      }],
      [{ "title": "表中文名", "field": "columnCNName", "required": true, placeholder: "表对应的中文名字,界面上显示会用到" }],
      [{ "title": "实际表名", "field": "tableName", "required": true, placeholder: "数据库实际表名或者视图名(多表关联请创建视图再生成代码)" }],
      [{ "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": "代码生成存放的位置",
          "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项目所在绝对路径,到views文件夹,如:E:/app/src/views', colSize: 12 },
      // { "title": "app路径", "field": "appPath", type: "text", placeholder: 'uniapp项目所在绝对路径,到pages文件夹,如: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项目所在绝对路径,到views文件夹,如:E:/app/src/views' },
      // ]
    ]
  },
  //2021.01.09增加代码生成器设置table排序功能
  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: '编辑列标签宽度colSize', 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
在上述文件截断后对比
项目代码/WCS/WIDESEAWCS_Client/src/views/builder/coder.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/IviewCircle.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/chart-options.js 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/head_bg.png 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/bigdata/layout.less 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/chart.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/chartOptions.js 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/flex.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/formChart.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/charts/formOptions.js 项目代码/WCS/WIDESEAWCS_Client/src/views/home/home-chart-options.js 项目代码/WCS/WIDESEAWCS_Client/src/views/index/Message.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/index/MessageConfig.js 项目代码/WCS/WIDESEAWCS_Client/src/views/index/index.less 项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Permission.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Permission/RoleTree.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Menu.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/UserInfo.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/system/test.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/locationInfo.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfo.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfoDetail.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfoDetail_Hty.vue 项目代码/WCS/WIDESEAWCS_Client/src/views/wmsPart/stockInfo_Hty.vue 项目代码/WCS/WIDESEAWCS_Client/tests/unit/example.spec.js 项目代码/WCS/WIDESEAWCS_Client/vue.config.js 项目代码/WCS/WIDESEAWCS_Client/yarn.lock 项目代码/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v17/DocumentLayout.backup.json 项目代码/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v17/DocumentLayout.json 项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/System/Sys_Log.cs 项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/CustomProfile.cs 项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/wwwroot/WIDESEAWCS_DB.DBSeed.Json/Dt_DeviceInfo.tsv 项目代码/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/wwwroot/WIDESEAWCS_DB.DBSeed.Json/Dt_DispatchInfo.tsv