Browse Source

Merge branch 'zyh' into zyj

master
小久哥 3 years ago
parent
commit
37f811dcc7
  1. 0
      .env.demo
  2. 2
      .env.dev
  3. 59
      package.json
  4. 196
      src/api/api.js
  5. 189
      src/api/manage.js
  6. 273
      src/assets/less/JAreaLinkage.less
  7. 15
      src/assets/less/TableExpand.less
  8. 74
      src/assets/less/common.less
  9. 29
      src/assets/less/index.less
  10. 36
      src/components/Ellipsis/Ellipsis.vue
  11. 3
      src/components/Ellipsis/index.js
  12. 135
      src/components/ListColumnsSetter.vue
  13. 155
      src/components/jeecg/JAreaLinkage.vue
  14. 238
      src/components/jeecg/JCategorySelect.vue
  15. 43
      src/components/jeecg/JCheckbox.vue
  16. 429
      src/components/jeecg/JCodeEditor.vue
  17. 65
      src/components/jeecg/JCron.vue
  18. 85
      src/components/jeecg/JDate.vue
  19. 3123
      src/components/jeecg/JEditableTable.vue
  20. 29
      src/components/jeecg/JEllipsis.vue
  21. 61
      src/components/jeecg/JFormContainer.vue
  22. 202
      src/components/jeecg/JGraphicCode.vue
  23. 226
      src/components/jeecg/JImageUpload.vue
  24. 123
      src/components/jeecg/JImportModal.vue
  25. 100
      src/components/jeecg/JInput.vue
  26. 237
      src/components/jeecg/JModal/index.vue
  27. 66
      src/components/jeecg/JSelectMultiple.vue
  28. 116
      src/components/jeecg/JSlider.vue
  29. 638
      src/components/jeecg/JSuperQuery.vue
  30. 57
      src/components/jeecg/JSwitch.vue
  31. 199
      src/components/jeecg/JTreeDict.vue
  32. 260
      src/components/jeecg/JTreeSelect.vue
  33. 180
      src/components/jeecg/JTreeTable.vue
  34. 452
      src/components/jeecg/JUpload.vue
  35. 509
      src/components/jeecg/README.md
  36. 577
      src/components/jeecg/README_JEditableTable.md
  37. 9
      src/components/jeecg/index.js
  38. 113
      src/components/jeecg/minipop/JFilePop.vue
  39. 98
      src/components/jeecg/minipop/JInputPop.vue
  40. 928
      src/components/jeecg/modal/JCronModal.vue
  41. 334
      src/components/jeecgbiz/JSelectBizComponent/JSelectBizComponentModal.vue
  42. 36
      src/components/jeecgbiz/JSelectBizComponent/README.md
  43. 165
      src/components/jeecgbiz/JSelectBizComponent/index.vue
  44. 122
      src/components/jeecgbiz/JSelectDepart.vue
  45. 327
      src/components/jeecgbiz/JSelectList.vue
  46. 87
      src/components/jeecgbiz/JSelectMaterial.vue
  47. 47
      src/components/jeecgbiz/JSelectMultiUser.vue
  48. 37
      src/components/jeecgbiz/JSelectPosition.vue
  49. 38
      src/components/jeecgbiz/JSelectRole.vue
  50. 87
      src/components/jeecgbiz/JSelectSerialMaterial.vue
  51. 89
      src/components/jeecgbiz/JSelectUserByDep.vue
  52. 137
      src/components/jeecgbiz/README.md
  53. 248
      src/components/jeecgbiz/modal/JSelectBatchModal.vue
  54. 241
      src/components/jeecgbiz/modal/JSelectDepartModal.vue
  55. 370
      src/components/jeecgbiz/modal/JSelectMaterialModal.vue
  56. 262
      src/components/jeecgbiz/modal/JSelectSerialMaterialModal.vue
  57. 265
      src/components/jeecgbiz/modal/JSelectSnModal.vue
  58. 329
      src/components/jeecgbiz/modal/JSelectUserByDepModal.vue
  59. 122
      src/components/jeecgbiz/modal/SelectUserListModal.vue
  60. 36
      src/components/tools/DynamicNotice.vue
  61. 139
      src/components/tools/ShowAnnouncement.vue
  62. 15
      src/main.js
  63. 173
      src/mixins/JEditableTableMixin.js
  64. 438
      src/mixins/JeecgListMixin.js
  65. 82
      src/mixins/tableDragResizeMixin.js
  66. 176
      src/utils/JEditableTableUtil.js
  67. 40
      src/utils/mixin.js
  68. 115
      src/utils/props-util.js
  69. 669
      src/utils/util.js
  70. 8
      src/utils/validate.js
  71. 2
      src/views/bpm/processInstance/index.vue
  72. 216
      src/views/erp/bill/AllocationOutList.vue
  73. 216
      src/views/erp/bill/AssembleList.vue
  74. 216
      src/views/erp/bill/DisassembleList.vue
  75. 224
      src/views/erp/bill/InventoryReview.vue
  76. 234
      src/views/erp/bill/OtherInList.vue
  77. 234
      src/views/erp/bill/OtherOutList.vue
  78. 246
      src/views/erp/bill/PurchaseBackList.vue
  79. 255
      src/views/erp/bill/PurchaseInList.vue
  80. 230
      src/views/erp/bill/PurchaseOrderList.vue
  81. 244
      src/views/erp/bill/RetailBackList.vue
  82. 238
      src/views/erp/bill/RetailOutList.vue
  83. 246
      src/views/erp/bill/SaleBackList.vue
  84. 245
      src/views/erp/bill/SaleOrderList.vue
  85. 254
      src/views/erp/bill/SaleOutList.vue
  86. 112
      src/views/erp/bill/dialog/BatchSetDepot.vue
  87. 1376
      src/views/erp/bill/dialog/BillDetail.vue
  88. 71
      src/views/erp/bill/dialog/BillPrintIframe.vue
  89. 159
      src/views/erp/bill/dialog/LinkBillList.vue
  90. 188
      src/views/erp/bill/dialog/ManyAccountModal.vue
  91. 133
      src/views/erp/bill/mixins/BillListMixin.js
  92. 748
      src/views/erp/bill/mixins/BillModalMixin.js
  93. 239
      src/views/erp/bill/modules/AllocationOutModal.vue
  94. 258
      src/views/erp/bill/modules/AssembleModal.vue
  95. 257
      src/views/erp/bill/modules/DisassembleModal.vue
  96. 287
      src/views/erp/bill/modules/InventoryReviewModal.vue
  97. 269
      src/views/erp/bill/modules/OtherInModal.vue
  98. 267
      src/views/erp/bill/modules/OtherOutModal.vue
  99. 404
      src/views/erp/bill/modules/PurchaseBackModal.vue
  100. 564
      src/views/erp/bill/modules/PurchaseInModal.vue
  101. Some files were not shown because too many files have changed in this diff Show More

0
.env.demo1024 → .env.demo

2
.env.dev

@ -5,7 +5,7 @@ ENV = 'development'
VUE_APP_TITLE = 农场管理系统 VUE_APP_TITLE = 农场管理系统
# 农场管理系统/开发环境 # 农场管理系统/开发环境
VUE_APP_BASE_API = 'http://192.168.225.2' VUE_APP_BASE_API = 'http://192.168.10.129:48080'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true

59
package.json

@ -39,17 +39,17 @@
"url": "https://github.com/YunaiV/ruoyi-vue-pro" "url": "https://github.com/YunaiV/ruoyi-vue-pro"
}, },
"dependencies": { "dependencies": {
"@babel/parser": "^7.7.4",
"@riophae/vue-treeselect": "0.4.0", "@riophae/vue-treeselect": "0.4.0",
"axios": "0.24.0", "axios": "0.24.0",
"bpmn-js-token-simulation": "^0.10.0",
"cache-loader": "^2.0.1",
"clipboard": "2.0.8", "clipboard": "2.0.8",
"codemirror": "^5.49.2",
"core-js": "^3.21.1", "core-js": "^3.21.1",
"echarts": "4.9.0", "echarts": "4.9.0",
"echarts-gl": "^1.1.1", "echarts-gl": "^1.1.1",
"echarts-wordcloud": "^1.1.3", "echarts-wordcloud": "^1.1.3",
"vue-quill-editor": "^3.0.6",
"vue-ueditor-wrap": "^2.4.1",
"wangeditor": ">=3.0.0",
"element-ui": "^2.15.6", "element-ui": "^2.15.6",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.4.3", "fuse.js": "6.4.3",
@ -57,55 +57,68 @@
"js-beautify": "1.13.0", "js-beautify": "1.13.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.0.0-rc.1", "jsencrypt": "3.0.0-rc.1",
"less-loader": "^5.0.0",
"min-dash": "^3.5.2",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"qs": "^6.10.3",
"quill": "1.3.7", "quill": "1.3.7",
"screenfull": "5.0.2", "screenfull": "5.0.2",
"sortablejs": "1.10.2", "sortablejs": "1.10.2",
"throttle-debounce": "^2.1.0",
"vue": "2.6.12", "vue": "2.6.12",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "0.5.5", "vue-cropper": "0.5.5",
"vue-draggable-resizable": "^2.3.0",
"vue-meta": "^2.4.0", "vue-meta": "^2.4.0",
"vue-quill-editor": "^3.0.6",
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vue-ueditor-wrap": "^2.4.1",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",
"vuex": "3.1.0", "vuex": "3.1.0",
"bpmn-js-token-simulation": "^0.10.0", "wangeditor": ">=3.0.0",
"min-dash": "^3.5.2", "webpack": "^4.41.5",
"xlsx": "^0.14.3",
"xml-js": "^1.6.11", "xml-js": "^1.6.11",
"@babel/parser": "^7.7.4", "lodash.get": "^4.4.2",
"throttle-debounce": "^2.1.0", "lodash.pick": "^4.4.0",
"codemirror": "^5.49.2" "md5": "^2.2.1",
"mathjs": "^10.1.1",
"jquery": "^1.12.4",
"moment": "^2.21.0",
"vue-ls": "^3.2.0",
"ant-design-vue": "1.5.2"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "4.4.6", "@vue/cli-plugin-babel": "4.4.6",
"@vue/cli-plugin-eslint": "4.4.6", "@vue/cli-plugin-eslint": "4.4.6",
"@vue/cli-service": "4.4.6", "@vue/cli-service": "4.4.6",
"@vue/compiler-sfc": "^3.0.1",
"@vue/eslint-config-prettier": "^5.0.0",
"babel-eslint": "10.1.0", "babel-eslint": "10.1.0",
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"chalk": "4.1.0", "chalk": "4.1.0",
"compression-webpack-plugin": "5.0.2", "compression-webpack-plugin": "5.0.2",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "7.15.0", "eslint": "7.15.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-vue": "7.2.0", "eslint-plugin-vue": "7.2.0",
"fs-extra": "^8.1.0",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"sass": "1.32.13",
"runjs": "4.4.2", "runjs": "4.4.2",
"sass": "1.32.13",
"sass-loader": "10.1.1", "sass-loader": "10.1.1",
"sass-resources-loader": "^2.0.0",
"script-ext-html-webpack-plugin": "2.1.5", "script-ext-html-webpack-plugin": "2.1.5",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"svg-sprite-loader": "5.1.1", "svg-sprite-loader": "5.1.1",
"terser-webpack-plugin": "^4.2.3",
"vue-template-compiler": "2.6.12", "vue-template-compiler": "2.6.12",
"vue2-ace-editor": "^0.0.15", "vue2-ace-editor": "^0.0.15",
"@vue/compiler-sfc": "^3.0.1", "webpack-bundle-analyzer": "^3.9.0"
"@vue/eslint-config-prettier": "^5.0.0",
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"eslint-plugin-prettier": "^3.1.0",
"fs-extra": "^8.1.0",
"terser-webpack-plugin": "^4.2.3",
"webpack-bundle-analyzer": "^3.9.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.20.0",
"sass-resources-loader": "^2.0.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2"
}, },
"engines": { "engines": {
"node": ">=8.9", "node": ">=8.9",

196
src/api/api.js

@ -0,0 +1,196 @@
import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
//首页统计
const getBuyAndSaleStatistics = (params)=>getAction("/erp/depotHead/getBuyAndSaleStatistics",params);
const buyOrSalePrice = (params)=>getAction("/erp/depotItem/buyOrSalePrice",params);
//租户管理
const checkTenant = (params)=>getAction("/erp/tenant/checkIsNameExist",params);
const editTenant = (params)=>putAction("/erp/tenant/update",params);
//角色管理
const addRole = (params)=>postAction("/erp/role/add",params);
const editRole = (params)=>putAction("/erp/role/update",params);
const checkRole = (params)=>getAction("/erp/role/checkIsNameExist",params);
const roleAllList = (params)=>getAction("/erp/role/allList",params);
//用户管理
const registerUser = (params)=>postAction("/erp/user/registerUser",params);
const addUser = (params)=>postAction("/erp/user/addUser",params);
const editUser = (params)=>putAction("/erp/user/updateUser",params);
const getUserList = (params)=>getAction("/erp/user/getUserList",params);
const queryPermissionsByUser = (params)=>postAction("/erp/function/findMenuByPNumber",params);
//机构管理
const queryOrganizationTreeList = (params)=>getAction("/erp/organization/getOrganizationTree",params);
const queryOrganizationById = (params)=>getAction("/erp/organization/findById",params);
const checkOrganization = (params)=>getAction("/erp/organization/checkIsNameExist",params);
//经手人管理
const addPerson = (params)=>postAction("/erp/person/add",params);
const editPerson = (params)=>putAction("/erp/person/update",params);
const checkPerson = (params)=>getAction("/erp/person/checkIsNameExist",params);
const getPersonByType = (params)=>getAction("/erp/person/getPersonByType",params);
const getPersonByNumType = (params)=>getAction("/erp/person/getPersonByNumType",params);
//账户管理
const addAccount = (params)=>postAction("/erp/account/add",params);
const editAccount = (params)=>putAction("/erp/account/update",params);
const checkAccount = (params)=>getAction("/erp/account/checkIsNameExist",params);
const getAccount = (params)=>getAction("/erp/account/getAccount",params);
//收支项目
const addInOutItem = (params)=>postAction("/erp/inOutItem/add",params);
const editInOutItem = (params)=>putAction("/erp/inOutItem/update",params);
const checkInOutItem = (params)=>getAction("/erp/inOutItem/checkIsNameExist",params);
const findInOutItemByParam = (params)=>getAction("/erp/inOutItem/findBySelect",params);
//仓库信息
const addDepot = (params)=>postAction("/erp/depot/add",params);
const editDepot = (params)=>putAction("/erp/depot/update",params);
const checkDepot = (params)=>getAction("/erp/depot/checkIsNameExist",params);
//商品属性
const editMaterialProperty = (params)=>putAction("/erp/materialProperty/update",params);
//商品类型
const queryMaterialCategoryTreeList = (params)=>getAction("/erp/materialCategory/getMaterialCategoryTree",params);
const queryMaterialCategoryById = (params)=>getAction("/erp/materialCategory/findById",params);
const checkMaterialCategory = (params)=>getAction("/erp/materialCategory/checkIsNameExist",params);
//商品管理
const addMaterial = (params)=>postAction("/erp/material/add",params);
const editMaterial = (params)=>putAction("/erp/material/update",params);
const checkMaterial = (params)=>getAction("/erp/material/checkIsExist",params);
const getMaterialBySelect = (params)=>getAction("/erp/material/findBySelect",params);
const getSerialMaterialBySelect = (params)=>getAction("/erp/material/getMaterialEnableSerialNumberList",params);
const getMaterialByBarCode = (params)=>getAction("/erp/material/getMaterialByBarCode",params);
const getMaxBarCode = (params)=>getAction("/erp/material/getMaxBarCode",params);
const checkMaterialBarCode = (params)=>getAction("/erp/materialsExtend/checkIsBarCodeExist",params);
//序列号
const addSerialNumber = (params)=>postAction("/erp/serialNumber/add",params);
const editSerialNumber = (params)=>putAction("/erp/serialNumber/update",params);
const checkSerialNumber = (params)=>getAction("/erp/serialNumber/checkIsNameExist",params);
const batAddSerialNumber = (params)=>postAction("/erp/serialNumber/batAddSerialNumber",params);
const getEnableSerialNumberList = (params)=>getAction("/erp/serialNumber/getEnableSerialNumberList",params);
//多属性
const addMaterialAttribute = (params)=>postAction("/erp/materialAttribute/add",params);
const editMaterialAttribute = (params)=>putAction("/erp/materialAttribute/update",params);
const checkMaterialAttribute = (params)=>getAction("/erp/materialAttribute/checkIsNameExist",params);
const getAllMaterialAttribute = (params)=>getAction("/erp/materialAttribute/getAll",params);
//功能管理
const addFunction = (params)=>postAction("/erp/function/add",params);
const editFunction = (params)=>putAction("/erp/function/update",params);
const checkFunction = (params)=>getAction("/erp/function/checkIsNameExist",params);
//系统配置
const addSystemConfig = (params)=>postAction("/erp/systemConfig/add",params);
const editSystemConfig = (params)=>putAction("/erp/systemConfig/update",params);
const checkSystemConfig = (params)=>getAction("/erp/systemConfig/checkIsNameExist",params);
const getCurrentSystemConfig = (params)=>getAction("/erp/systemConfig/getCurrentInfo",params);
const fileSizeLimit = (params)=>getAction("/erp/systemConfig/fileSizeLimit",params);
//平台参数
const addPlatformConfig = (params)=>postAction("/erp/platformConfig/add",params);
const editPlatformConfig = (params)=>putAction("/erp/platformConfig/update",params);
const getPlatformConfigByKey = (params)=>getAction("/erp/platformConfig/getPlatformConfigByKey",params);
//用户|角色|模块关系
const addUserBusiness = (params)=>postAction("/erp/userBusiness/add",params);
const editUserBusiness = (params)=>putAction("/erp/userBusiness/update",params);
const checkUserBusiness = (params)=>getAction("/erp/userBusiness/checkIsValueExist",params);
const updateBtnStrByRoleId = (params)=>postAction("/erp/userBusiness/updateBtnStr",params);
//计量单位
const addUnit = (params)=>postAction("/erp/unit/add",params);
const editUnit = (params)=>putAction("/erp/unit/update",params);
const checkUnit = (params)=>getAction("/erp/unit/checkIsNameExist",params);
//供应商|客户|会员
const addSupplier = (params)=>postAction("/erp/supplier/add",params);
const editSupplier = (params)=>putAction("/erp/supplier/update",params);
const checkSupplier = (params)=>getAction("/erp/supplier/checkIsNameAndTypeExist",params);
const findBySelectSup = (params)=>postAction("/erp/supplier/findBySelect_sup",params);
const findBySelectCus = (params)=>postAction("/erp/supplier/findBySelect_cus",params);
const findBySelectRetail = (params)=>postAction("/erp/supplier/findBySelect_retail",params);
const findBySelectOrgan = (params)=>postAction("/erp/supplier/findBySelect_organ",params);
//单据相关
const findBillDetailByNumber = (params)=>getAction("/erp/depotHead/getDetailByNumber",params);
const findStockByDepotAndBarCode = (params)=>getAction("/erp/depotItem/findStockByDepotAndBarCode",params);
const getBatchNumberList = (params)=>getAction("/erp/depotItem/getBatchNumberList",params);
const findFinancialDetailByNumber = (params)=>getAction("/erp/accountHead/getDetailByNumber",params);
//租户信息
const getAlltenantInfo = (params)=>getAction("/erp/tenant/listAllTenant",params);
export {
getBuyAndSaleStatistics,
buyOrSalePrice,
checkTenant,
editTenant,
addRole,
editRole,
checkRole,
roleAllList,
registerUser,
addUser,
editUser,
getUserList,
queryPermissionsByUser,
queryOrganizationTreeList,
queryOrganizationById,
checkOrganization,
addPerson,
editPerson,
checkPerson,
getPersonByType,
getPersonByNumType,
addAccount,
editAccount,
checkAccount,
getAccount,
addInOutItem,
editInOutItem,
checkInOutItem,
findInOutItemByParam,
addDepot,
editDepot,
checkDepot,
editMaterialProperty,
queryMaterialCategoryTreeList,
queryMaterialCategoryById,
checkMaterialCategory,
addMaterial,
editMaterial,
checkMaterial,
getMaterialBySelect,
getSerialMaterialBySelect,
getMaterialByBarCode,
getMaxBarCode,
checkMaterialBarCode,
addSerialNumber,
editSerialNumber,
checkSerialNumber,
batAddSerialNumber,
getEnableSerialNumberList,
addMaterialAttribute,
editMaterialAttribute,
checkMaterialAttribute,
getAllMaterialAttribute,
addFunction,
editFunction,
checkFunction,
addSystemConfig,
editSystemConfig,
checkSystemConfig,
getCurrentSystemConfig,
fileSizeLimit,
addPlatformConfig,
editPlatformConfig,
getPlatformConfigByKey,
addUserBusiness,
editUserBusiness,
checkUserBusiness,
updateBtnStrByRoleId,
addUnit,
editUnit,
checkUnit,
addSupplier,
editSupplier,
checkSupplier,
findBySelectSup,
findBySelectCus,
findBySelectRetail,
findBySelectOrgan,
findBillDetailByNumber,
findStockByDepotAndBarCode,
getBatchNumberList,
findFinancialDetailByNumber,
getAlltenantInfo
}

189
src/api/manage.js

@ -0,0 +1,189 @@
import Vue from 'vue'
import request from '@/utils/request'
import { typeOf } from 'mathjs'
import Cookies from 'js-cookie'
const api = {
user: '/erp/api/user',
role: '/erp/api/role',
service: '/erp/api/service',
permission: '/erp/api/permission',
permissionNoPager: '/erp/api/permission/no-pager'
}
export default api
let startTime = Cookies.get('SEASON')?Cookies.get('SEASON').startTime:"2020-01-01 00:00:00";
//post
export function postAction(url,parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: url,
method:'post' ,
data: parameter
})
}
//post method= {post | put}
export function httpAction(url,parameter,method) {
parameter = {startTime:startTime,...parameter}
return request({
url: url,
method:method ,
data: parameter
})
}
//put
export function putAction(url,parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: url,
method:'put',
data: parameter
})
}
//get
export function getAction(url,parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: url,
method: 'get',
params: parameter
})
}
//deleteAction
export function deleteAction(url,parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: url,
method: 'delete',
params: parameter
})
}
export function getUserList(parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: api.user,
method: 'get',
params: parameter
})
}
export function getRoleList(parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: api.role,
method: 'get',
params: parameter
})
}
export function getServiceList(parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: api.service,
method: 'get',
params: parameter
})
}
export function getPermissions(parameter) {
parameter = {startTime:startTime,...parameter}
return request({
url: api.permissionNoPager,
method: 'get',
params: parameter
})
}
// id == 0 add post
// id != 0 update put
export function saveService(parameter) {
return request({
url: api.service,
method: parameter.id == 0 ? 'post' : 'put',
data: parameter
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFile(url,parameter){
return request({
url: url,
params: parameter,
method:'get' ,
responseType: 'blob'
})
}
/**
* 下载文件
* @param url 文件路径
* @param fileName 文件名
* @param parameter
* @returns {*}
*/
export function downloadFile(url, fileName, parameter) {
return downFile(url, parameter).then((data) => {
if (!data || data.size === 0) {
Vue.prototype['$message'].warning('文件下载失败')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName)
} else {
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) //下载完成移除元素
window.URL.revokeObjectURL(url) //释放掉blob对象
}
})
}
/**
* 文件上传 用于富文本上传图片
* @param url
* @param parameter
* @returns {*}
*/
export function uploadAction(url,parameter){
return request({
url: url,
data: parameter,
method:'post' ,
headers: {
'Content-Type': 'multipart/form-data', // 文件上传
},
})
}
/**
* 获取文件服务访问路径
* @param avatar
* @param subStr
* @returns {*}
*/
export function getFileAccessHttpUrl(avatar,subStr) {
if(!subStr) subStr = 'http'
if(avatar && avatar.startsWith(subStr)){
return avatar;
}else{
if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
return window._CONFIG['domianURL'] + "/" + avatar;
}
}
}

273
src/assets/less/JAreaLinkage.less

@ -0,0 +1,273 @@
.area-zoom-in-top-enter-active,
.area-zoom-in-top-leave-active {
opacity: 1;
transform: scaleY(1);
}
.area-zoom-in-top-enter,
.area-zoom-in-top-leave-active {
opacity: 0;
transform: scaleY(0);
}
.area-select {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: 'tnum';
position: relative;
outline: 0;
display: block;
background-color: #fff;
border: 1px solid #d9d9d9;
border-top-width: 1.02px;
border-radius: 4px;
outline: none;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.area-select-wrap .area-select {
display: inline-block;
}
.area-select * {
box-sizing: border-box;
}
.area-select:hover {
border-color: #40a9ff;
border-right-width: 1px !important;
outline: 0;
}
.area-select:active {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.area-select.small {
width: 126px;
}
.area-select.medium {
width: 160px;
}
.area-select.large {
width: 194px;
}
.area-select.is-disabled {
background: #eceff5;
cursor: not-allowed;
}
.area-select.is-disabled:hover {
border-color: #e1e2e6;
}
.area-select.is-disabled .area-selected-trigger {
cursor: not-allowed;
}
.area-select .area-selected-trigger {
position: relative;
display: block;
font-size: 14px;
cursor: pointer;
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
height: 100%;
padding: 8px 20px 7px 12px;
}
.area-select .area-select-icon {
position: absolute;
top: 50%;
margin-top: -2px;
right: 6px;
content: "";
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: rgba(0, 0, 0, 0.25);
transition: all .3s linear;
transform-origin: center;
}
.area-select .area-select-icon.active {
margin-top: -8px;
transform: rotate(180deg);
}
.area-selectable-list-wrap {
position: absolute;
width: 100%;
max-height: 275px;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow-x: auto;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.area-selectable-list {
position: relative;
margin: 0;
padding: 6px 0;
width: 100%;
font-size: 14px;
color: #565656;
list-style: none;
}
.area-selectable-list .area-select-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.area-selectable-list .area-select-option.hover {
background-color: #e6f7ff;
}
.area-selectable-list .area-select-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list-wrap {
position: absolute;
white-space: nowrap;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
font-size: 0;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.cascader-menu-list {
position: relative;
margin: 0;
font-size: 14px;
color: #565656;
padding: 6px 0;
list-style: none;
display: inline-block;
height: 204px;
overflow-x: hidden;
overflow-y: auto;
min-width: 160px;
vertical-align: top;
background-color: #fff;
border-right: 1px solid #e4e7ed;
}
.cascader-menu-list:last-child {
border-right: none;
}
.cascader-menu-list .cascader-menu-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.cascader-menu-list .cascader-menu-option.hover,
.cascader-menu-list .cascader-menu-option:hover {
background-color: #e6f7ff;
}
.cascader-menu-list .cascader-menu-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list .cascader-menu-option.cascader-menu-extensible:after {
position: absolute;
top: 50%;
margin-top: -4px;
right: 5px;
content: "";
width: 0;
height: 0;
border: 4px solid transparent;
border-left-color: #a1a4ad;
}
.cascader-menu-list::-webkit-scrollbar,
.area-selectable-list-wrap::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:decremen,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:end:decrement,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:increment,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:start:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:decremen,
.cascader-menu-list::-webkit-scrollbar-button:vertical:end:decrement,
.cascader-menu-list::-webkit-scrollbar-button:vertical:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:start:increment {
display: none;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical {
background-color: #b8b8b8;
border-radius: 4px;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
background-color: #777;
}
.resize-table-th {
position: relative;
.table-draggable-handle {
transform: none !important;
position: absolute;
height: 100% !important;
bottom: 0;
left: auto !important;
right: -5px;
cursor: col-resize;
touch-action: none;
}
}

15
src/assets/less/TableExpand.less

@ -0,0 +1,15 @@
/** [表格主题样式一] 表格强制列不换行 */
.j-table-force-nowrap {
td, th {
white-space: nowrap;
}
.ant-table-selection-column {
padding: 12px 22px !important;
}
/** 列自适应,弊端会导致列宽失效 */
&.ant-table-wrapper .ant-table-content {
overflow-x: auto;
}
}

74
src/assets/less/common.less

@ -0,0 +1,74 @@
/*列表上方操作按钮区域*/
.ant-card-body .table-operator {
margin-bottom: 0px;
}
/** Button按钮间距 */
.table-operator .ant-btn {
margin: 0 8px 8px 0;
}
.table-operator .ant-btn-group .ant-btn {
margin: 0;
}
.table-operator .ant-btn-group .ant-btn:last-child {
margin: 0 8px 8px 0;
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
/*列表页面弹出modal*/
.ant-modal-cust-warp {
height: 100%
}
/*弹出modal Y轴滚动条*/
.ant-modal-cust-warp .ant-modal-body {
padding: 24px 24px 12px 24px;
height: calc(100% - 110px) !important;
overflow-y: auto
}
/*弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-cust-warp .ant-modal-content {
height: 90%;
overflow-y: hidden
}
/*文本框样式*/
.ant-modal-cust-warp .ant-form-item {
margin-bottom: 12px;
}
/*商品-列表页面弹出modal*/
.ant-modal-material-warp {
height: 100%
}
/*商品-弹出modal Y轴滚动条*/
.ant-modal-material-warp .ant-modal-body {
padding: 12px 24px 12px 24px;
height: calc(100% - 110px) !important;
overflow-y: auto
}
/*商品-弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-material-warp .ant-modal-content {
height: 90%;
overflow-y: hidden
}
/*商品-文本框样式*/
.ant-modal-material-warp .ant-form-item {
margin-bottom: 12px;
}
/*列表中有图片的加这个样式 参考用户管理*/
.anty-img-wrap {
height: 25px;
position: relative;
}
.anty-img-wrap > img {
max-height: 100%;
}
/*列表中范围查询样式*/
.query-group-cust{width: calc(50% - 10px)}
.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}
/*erp风格子表外框padding设置*/
.ant-card-wider-padding.cust-erp-sub-tab>.ant-card-body{padding:5px 12px}
/* 内嵌子表背景颜色 */
.j-inner-table-wrapper /deep/ .ant-table-expanded-row .ant-table-wrapper .ant-table-tbody .ant-table-row {
background-color: #FFFFFF;
}

29
src/assets/less/index.less

@ -0,0 +1,29 @@
/**
* 列表查询通用样式,移动端自适应
*/
.search{
margin-bottom: 54px;
}
.fold{
width: calc(100% - 216px);
display: inline-block
}
.operator{
margin-bottom: 18px;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
.operator button {
margin-right: 5px;
}
i {
cursor: pointer;
}
.trcolor{
background-color: rgba(255, 192, 203, 0.31);
color:red;
}

36
src/components/Ellipsis/Ellipsis.vue

@ -0,0 +1,36 @@
<script>
export default {
name: 'Ellipsis',
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean,
default: true,
},
length: {
type: Number,
default: 25,
},
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
},
methods: {},
render() {
const { tooltip, length } = this.$props
let text = ''
// default
if (this.$slots.default) {
text = this.$slots.default.map(vNode => vNode.text).join('')
}
}
}
</script>

3
src/components/Ellipsis/index.js

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

135
src/components/ListColumnsSetter.vue

@ -0,0 +1,135 @@
<template>
<a-popover title="自定义列" trigger="click" placement="leftBottom">
<template slot="content">
<a-checkbox-group @change="onColSettingsChange" v-model="settingColumns"
:defaultValue="settingColumns" style="width: 400px;" >
<a-row>
<template v-for="(item,index) in defColumns_">
<template v-if="!ignoreColumns.includes(item.key) && !ignoreColumns.includes(item.dataIndex)">
<a-col :span="12"><a-checkbox :value="item.dataIndex" >{{ item.title }}</a-checkbox></a-col>
</template>
</template>
</a-row>
</a-checkbox-group>
</template>
<a-button type="link" icon="setting">自定义列</a-button>
</a-popover>
</template>
<script>
import Vue from "vue";
export default {
name: "ListColumnsSetter",
props: {
columns:{
type:Array,
required: true
},
defColumns:{
type:Array,
required: true
},
ignoreColumns:{
type: Array,
default: () => ['rowIndex', 'action', 'flag']
},
listName:{
type:String,
default: '',
}
},
model: {
prop: 'columns',
event: 'change'
},
data() {
return {
defColumns_: [],
settingColumns:[]
}
},
computed: {
ls_key() { return this.$route.name + '/' + this.listName + ':colsettings'},
},
mounted() {
this.defColumns_.push(...this.defColumns);
this.initColumns();
},
methods: {
initColumns(){
//
//this.defColumns_ = colAuthFilter(this.defColumns_,'testdemo:');
console.log(this.ls_key)
let colSettings = Vue.ls.get(this.ls_key);
let columns = [];
if(colSettings===null||colSettings===undefined){
let allSettingColumns = [];
this.defColumns_.forEach(function (item,i,array ) {
allSettingColumns.push(item.dataIndex);
})
this.settingColumns = allSettingColumns;
columns = this.defColumns_;
} else {
this.settingColumns = colSettings;
//
// this.settingColumns.forEach((item,index) => {
// if(item == "model") {
// this.settingColumns.splice(index,1)
// }
// if(item == "color") {
// this.settingColumns.splice(index,1)
// }
// if(item == "categoryName") {
// this.settingColumns.splice(index,1)
// }
// if(item == "materialOther") {
// this.settingColumns.splice(index,1)
// }
// if(item == "commodityDecimal") {
// this.settingColumns.splice(index,1)
// }
// if(item == "lowDecimal") {
// this.settingColumns.splice(index,1)
// }
// })
columns = this.defColumns_.filter(item => {
if(this.ignoreColumns.includes(item.key) || this.ignoreColumns.includes(item.dataIndex)){
return true;
}
if (colSettings.includes(item.dataIndex)) {
return true;
}
return false;
})
}
this.$emit('change', columns);
},
//
onColSettingsChange (checkedValues) {
console.log("checkedValues",checkedValues);
Vue.ls.set(this.ls_key, checkedValues, 7*24*60*60*1000)
this.settingColumns = checkedValues;
const columns = this.defColumns_.filter(item => {
if(this.ignoreColumns.includes(item.key) || this.ignoreColumns.includes(item.dataIndex)){
return true
}
if (this.settingColumns.includes(item.dataIndex)) {
return true
}
return false
})
this.$emit('change', columns);
},
},
}
</script>

155
src/components/jeecg/JAreaLinkage.vue

@ -0,0 +1,155 @@
<template>
<div v-if="!reloading" class="j-area-linkage">
<area-cascader
v-if="_type === enums.type[0]"
:value="innerValue"
:data="pcaa"
:level="1"
:style="{width}"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
/>
<area-select
v-else-if="_type === enums.type[1]"
:value="innerValue"
:data="pcaa"
:level="2"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
/>
<div v-else>
<span style="color:red;"> Bad type value: {{_type}}</span>
</div>
</div>
</template>
<script>
import { pcaa } from 'area-data'
export default {
name: 'JAreaLinkage',
props: {
value: {
type: String,
required:false
},
//
// select
// cascader
type: {
type: String,
default: 'cascader'
},
width: {
type: String,
default: '100%'
}
},
data() {
return {
pcaa,
innerValue: [],
usedListeners: ['change'],
enums: {
type: ['cascader', 'select']
},
reloading: false,
areaData:''
}
},
computed: {
_listeners() {
let listeners = { ...this.$listeners }
// 使
this.usedListeners.forEach(key => {
delete listeners[key]
})
return listeners
},
_type() {
if (this.enums.type.includes(this.type)) {
return this.type
} else {
console.error(`JAreaLinkage的type属性只能接收指定的值(${this.enums.type.join('|')}`)
return this.enums.type[0]
}
},
},
watch: {
value: {
immediate: true,
handler() {
this.loadDataByValue(this.value)
}
},
},
created() {
this.initAreaData();
},
methods: {
/** 通过 value 反推 options */
loadDataByValue(value) {
if(!value || value.length==0){
this.innerValue = []
this.reloading = true;
setTimeout(()=>{
this.reloading = false
},100)
}else{
this.initAreaData();
let arr = this.areaData.getRealCode(value);
this.innerValue = arr
}
},
/** 通过地区code获取子级 */
loadDataByCode(value) {
let options = []
let data = pcaa[value]
if (data) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
options.push({ value: key, label: data[key], })
}
}
return options
} else {
return []
}
},
/** 判断是否有子节点 */
hasChildren(options) {
options.forEach(option => {
let data = this.loadDataByCode(option.value)
option.isLeaf = data.length === 0
})
},
handleChange(values) {
let value = values[values.length - 1]
this.$emit('change', value)
},
initAreaData(){
if(!this.areaData){
this.areaData = new Area();
}
},
},
model: { prop: 'value', event: 'change' },
}
</script>
<style lang="less" scoped>
.j-area-linkage {
height:40px;
/deep/ .area-cascader-wrap .area-select {
width: 100%;
}
/deep/ .area-select .area-selected-trigger {
line-height: 1.15;
}
}
</style>

238
src/components/jeecg/JCategorySelect.vue

@ -0,0 +1,238 @@
<template>
<a-tree-select
allowClear
labelInValue
style="width: 100%"
:disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="placeholder"
:loadData="asyncLoadTreeData"
:value="treeValue"
:treeData="treeData"
:multiple="multiple"
@change="onChange">
</a-tree-select>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JCategorySelect',
props: {
value:{
type: String,
required: false
},
placeholder:{
type: String,
default: '请选择',
required: false
},
disabled:{
type:Boolean,
default:false,
required:false
},
condition:{
type:String,
default:'',
required:false
},
//
multiple: {
type: Boolean,
default: false,
},
loadTriggleChange:{
type: Boolean,
default: false,
required:false
},
pid:{
type:String,
default:'',
required:false
},
pcode:{
type:String,
default:'',
required:false
},
back:{
type:String,
default:'',
required:false
}
},
data () {
return {
treeValue:"",
treeData:[],
url:"/sys/category/loadTreeData",
view:'/sys/category/loadDictItem/',
tableName:"",
text:"",
code:"",
}
},
watch: {
value () {
this.loadItemByCode()
},
pcode(){
this.loadRoot();
}
},
created(){
this.validateProp().then(()=>{
this.loadRoot()
this.loadItemByCode()
})
},
methods: {
/**加载一级节点 */
loadRoot(){
let param = {
pid:this.pid,
pcode:!this.pcode?'0':this.pcode,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success && res.result){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.treeData = [...res.result]
}else{
console.log("树一级节点查询结果-else",res)
}
})
},
/** 数据回显*/
loadItemByCode(){
if(!this.value || this.value=="0"){
this.treeValue = []
}else{
getAction(this.view,{ids:this.value}).then(res=>{
if(res.success){
let values = this.value.split(',')
this.treeValue = res.result.map((item, index) => ({
key: values[index],
value: values[index],
label: item
}))
this.onLoadTriggleChange(res.result[0]);
}
})
}
},
onLoadTriggleChange(text){
//
if(!this.multiple && this.loadTriggleChange){
this.backValue(this.value,text)
}
},
backValue(value,label){
let obj = {}
if(this.back){
obj[this.back] = label
}
this.$emit('change', value, obj)
},
asyncLoadTreeData (treeNode) {
return new Promise((resolve) => {
if (treeNode.$vnode.children) {
resolve()
return
}
let pid = treeNode.$vnode.key
let param = {
pid:pid,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.addChildren(pid,res.result,this.treeData)
this.treeData = [...this.treeData]
}
resolve()
})
})
},
addChildren(pid,children,treeArray){
if(treeArray && treeArray.length>0){
for(let item of treeArray){
if(item.key == pid){
if(!children || children.length==0){
item.isLeaf=true
}else{
item.children = children
}
break
}else{
this.addChildren(pid,children,item.children)
}
}
}
},
onChange(value){
if(!value){
this.$emit('change', '');
this.treeValue = ''
} else if (value instanceof Array) {
//this.$emit('change', value.map(item => item.value).join(','))
//this.treeValue = value
} else {
this.backValue(value.value,value.label)
this.treeValue = value
}
},
getCurrTreeData(){
return this.treeData
},
validateProp(){
let mycondition = this.condition
return new Promise((resolve,reject)=>{
if(!mycondition){
resolve();
}else{
try {
let test=JSON.parse(mycondition);
if(typeof test == 'object' && test){
resolve()
}else{
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
reject()
}
} catch(e) {
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
reject()
}
}
})
}
},
//2.2
model: {
prop: 'value',
event: 'change'
}
}
</script>

43
src/components/jeecg/JCheckbox.vue

@ -0,0 +1,43 @@
<template>
<a-checkbox-group :options="options" :value="checkboxArray" v-bind="$attrs" @change="onChange" />
</template>
<script>
export default {
name: 'JCheckbox',
props: {
value:{
type: String,
required: false
},
/*label value*/
options:{
type: Array,
required: true
}
},
data(){
return {
checkboxArray:!this.value?[]:this.value.split(",")
}
},
watch:{
value (val) {
if(!val){
this.checkboxArray = []
}else{
this.checkboxArray = this.value.split(",")
}
}
},
methods:{
onChange (checkedValues) {
this.$emit('change', checkedValues.join(","));
},
},
model: {
prop: 'value',
event: 'change'
}
}
</script>

429
src/components/jeecg/JCodeEditor.vue

@ -0,0 +1,429 @@
<template>
<div v-bind="fullScreenParentProps">
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
<div class="code-editor-cust full-screen-child">
<textarea ref="textarea"></textarea>
<span @click="nullTipClick" class="null-tip" :class="{'null-tip-hidden':hasCode}" :style="nullTipStyle">{{ placeholderShow }}</span>
<template v-if="languageChange">
<a-select v-model="mode" size="small" class="code-mode-select" @change="changeMode" placeholder="请选择主题">
<a-select-option
v-for="mode in modes"
:key="mode.value"
:value="mode.value">
{{ mode.label }}
</a-select-option>
</a-select>
</template>
</div>
</div>
</template>
<script type="text/ecmascript-6">
//
import _CodeMirror from 'codemirror'
//
import 'codemirror/lib/codemirror.css'
// options darcula gruvbox-dark hopscotch monokai
import 'codemirror/theme/panda-syntax.css'
//css
import "codemirror/addon/hint/show-hint.css";
//
// codemirror /addon/mode/loadmode.js /mode/meta.js
// vue JS JS
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/mode/css/css.js'
import 'codemirror/mode/xml/xml.js'
import 'codemirror/mode/clike/clike.js'
import 'codemirror/mode/markdown/markdown.js'
import 'codemirror/mode/python/python.js'
import 'codemirror/mode/r/r.js'
import 'codemirror/mode/shell/shell.js'
import 'codemirror/mode/sql/sql.js'
import 'codemirror/mode/swift/swift.js'
import 'codemirror/mode/vue/vue.js'
//
const CodeMirror = window.CodeMirror || _CodeMirror
export default {
name: 'JCodeEditor',
props: {
//
value: {
type: String,
default: ''
},
//
language: {
type: String,
default: null
},
languageChange:{
type: Boolean,
default:false,
required:false
},
placeholder: {
type: String,
default: null
},
//
lineNumbers: {
type: Boolean,
default: true
},
//
fullScreen: {
type: Boolean,
default: false
},
// z-index
zIndex: {
type: [Number, String],
default: 999
}
},
data () {
return {
//
code: '',
iconType: 'fullscreen',
hasCode:false,
//
mode: 'javascript',
//
coder: null,
//
options: {
//
tabSize: 2,
// JS
theme: 'panda-syntax',
line: true,
// extraKeys: {'Ctrl': 'autocomplete'},//
hintOptions: {
tables: {
users: ['name', 'score', 'birthDate'],
countries: ['name', 'population', 'size']
}
},
},
// JS
// 使 MIME-TYPE text/
modes: [{
value: 'css',
label: 'CSS'
}, {
value: 'javascript',
label: 'Javascript'
}, {
value: 'html',
label: 'XML/HTML'
}, {
value: 'x-java',
label: 'Java'
}, {
value: 'x-objectivec',
label: 'Objective-C'
}, {
value: 'x-python',
label: 'Python'
}, {
value: 'x-rsrc',
label: 'R'
}, {
value: 'x-sh',
label: 'Shell'
}, {
value: 'x-sql',
label: 'SQL'
}, {
value: 'x-swift',
label: 'Swift'
}, {
value: 'x-vue',
label: 'Vue'
}, {
value: 'markdown',
label: 'Markdown'
}],
// code
fullCoder: false
}
},
watch: {
fullCoder:{
handler(value) {
if(value){
this.iconType="fullscreen-exit"
}else{
this.iconType="fullscreen"
}
}
},
// value: {
// immediate: false,
// handler(value) {
// this._getCoder().then(() => {
// this.coder.setValue(value)
// })
// }
// },
language: {
immediate: true,
handler(language) {
this._getCoder().then(() => {
//
if (language) {
//
let modeObj = this._getLanguage(language)
//
if (modeObj) {
this.mode = modeObj.label
this.coder.setOption('mode', `text/${modeObj.value}`)
}
}
})
}
}
},
computed: {
placeholderShow() {
if (this.placeholder == null) {
return `请在此输入${this.language}代码`
} else {
return this.placeholder
}
},
nullTipStyle(){
if (this.lineNumbers) {
return { left: '36px' }
} else {
return { left: '12px' }
}
},
// coder
coderOptions() {
return {
tabSize: this.options.tabSize,
theme: this.options.theme,
lineNumbers: this.lineNumbers,
line: true,
hintOptions: this.options.hintOptions
}
},
fullScreenParentProps(){
let props = {
class: ['full-screen-parent', this.fullCoder ? 'full-screen' : ''],
style: {}
}
if (this.fullCoder) {
props.style['z-index'] = this.zIndex
}
return props
}
},
mounted () {
//
this._initialize()
},
methods: {
//
_initialize () {
//
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, this.coderOptions)
//
if(this.value||this.code){
this.hasCode=true
this.coder.setValue(this.value || this.code)
}else{
this.coder.setValue('')
this.hasCode=false
}
//
this.coder.on('change', (coder) => {
this.code = coder.getValue()
if(this.code){
this.hasCode=true
}else{
this.hasCode=false
}
if (this.$emit) {
this.$emit('input', this.code)
}
})
this.coder.on('focus', () => {
this.hasCode=true
})
this.coder.on('blur', () => {
if(this.code){
this.hasCode=true
}else{
this.hasCode=false
}
})
/* this.coder.on('cursorActivity',()=>{
this.coder.showHint()
})*/
},
getCodeContent(){
return this.code
},
setCodeContent(val){
setTimeout(()=>{
if(!val){
this.coder.setValue('')
}else{
this.coder.setValue(val)
}
},300)
},
//
_getLanguage (language) {
//
return this.modes.find((mode) => {
// 便
let currentLanguage = language.toLowerCase()
let currentLabel = mode.label.toLowerCase()
let currentValue = mode.value.toLowerCase()
// java x-java value label
return currentLabel === currentLanguage || currentValue === currentLanguage
})
},
_getCoder() {
let _this = this
return new Promise((resolve) => {
(function get() {
if (_this.coder) {
resolve(_this.coder)
} else {
setTimeout(get, 10)
}
})()
})
},
//
changeMode (val) {
//
this.coder.setOption('mode', `text/${val}`)
//
let label = this._getLanguage(val).label.toLowerCase()
//
this.$emit('language-change', label)
},
nullTipClick(){
this.coder.focus()
}
}
}
</script>
<style lang="less">
.code-editor-cust{
flex-grow:1;
display:flex;
position:relative;
height:100%;
.CodeMirror{
flex-grow:1;
z-index:1;
.CodeMirror-code{
line-height:19px;
}
}
.code-mode-select{
position:absolute;
z-index:2;
right:10px;
top:10px;
max-width:130px;
}
.CodeMirror{
height: auto;
min-height:100%;
}
.null-tip{
position: absolute;
top: 4px;
left: 36px;
z-index: 10;
color: #ffffffc9;
line-height: initial;
}
.null-tip-hidden{
display: none;
}
}
/* 全屏样式 */
.full-screen-parent {
position: relative;
.full-screen-icon {
opacity: 0;
color: black;
width: 20px;
height: 20px;
line-height: 24px;
background-color: white;
position: absolute;
top: 2px;
right: 2px;
z-index: 9;
cursor: pointer;
transition: opacity 0.3s;
}
&:hover {
.full-screen-icon {
opacity: 1;
&:hover {
background-color: rgba(255, 255, 255, 0.88);
}
}
}
&.full-screen {
position: fixed;
top: 10px;
left: 10px;
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
background-color: #f5f5f5;
.full-screen-icon {
top: 12px;
right: 12px;
}
.full-screen-child {
height: 100%;
max-height: 100%;
min-height: 100%;
}
}
.full-screen-child {
min-height: 120px;
max-height: 320px;
overflow:hidden;
}
}
.CodeMirror-cursor{
height:18.4px !important;
}
</style>

65
src/components/jeecg/JCron.vue

@ -0,0 +1,65 @@
<template>
<div class="components-input-demo-presuffix">
<a-input @click="openModal" placeholder="corn表达式" v-model="cron" @change="handleOK">
<a-icon slot="prefix" type="schedule" title="corn控件"/>
<a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<JCronModal ref="innerVueCron" :data="cron" @ok="handleOK"></JCronModal>
</div>
</template>
<script>
import JCronModal from "./modal/JCronModal";
export default {
name: 'JCron',
components: {
JCronModal
},
props: {
value: {
required: false,
type: String,
}
},
data(){
return {
cron: this.value,
}
},
watch:{
value(val){
this.cron = val
}
},
methods:{
openModal(){
this.$refs.innerVueCron.show();
},
handleOK(val){
this.cron = val;
this.$emit("change", this.cron);
//this.$emit("change", Object.assign({}, this.cron));
},
handleEmpty(){
this.handleOK('')
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>

85
src/components/jeecg/JDate.vue

@ -0,0 +1,85 @@
<template>
<a-date-picker
:disabled="disabled || readOnly"
:placeholder="placeholder"
@change="handleDateChange"
:value="momVal"
:showTime="showTime"
:format="dateFormat"
:getCalendarContainer="getCalendarContainer"
/>
</template>
<script>
import moment from 'moment'
export default {
name: 'JDate',
props: {
placeholder:{
type: String,
default: '',
required: false
},
value:{
type: String,
required: false
},
dateFormat:{
type: String,
default: 'YYYY-MM-DD HH:mm:ss',
required: false
},
//
triggerChange:{
type: Boolean,
required: false,
default: false
},
readOnly:{
type: Boolean,
required: false,
default: false
},
disabled:{
type: Boolean,
required: false,
default: false
},
showTime:{
type: Boolean,
required: false,
default: false
},
getCalendarContainer: {
type: Function,
default: (node) => node.parentNode
}
},
data () {
let dateStr = this.value;
return {
decorator:"",
momVal:!dateStr?null:moment(dateStr,this.dateFormat)
}
},
watch: {
value (val) {
if(!val){
this.momVal = null
}else{
this.momVal = moment(val,this.dateFormat)
}
}
},
methods: {
moment,
handleDateChange(mom,dateStr){
this.$emit('change', dateStr);
}
},
//2.2
model: {
prop: 'value',
event: 'change'
}
}
</script>

3123
src/components/jeecg/JEditableTable.vue

File diff suppressed because it is too large Load Diff

29
src/components/jeecg/JEllipsis.vue

@ -0,0 +1,29 @@
<template>
<a-tooltip placement="topLeft">
<template slot="title">
<span>{{value}}</span>
</template>
{{ value | ellipsis(length) }}
</a-tooltip>
</template>
<script>
export default {
name: 'JEllipsis',
props: {
value: {
type: String,
required: false,
},
length: {
type: Number,
required: false,
default: 25,
}
}
}
</script>
<style scoped>
</style>

61
src/components/jeecg/JFormContainer.vue

@ -0,0 +1,61 @@
<template>
<div :class="disabled?'jeecg-form-container-disabled':''">
<fieldset :disabled="disabled">
<slot name="detail"></slot>
</fieldset>
<slot name="edit"></slot>
<fieldset disabled>
<slot></slot>
</fieldset>
</div>
</template>
<script>
/**
* 使用方法
* 在form下直接写这个组件就行了
*<a-form layout="inline" :form="form" >
* <j-form-container :disabled="true">
* <!-- 表单内容省略..... -->
* </j-form-container>
*</a-form>
*/
export default {
name: 'JFormContainer',
props:{
disabled:{
type:Boolean,
default:false,
required:false
}
},
mounted(){
console.log("我是表单禁用专用组件,但是我并不支持表单中iframe的内容禁用")
}
}
</script>
<style>
.jeecg-form-container-disabled{
cursor: not-allowed;
}
.jeecg-form-container-disabled fieldset[disabled] {
-ms-pointer-events: none;
pointer-events: none;
}
.jeecg-form-container-disabled .ant-select{
-ms-pointer-events: none;
pointer-events: none;
}
.jeecg-form-container-disabled .ant-upload-select{display:none}
.jeecg-form-container-disabled .ant-upload-list{cursor:grabbing}
.jeecg-form-container-disabled fieldset[disabled] .ant-upload-list{
-ms-pointer-events: auto !important;
pointer-events: auto !important;
}
.jeecg-form-container-disabled .ant-upload-list-item-actions .anticon-delete,
.jeecg-form-container-disabled .ant-upload-list-item .anticon-close{
display: none;
}
</style>

202
src/components/jeecg/JGraphicCode.vue

@ -0,0 +1,202 @@
<template>
<div class="gc-canvas" @click="reloadPic">
<canvas id="gc-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</div>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JGraphicCode',
props: {
length:{
type: Number,
default: 4
},
fontSizeMin: {
type: Number,
default: 20
},
fontSizeMax: {
type: Number,
default: 45
},
backgroundColorMin: {
type: Number,
default: 180
},
backgroundColorMax: {
type: Number,
default: 240
},
colorMin: {
type: Number,
default: 50
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 40
},
lineColorMax: {
type: Number,
default: 180
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default:136
},
contentHeight: {
type: Number,
default: 38
},
remote:{
type:Boolean,
default:false,
required:false
}
},
methods: {
//
randomNum (min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
//
randomColor (min, max) {
let r = this.randomNum(min, max)
let g = this.randomNum(min, max)
let b = this.randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
},
drawPic () {
this.randomCode().then(()=>{
let canvas = document.getElementById('gc-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
//
ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
//
for (let i = 0; i < this.code.length; i++) {
this.drawText(ctx, this.code[i], i)
}
this.drawLine(ctx)
this.drawDot(ctx)
this.$emit("success",this.code)
})
},
drawText (ctx, txt, i) {
ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
let fontSize = this.randomNum(this.fontSizeMin, this.fontSizeMax)
ctx.font = fontSize + 'px SimHei'
let padding = 10;
let offset = (this.contentWidth-40)/(this.code.length-1)
let x=padding;
if(i>0){
x = padding+(i*offset)
}
//let x = (i + 1) * (this.contentWidth / (this.code.length + 1))
let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
if(fontSize>40){
y=40
}
var deg = this.randomNum(-10,10)
//
ctx.translate(x, y)
ctx.rotate(deg * Math.PI / 180)
ctx.fillText(txt, 0, 0)
//
ctx.rotate(-deg * Math.PI / 180)
ctx.translate(-x, -y)
},
drawLine (ctx) {
// 线
for (let i = 0; i <1; i++) {
ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
ctx.beginPath()
ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.stroke()
}
},
drawDot (ctx) {
//
for (let i = 0; i < 100; i++) {
ctx.fillStyle = this.randomColor(0, 255)
ctx.beginPath()
ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
},
reloadPic(){
this.drawPic()
},
randomCode(){
return new Promise((resolve)=>{
if(this.remote==true){
getAction("/sys/getCheckCode").then(res=>{
if(res.success){
this.checkKey = res.result.key
this.code = window.atob(res.result.code)
resolve();
}else{
this.$message.error("生成验证码错误,请联系系统管理员")
this.code = 'BUG'
resolve();
}
}).catch(()=>{
console.log("生成验证码连接服务器异常")
this.code = 'BUG'
resolve();
})
}else{
this.randomLocalCode();
resolve();
}
})
},
randomLocalCode(){
let random = ''
//I l i o O
let str = "QWERTYUPLKJHGFDSAZXCVBNMqwertyupkjhgfdsazxcvbnm1234567890"
for(let i = 0; i < this.length; i++) {
let index = Math.floor(Math.random()*57);
random += str[index];
}
this.code = random
},
getLoginParam(){
return {
checkCode:this.code,
checkKey:this.checkKey
}
}
},
mounted () {
this.drawPic()
},
data(){
return {
code:"",
checkKey:""
}
}
}
</script>
<style scoped>
</style>

226
src/components/jeecg/JImageUpload.vue

@ -0,0 +1,226 @@
<template>
<a-upload
name="file"
listType="picture-card"
:multiple="isMultiple"
:action="uploadAction"
:headers="headers"
:data="{biz:bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
:disabled="disabled"
:isMultiple="isMultiple"
:showUploadList="isMultiple"
@change="handleChange"
@preview="handlePreview">
<img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="height:104px;max-width:300px"/>
<div v-else >
<a-icon :type="uploadLoading ? 'loading' : 'plus'" />
<div class="ant-upload-text">{{ text }}</div>
</div>
<a-modal :visible="previewVisible" :width="1000" :footer="null" @cancel="handleCancel()">
<img alt="example" style="width: 100%" :src="previewImage"/>
</a-modal>
</a-upload>
</template>
<script>
import Vue from 'vue'
import { getFileAccessHttpUrl } from '@/api/manage'
import { fileSizeLimit } from '@/api/api'
const uidGenerator=()=>{
return '-'+parseInt(Math.random()*10000+1,10);
}
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JImageUpload',
data(){
return {
uploadAction:window._CONFIG['domianURL']+"/systemConfig/upload",
uploadLoading:false,
picUrl:false,
headers:{},
fileList: [],
previewImage:"",
previewVisible: false,
sizeLimit: 0,
uploadGoOn:true,
}
},
props:{
text:{
type:String,
required:false,
default:"上传"
},
/*这个属性用于控制文件上传的业务路径*/
bizPath:{
type:String,
required:false,
default:"temp"
},
value:{
type:[String,Array],
required:false
},
disabled:{
type:Boolean,
required:false,
default: false
},
isMultiple:{
type:Boolean,
required:false,
default: false
}
},
watch:{
value(val){
if (val instanceof Array) {
this.initFileList(val.join(','))
} else {
this.initFileList(val)
}
}
},
created(){
this.initFileSizeLimit()
this.headers = {"X-Access-Token":""}
},
methods:{
initFileSizeLimit() {
fileSizeLimit().then((res)=>{
if(res.code === 200) {
this.sizeLimit = res.data
}
})
},
initFileList(paths){
if(!paths || paths.length==0){
this.fileList = [];
this.picUrl = false;
return;
}
this.picUrl = true;
let fileList = [];
let arr = paths.split(",")
for(var a=0;a<arr.length;a++){
let url = getFileAccessHttpUrl(arr[a]);
fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: url,
response:{
code:"history",
data:arr[a]
}
})
}
this.fileList = fileList
},
beforeUpload: function(file){
this.uploadGoOn=true
let fileType = file.type;
let fileSize = file.size;
if(fileType.indexOf('image')<0){
this.$message.warning('请上传图片');
this.uploadGoOn=false
return false;
}
//
if(fileSize>this.sizeLimit) {
let parseSizeLimit = (this.sizeLimit/1024/1024).toFixed(2)
this.$message.warning('抱歉,图片大小不能超过' + parseSizeLimit + 'M');
this.uploadGoOn=false
return false;
}
return true
},
handleChange(info) {
console.log(info,"--文件列表改变--")
if(!info.file.status && this.uploadGoOn === false){
info.fileList.pop();
}
this.picUrl = false;
let fileList = info.fileList
if(info.file.status==='done'){
if(info.file.response.code === 200){
this.picUrl = true;
fileList = fileList.map((file) => {
if (file.response) {
file.url = file.response.data;
}
return file;
});
}
//this.$message.success(`${info.file.name} !`);
}else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`);
}else if(info.file.status === 'removed'){
this.handleDelete(info.file)
}
this.fileList = fileList
if(info.file.status==='done' || info.file.status === 'removed'){
this.handlePathChange()
}
},
//
handlePreview (file) {
this.previewImage = file.url || file.thumbUrl
this.previewVisible = true
},
getAvatarView(){
if(this.fileList.length>0){
let url = this.fileList[0].url
return url
}
},
handlePathChange(){
let uploadFiles = this.fileList
let path = ''
if(!uploadFiles || uploadFiles.length==0){
path = ''
}
let arr = [];
if(!this.isMultiple){
arr.push(uploadFiles[uploadFiles.length-1].response.data)
}else{
for(var a=0;a<uploadFiles.length;a++){
arr.push(uploadFiles[a].response.data)
}
}
if(arr.length>0){
path = arr.join(",")
}
this.$emit('change', path);
},
handleDelete(file){
//
console.log(file)
},
handleCancel() {
this.close();
this.previewVisible = false;
},
close () {
},
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
</style>

123
src/components/jeecg/JImportModal.vue

@ -0,0 +1,123 @@
<template>
<a-modal
title="导入EXCEL"
:width="600"
:visible="visible"
:confirmLoading="uploading"
@cancel="handleClose">
<a-upload
name="file"
:multiple="true"
accept=".xls,.xlsx"
:fileList="fileList"
:remove="handleRemove"
:beforeUpload="beforeUpload">
<a-button>
<a-icon type="upload" />
选择导入文件
</a-button>
</a-upload>
<template slot="footer">
<a-button @click="handleClose">关闭</a-button>
<a-button
type="primary"
@click="handleImport"
:disabled="fileList.length === 0"
:loading="uploading">
{{ uploading ? '上传中...' : '开始上传' }}
</a-button>
</template>
</a-modal>
</template>
<script>
import { postAction } from '@/api/manage'
export default {
name: 'JImportModal',
props:{
url:{
type: String,
default: '',
required: false
},
biz:{
type: String,
default: '',
required: false
}
},
data(){
return {
visible:false,
uploading:false,
fileList:[],
uploadAction:'',
foreignKeys:''
}
},
watch: {
url (val) {
if(val){
this.uploadAction = window._CONFIG['domianURL']+val
}
}
},
created () {
this.uploadAction = window._CONFIG['domianURL']+this.url
},
methods:{
handleClose(){
this.visible=false
},
show(arg){
this.fileList = []
this.uploading = false
this.visible = true
this.foreignKeys = arg;
},
handleRemove(file) {
const index = this.fileList.indexOf(file);
const newFileList = this.fileList.slice();
newFileList.splice(index, 1);
this.fileList = newFileList
},
beforeUpload(file) {
this.fileList = [...this.fileList, file]
return false;
},
handleImport() {
const { fileList } = this;
const formData = new FormData();
if(this.biz){
formData.append('isSingleTableImport',this.biz);
}
if(this.foreignKeys && this.foreignKeys.length>0){
formData.append('foreignKeys',this.foreignKeys);
}
fileList.forEach((file) => {
formData.append('files[]', file);
});
this.uploading = true
postAction(this.uploadAction, formData).then((res) => {
this.uploading = false
if(res.success){
this.$message.success(res.message)
this.visible=false
this.$emit('ok')
}else{
this.$message.warning(res.message)
}
})
}
}
}
</script>
<style scoped>
</style>

100
src/components/jeecg/JInput.vue

@ -0,0 +1,100 @@
<template>
<a-input :placeholder="placeholder" :value="inputVal" @input="backValue"></a-input>
</template>
<script>
const JINPUT_QUERY_LIKE = 'like';
const JINPUT_QUERY_NE = 'ne';
const JINPUT_QUERY_GE = 'ge'; //
const JINPUT_QUERY_LE = 'le'; //
export default {
name: 'JInput',
props:{
value:{
type:String,
required:false
},
type:{
type:String,
required:false,
default:JINPUT_QUERY_LIKE
},
placeholder:{
type:String,
required:false,
default:''
}
},
watch:{
value:{
immediate:true,
handler:function(){
this.initVal();
}
},
// update-begin author:sunjianlei date:20200225 for: type ------
type() {
this.backValue({ target: { value: this.inputVal } })
},
// update-end author:sunjianlei date:20200225 for: type ------
},
model: {
prop: 'value',
event: 'change'
},
data(){
return {
inputVal:''
}
},
methods:{
initVal(){
if(!this.value){
this.inputVal = ''
}else{
let text = this.value
switch (this.type) {
case JINPUT_QUERY_LIKE:
text = text.substring(1,text.length-1);
break;
case JINPUT_QUERY_NE:
text = text.substring(1);
break;
case JINPUT_QUERY_GE:
text = text.substring(2);
break;
case JINPUT_QUERY_LE:
text = text.substring(2);
break;
default:
}
this.inputVal = text
}
},
backValue(e){
let text = e.target.value
switch (this.type) {
case JINPUT_QUERY_LIKE:
text = "*"+text+"*";
break;
case JINPUT_QUERY_NE:
text = "!"+text;
break;
case JINPUT_QUERY_GE:
text = ">="+text;
break;
case JINPUT_QUERY_LE:
text = "<="+text;
break;
default:
}
this.$emit("change",text)
}
}
}
</script>
<style scoped>
</style>

237
src/components/jeecg/JModal/index.vue

@ -0,0 +1,237 @@
<template>
<a-modal
ref="modal"
:class="getClass(modalClass)"
:style="getStyle(modalStyle)"
:visible="visible"
v-bind="_attrs"
v-on="$listeners"
@ok="handleOk"
@cancel="handleCancel"
>
<slot></slot>
<template v-if="!isNoTitle" slot="title">
<a-row class="j-modal-title-row" type="flex">
<a-col class="left">
<slot name="title">{{ title }}</slot>
</a-col>
<a-col class="right">
<a-button v-if="switchFullscreen" @click="toggleFullscreen" class="ant-modal-close ant-modal-close-x"
ghost type="link" :icon="fullscreenButtonIcon"/>
</a-col>
</a-row>
</template>
<!-- 处理 scopedSlots -->
<template v-for="slotName of scopedSlotsKeys" :slot="slotName">
<slot :name="slotName"></slot>
</template>
<!-- 处理 slots -->
<template v-for="slotName of slotsKeys" v-slot:[slotName]>
<slot :name="slotName"></slot>
</template>
</a-modal>
</template>
<script>
import { getClass, getStyle } from '@/utils/props-util'
import { triggerWindowResizeEvent } from "@/utils/util"
import Vue from 'vue'
export default {
name: 'JModal',
props: {
title: String,
// 使 .sync
visible: Boolean,
//
prefixNo: String,
// body 使 .sync
fullscreen: {
type: Boolean,
default: false
},
//
switchHelp: {
type: Boolean,
default: false
},
//
switchFullscreen: {
type: Boolean,
default: false
},
//
okClose: {
type: Boolean,
default: true
},
},
data() {
return {
// 使 slots
usedSlots: ['title'],
//
innerFullscreen: this.fullscreen,
}
},
computed: {
// a-modal
_attrs() {
let attrs = { ...this.$attrs }
// 100%
if (this.innerFullscreen) {
attrs['width'] = '100%'
}
return attrs
},
modalClass() {
return {
'j-modal-box': true,
'fullscreen': this.innerFullscreen,
'no-title': this.isNoTitle,
'no-footer': this.isNoFooter,
}
},
modalStyle() {
let style = {}
// top 0
if (this.innerFullscreen) {
style['top'] = '0'
}
return style
},
isNoTitle() {
return !this.title && !this.allSlotsKeys.includes('title')
},
isNoFooter() {
return this._attrs['footer'] === null
},
slotsKeys() {
return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))
},
scopedSlotsKeys() {
return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
},
allSlotsKeys() {
return this.slotsKeys.concat(this.scopedSlotsKeys)
},
//
fullscreenButtonIcon() {
return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'
},
},
watch: {
visible() {
if (this.visible) {
this.innerFullscreen = this.fullscreen
}
},
innerFullscreen(val) {
this.$emit('update:fullscreen', val)
},
},
methods: {
getClass(clazz) {
return { ...getClass(this), ...clazz }
},
getStyle(style) {
return { ...getStyle(this), ...style }
},
close() {
this.$emit('update:visible', false)
},
handleOk() {
if (this.okClose) {
this.close()
}
},
handleCancel() {
this.close()
},
/** 切换全屏 */
toggleFullscreen() {
this.innerFullscreen = !this.innerFullscreen
triggerWindowResizeEvent()
},
}
}
</script>
<style lang="less">
.j-modal-box {
&.fullscreen {
top: 0;
left: 0;
padding: 0;
height: 100vh;
& .ant-modal-content {
height: 100vh;
border-radius: 0;
& .ant-modal-body {
/* title 和 footer 各占 55px */
height: calc(100% - 55px - 55px);
overflow: auto;
}
}
&.no-title, &.no-footer {
.ant-modal-body {
height: calc(100% - 55px);
}
}
&.no-title.no-footer {
.ant-modal-body {
height: 100%;
}
}
}
.j-modal-title-row {
.left {
width: calc(100% - 56px - 56px);
}
.right {
width: 56px;
position: inherit;
.ant-modal-close {
right: 56px;
color: rgba(0, 0, 0, 0.45);
&:hover {
color: rgba(0, 0, 0, 0.75);
}
}
}
}
}
@media (max-width: 767px) {
.j-modal-box.fullscreen {
margin: 0;
max-width: 100vw;
}
}
</style>

66
src/components/jeecg/JSelectMultiple.vue

@ -0,0 +1,66 @@
<template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
<a-select-option
v-for="(item,index) in options"
:key="index"
:value="item.value">
{{ item.text || item.label }}
</a-select-option>
</a-select>
</template>
<script>
//option {label:,value:}
export default {
name: 'JSelectMultiple',
props: {
placeholder:{
type: String,
default:'',
required: false
},
value:{
type: String,
required: false
},
readOnly:{
type: Boolean,
required: false,
default: false
},
options:{
type: Array,
required: true
},
triggerChange:{
type: Boolean,
required: false,
default: false
}
},
data(){
return {
arrayValue:!this.value?[]:this.value.split(",")
}
},
watch:{
value (val) {
if(!val){
this.arrayValue = []
}else{
this.arrayValue = this.value.split(",")
}
}
},
methods:{
onChange (selectedValue) {
if(this.triggerChange){
this.$emit('change', selectedValue.join(","));
}else{
this.$emit('input', selectedValue.join(","));
}
},
},
}
</script>

116
src/components/jeecg/JSlider.vue

@ -0,0 +1,116 @@
<template>
<div class="drag" ref="dragDiv">
<div class="drag_bg"></div>
<div class="drag_text">{{confirmWords}}</div>
<div ref="moveDiv" @mousedown="mousedownFn($event)" :class="{'handler_ok_bg':confirmSuccess}" class="handler handler_bg" style="border: 0.5px solid #fff;height: 34px;position: absolute;top: 0px;left: 0px;"></div>
</div>
</template>
<script>
export default {
name:"JSlider",
data(){
return {
beginClientX:0, /*距离屏幕左端距离*/
mouseMoveStata:false, /*触发拖动状态 判断*/
maxwidth:'', /*拖动最大宽度,依据滑块宽度算出来的*/
confirmWords:'拖动滑块验证', /*滑块文字*/
confirmSuccess:false /*验证成功判断*/
}
},
methods: {
isSuccess(){
return this.confirmSuccess
},
mousedownFn:function (e) {
if(!this.confirmSuccess){
e.preventDefault && e.preventDefault(); //
this.mouseMoveStata = true;
this.beginClientX = e.clientX;
}
}, //mousedoen
successFunction(){
this.confirmSuccess = true
this.confirmWords = '验证通过';
if(window.addEventListener){
document.getElementsByTagName('html')[0].removeEventListener('mousemove',this.mouseMoveFn);
document.getElementsByTagName('html')[0].removeEventListener('mouseup',this.moseUpFn);
}else {
document.getElementsByTagName('html')[0].removeEventListener('mouseup',()=>{});
}
document.getElementsByClassName('drag_text')[0].style.color = '#fff'
document.getElementsByClassName('handler')[0].style.left = this.maxwidth + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = this.maxwidth + 'px';
this.$emit("onSuccess",true)
}, //
mouseMoveFn(e){
if(this.mouseMoveStata){
let width = e.clientX - this.beginClientX;
if(width>0 && width<=this.maxwidth){
document.getElementsByClassName('handler')[0].style.left = width + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = width + 'px';
}else if(width>this.maxwidth){
this.successFunction();
}
}
}, //mousemove
moseUpFn(e){
this.mouseMoveStata = false;
var width = e.clientX - this.beginClientX;
if(width<this.maxwidth){
// ---- update-begin- author:sunjianlei --- date:20191009 --- for: handler ----
let handler = document.getElementsByClassName('handler')[0]
if (handler) {
handler.style.left = 0 + 'px'
document.getElementsByClassName('drag_bg')[0].style.width = 0 + 'px'
}
// ---- update-end- author:sunjianlei --- date:20191009 --- for: handler ----
}
} //mouseup
},
mounted(){
this.maxwidth = this.$refs.dragDiv.clientWidth - this.$refs.moveDiv.clientWidth;
document.getElementsByTagName('html')[0].addEventListener('mousemove',this.mouseMoveFn);
document.getElementsByTagName('html')[0].addEventListener('mouseup',this.moseUpFn)
}
}
</script>
<style scoped>
.drag{
position: relative;
background-color: #e8e8e8;
width: 100%;
height: 34px;
line-height: 34px;
text-align: center;
}
.handler{
width: 40px;
height: 32px;
border: 1px solid #ccc;
cursor: move;
}
.handler_bg{
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTEyNTVEMURGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTEyNTVEMUNGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MTc5NzNmZS02OTQxLTQyOTYtYTIwNi02NDI2YTNkOWU5YmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+YiRG4AAAALFJREFUeNpi/P//PwMlgImBQkA9A+bOnfsIiBOxKcInh+yCaCDuByoswaIOpxwjciACFegBqZ1AvBSIS5OTk/8TkmNEjwWgQiUgtQuIjwAxUF3yX3xyGIEIFLwHpKyAWB+I1xGSwxULIGf9A7mQkBwTlhBXAFLHgPgqEAcTkmNCU6AL9d8WII4HOvk3ITkWJAXWUMlOoGQHmsE45ViQ2KuBuASoYC4Wf+OUYxz6mQkgwAAN9mIrUReCXgAAAABJRU5ErkJggg==") no-repeat center;
}
.handler_ok_bg{
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDlBRDI3NjVGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDlBRDI3NjRGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDphNWEzMWNhMC1hYmViLTQxNWEtYTEwZS04Y2U5NzRlN2Q4YTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+k+sHwwAAASZJREFUeNpi/P//PwMyKD8uZw+kUoDYEYgloMIvgHg/EM/ptHx0EFk9I8wAoEZ+IDUPiIMY8IN1QJwENOgj3ACo5gNAbMBAHLgAxA4gQ5igAnNJ0MwAVTsX7IKyY7L2UNuJAf+AmAmJ78AEDTBiwGYg5gbifCSxFCZoaBMCy4A4GOjnH0D6DpK4IxNSVIHAfSDOAeLraJrjgJp/AwPbHMhejiQnwYRmUzNQ4VQgDQqXK0ia/0I17wJiPmQNTNBEAgMlQIWiQA2vgWw7QppBekGxsAjIiEUSBNnsBDWEAY9mEFgMMgBk00E0iZtA7AHEctDQ58MRuA6wlLgGFMoMpIG1QFeGwAIxGZo8GUhIysmwQGSAZgwHaEZhICIzOaBkJkqyM0CAAQDGx279Jf50AAAAAABJRU5ErkJggg==") no-repeat center;
}
.drag_bg{
background-color: #7ac23c;
height: 34px;
width: 0px;
}
.drag_text{
position: absolute;
top: 0px;
width: 100%;text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
-o-user-select:none;
-ms-user-select:none;
}
</style>

638
src/components/jeecg/JSuperQuery.vue

@ -0,0 +1,638 @@
<template>
<div class="j-super-query-box">
<slot name="button" :isActive="superQueryFlag" :isMobile="izMobile" :open="handleOpen" :reset="handleReset">
<a-tooltip v-if="superQueryFlag" v-bind="tooltipProps" :mouseLeaveDelay="0.2">
<!-- begin 不知道为什么不加上这段代码就无法生效 -->
<span v-show="false">{{tooltipProps}}</span>
<!-- end 不知道为什么不加上这段代码就无法生效 -->
<template slot="title">
<span>已有高级查询条件生效</span>
<a-divider type="vertical"/>
<a @click="handleReset">清空</a>
</template>
<a-button-group>
<a-button type="primary" @click="handleOpen">
<a-icon type="appstore" theme="twoTone" spin/>
<span>高级查询</span>
</a-button>
<a-button v-if="izMobile" type="primary" icon="delete" @click="handleReset"/>
</a-button-group>
</a-tooltip>
<a-button v-else type="primary" icon="filter" @click="handleOpen">高级查询</a-button>
</slot>
<j-modal
title="高级查询构造器"
:width="1000"
:visible="visible"
@cancel="handleCancel"
:mask="false"
:fullscreen="izMobile"
class="j-super-query-modal"
style="top:5%;max-height: 95%;"
>
<template slot="footer">
<div style="float: left">
<a-button :loading="loading" @click="handleReset">重置</a-button>
<a-button :loading="loading" @click="handleSave">保存查询条件</a-button>
</div>
<a-button :loading="loading" @click="handleCancel">关闭</a-button>
<a-button :loading="loading" type="primary" @click="handleOk">查询</a-button>
</template>
<a-spin :spinning="loading">
<a-row>
<a-col :sm="24" :md="24-5">
<a-empty v-if="queryParamsModel.length === 0" style="margin-bottom: 12px;">
<div slot="description">
<span>没有任何查询条件</span>
<a-divider type="vertical"/>
<a @click="handleAdd">点击新增</a>
</div>
</a-empty>
<a-form v-else layout="inline">
<a-row style="margin-bottom: 12px;">
<a-col :md="12" :xs="24">
<a-form-item label="过滤条件匹配" :labelCol="{md: 6,xs:24}" :wrapperCol="{md: 18,xs:24}" style="width: 100%;">
<a-select v-model="matchType" :getPopupContainer="node=>node.parentNode" style="width: 100%;">
<a-select-option value="and">AND所有条件都要求匹配</a-select-option>
<a-select-option value="or">OR条件中的任意一个匹配</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row type="flex" style="margin-bottom:10px" :gutter="16" v-for="(item, index) in queryParamsModel" :key="index">
<a-col :md="8" :xs="24" style="margin-bottom: 12px;">
<a-tree-select
showSearch
v-model="item.field"
:treeData="fieldTreeData"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="选择查询字段"
allowClear
treeDefaultExpandAll
:getPopupContainer="node=>node.parentNode"
style="width: 100%"
@select="(val,option)=>handleSelected(option,item)"
>
</a-tree-select>
</a-col>
<a-col :md="4" :xs="24" style="margin-bottom: 12px;">
<a-select placeholder="匹配规则" :value="item.rule" :getPopupContainer="node=>node.parentNode" @change="handleRuleChange(item,$event)">
<a-select-option value="eq">等于</a-select-option>
<a-select-option value="like">包含</a-select-option>
<a-select-option value="right_like">..开始</a-select-option>
<a-select-option value="left_like">..结尾</a-select-option>
<a-select-option value="in">...</a-select-option>
<a-select-option value="ne">不等于</a-select-option>
<a-select-option value="gt">大于</a-select-option>
<a-select-option value="ge">大于等于</a-select-option>
<a-select-option value="lt">小于</a-select-option>
<a-select-option value="le">小于等于</a-select-option>
</a-select>
</a-col>
<a-col :md="8" :xs="24" style="margin-bottom: 12px;">
<template v-if="item.dictCode">
<template v-if="item.type === 'table-dict'">
<j-popup
v-model="item.val"
:code="item.dictTable"
:field="item.dictCode"
:orgFields="item.dictCode"
:destFields="item.dictCode"
></j-popup>
</template>
<template v-else>
<j-multi-select-tag v-show="allowMultiple(item)" v-model="item.val" :dictCode="item.dictCode" placeholder="请选择"/>
<j-dict-select-tag v-show="!allowMultiple(item)" v-model="item.val" :dictCode="item.dictCode" placeholder="请选择"/>
</template>
</template>
<j-popup v-else-if="item.type === 'popup'" :value="item.val" v-bind="item.popup" group-id="superQuery" @input="(e,v)=>handleChangeJPopup(item,e,v)"/>
<j-select-multi-user
v-else-if="item.type === 'select-user' || item.type === 'sel_user'"
v-model="item.val"
:buttons="false"
:multiple="false"
placeholder="请选择用户"
:returnKeys="['id', item.customReturnField || 'username']"
/>
<j-select-depart
v-else-if="item.type === 'select-depart' || item.type === 'sel_depart'"
v-model="item.val"
:multi="false"
placeholder="请选择部门"
:customReturnField="item.customReturnField || 'id'"
/>
<a-select
v-else-if="item.options instanceof Array"
v-model="item.val"
:options="item.options"
allowClear
placeholder="请选择"
:mode="allowMultiple(item)?'multiple':''"
/>
<j-area-linkage v-model="item.val" v-else-if="item.type==='area-linkage' || item.type==='pca'" style="width: 100%"/>
<j-date v-else-if=" item.type=='date' " v-model="item.val" placeholder="请选择日期" style="width: 100%"></j-date>
<j-date v-else-if=" item.type=='datetime' " v-model="item.val" placeholder="请选择时间" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss" style="width: 100%"></j-date>
<a-time-picker v-else-if="item.type==='time'" :value="item.val ? moment(item.val,'HH:mm:ss') : null" format="HH:mm:ss" style="width: 100%" @change="(time,value)=>item.val=value"/>
<a-input-number v-else-if=" item.type=='int'||item.type=='number' " style="width: 100%" placeholder="请输入数值" v-model="item.val"/>
<a-input v-else v-model="item.val" placeholder="请输入值"/>
</a-col>
<a-col :md="4" :xs="0" style="margin-bottom: 12px;">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
<a-col :md="0" :xs="24" style="margin-bottom: 12px;text-align: right;">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
</a-row>
</a-form>
</a-col>
<a-col :sm="24" :md="5">
<!-- 查询记录 -->
<a-card class="j-super-query-history-card" :bordered="true">
<div slot="title">
保存的查询
</div>
<a-empty v-if="saveTreeData.length === 0" class="j-super-query-history-empty" description="没有保存任何查询"/>
<a-tree
v-else
class="j-super-query-history-tree"
showIcon
:treeData="saveTreeData"
:selectedKeys="[]"
@select="handleTreeSelect"
>
</a-tree>
</a-card>
</a-col>
</a-row>
</a-spin>
<a-modal title="请输入保存的名称" :visible="prompt.visible" @cancel="prompt.visible=false" @ok="handlePromptOk">
<a-input v-model="prompt.value"></a-input>
</a-modal>
</j-modal>
</div>
</template>
<script>
import moment from 'moment'
import * as utils from '@/utils/util'
import JDate from '@/components/jeecg/JDate.vue'
import JSelectDepart from '@/components/jeecgbiz/JSelectDepart'
import JSelectMultiUser from '@/components/jeecgbiz/JSelectMultiUser'
import JAreaLinkage from '@comp/jeecg/JAreaLinkage'
export default {
name: 'JSuperQuery',
// mixins: [mixinDevice],
components: { JAreaLinkage, JDate, JSelectDepart, JSelectMultiUser },
props: {
/*
fieldList: [{
value:'',
text:'',
type:'',
dictCode:'' // dictCode type
}]
type:date datetime int number string
* */
fieldList: {
type: Array,
required: true
},
/*
* 这个回调函数接收一个数组参数 即查询条件
* */
callback: {
type: String,
required: false,
default: 'handleSuperQuery'
},
//
loading: {
type: Boolean,
default: false
},
// code code
// nullCode
saveCode: {
type: String,
default: null
}
},
data() {
return {
moment,
fieldTreeData: [],
prompt: {
visible: false,
value: ''
},
visible: false,
queryParamsModel: [],
treeIcon: <a-icon type="file-text"/>,
// treeData
saveTreeData: [],
//
saveCodeBefore: 'JSuperQuerySaved_',
// andor
matchType: 'and',
superQueryFlag: false,
}
},
computed: {
izMobile() {
return this.device === 'mobile'
},
tooltipProps() {
return this.izMobile ? { visible: false } : {}
},
fullSaveCode() {
let saveCode = this.saveCode
if (saveCode == null || saveCode === '') {
saveCode = this.$route.fullPath
}
return this.saveCodeBefore + saveCode
},
},
watch: {
// saveCode
fullSaveCode: {
immediate: true,
handler() {
let list = this.$ls.get(this.fullSaveCode)
if (list instanceof Array) {
this.saveTreeData = list.map(i => this.renderSaveTreeData(i))
}
}
},
fieldList: {
deep: true,
immediate: true,
handler(val) {
let mainData = [], subData = []
val.forEach(item => {
let data = { ...item }
data.label = data.label || data.text
let hasChildren = (data.children instanceof Array)
data.disabled = hasChildren
data.selectable = !hasChildren
if (hasChildren) {
data.children = data.children.map(item2 => {
let child = { ...item2 }
child.label = child.label || child.text
child.label = data.label + '-' + child.label
child.value = data.value + ',' + child.value
child.val = ''
return child
})
data.val = ''
subData.push(data)
} else {
mainData.push(data)
}
})
this.fieldTreeData = mainData.concat(subData)
}
}
},
methods: {
show() {
if (!this.queryParamsModel || this.queryParamsModel.length === 0) {
this.resetLine()
}
this.visible = true
},
handleOk() {
if (!this.isNullArray(this.queryParamsModel)) {
let event = {
matchType: this.matchType,
params: this.removeEmptyObject(this.queryParamsModel)
}
//
if (this.izMobile) {
this.visible = false
}
this.emitCallback(event)
} else {
this.$message.warn("不能查询空条件")
}
},
emitCallback(event = {}) {
let { params = [], matchType = this.matchType } = event
this.superQueryFlag = (params && params.length > 0)
for (let param of params) {
if (Array.isArray(param.val)) {
param.val = param.val.join(',')
}
}
console.debug('---高级查询参数--->', { params, matchType })
this.$emit(this.callback, params, matchType)
},
handleCancel() {
this.close()
},
close() {
this.$emit('close')
this.visible = false
},
handleAdd() {
this.addNewLine()
},
addNewLine() {
this.queryParamsModel.push({ rule: 'eq' })
},
resetLine() {
this.superQueryFlag = false
this.queryParamsModel = []
this.addNewLine()
},
handleDel(index) {
this.queryParamsModel.splice(index, 1)
},
handleSelected(node, item) {
let { type, options, dictCode, dictTable, customReturnField, popup } = node.dataRef
item['type'] = type
item['options'] = options
item['dictCode'] = dictCode
item['dictTable'] = dictTable
item['customReturnField'] = customReturnField
if (popup) {
item['popup'] = popup
}
this.$set(item, 'val', undefined)
},
handleOpen() {
this.show()
},
handleReset() {
this.resetLine()
this.emitCallback()
},
handleSave() {
let queryParams = this.removeEmptyObject(this.queryParamsModel)
if (this.isNullArray(queryParams)) {
this.$message.warning('空条件不能保存')
} else {
this.prompt.value = ''
this.prompt.visible = true
}
},
handlePromptOk() {
let { value } = this.prompt
if(!value){
this.$message.warning('保存名称不能为空')
return
}
//
let records = this.removeEmptyObject(this.queryParamsModel)
//
let filterList = this.saveTreeData.filter(i => i.originTitle === value)
if (filterList.length > 0) {
this.$confirm({
content: `${value} 已存在,是否覆盖?`,
onOk: () => {
this.prompt.visible = false
filterList[0].records = records
this.saveToLocalStore()
this.$message.success('保存成功')
}
})
} else {
//
this.prompt.visible = false
//
this.saveTreeData.push(this.renderSaveTreeData({
title: value,
matchType: this.matchType,
records: records
}))
// LocalStore
this.saveToLocalStore()
this.$message.success('保存成功')
}
},
handleTreeSelect(idx, event) {
if (event.selectedNodes[0]) {
let { matchType, records } = event.selectedNodes[0].data.props
// matchType使
this.matchType = matchType || this.matchType
this.queryParamsModel = utils.cloneObject(records)
}
},
handleRemoveSaveTreeItem(event, vNode) {
//
event.stopPropagation()
this.$confirm({
content: '是否删除当前查询?',
onOk: () => {
let { eventKey } = vNode
this.saveTreeData.splice(Number.parseInt(eventKey.substring(2)), 1)
this.saveToLocalStore()
},
})
},
// LocalStore
saveToLocalStore() {
let saveValue = this.saveTreeData.map(({ originTitle, matchType, records }) => ({ title: originTitle, matchType, records }))
this.$ls.set(this.fullSaveCode, saveValue)
},
isNullArray(array) {
//
if (!array || array.length === 0) {
return true
}
if (array.length === 1) {
let obj = array[0]
if (!obj.field || (obj.val == null || obj.val === '') || !obj.rule) {
return true
}
}
return false
},
//
removeEmptyObject(arr) {
let array = utils.cloneObject(arr)
for (let i = 0; i < array.length; i++) {
let item = array[i]
if (item == null || Object.keys(item).length <= 0) {
array.splice(i--, 1)
} else {
if (Array.isArray(item.options)) {
// options
if (item.dictCode) {
//
delete item.options
}
}
}
}
return array
},
/** 渲染保存查询条件的 title(加个删除按钮) */
renderSaveTreeData(item) {
item.icon = this.treeIcon
item.originTitle = item['title']
item.title = (fn, vNode) => {
let { originTitle } = vNode.dataRef
return (
<div class="j-history-tree-title">
<span>{originTitle}</span>
<div class="j-history-tree-title-closer" onClick={e => this.handleRemoveSaveTreeItem(e, vNode)}>
<a-icon type="close-circle"/>
</div>
</div>
)
}
return item
},
/** 判断是否允许多选 */
allowMultiple(item) {
return item.rule === 'in'
},
handleRuleChange(item, newValue) {
let oldValue = item.rule
this.$set(item, 'rule', newValue)
// intype
if (oldValue === 'in') {
if (item.dictCode || item.options instanceof Array) {
let value = item.val
if (typeof item.val === 'string') {
value = item.val.split(',')[0]
} else if (Array.isArray(item.val)) {
value = item.val[0]
}
this.$set(item, 'val', value)
}
}
},
handleChangeJPopup(item, e, values) {
item.val = values[item.popup['destFields']]
},
}
}
</script>
<style lang="less" scoped>
.j-super-query-box {
display: inline-block;
}
.j-super-query-modal {
.j-super-query-history-card {
/deep/ .ant-card-body,
/deep/ .ant-card-head-title {
padding: 0;
}
/deep/ .ant-card-head {
padding: 4px 8px;
min-height: initial;
}
}
.j-super-query-history-empty {
/deep/ .ant-empty-image {
height: 80px;
line-height: 80px;
margin-bottom: 0;
}
/deep/ img {
width: 80px;
height: 65px;
}
/deep/ .ant-empty-description {
color: #afafaf;
margin: 8px 0;
}
}
.j-super-query-history-tree {
.j-history-tree-title {
width: calc(100% - 24px);
position: relative;
display: inline-block;
&-closer {
color: #999999;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
text-align: center;
opacity: 0;
transition: opacity 0.3s, color 0.3s;
&:hover {
color: #666666;
}
&:active {
color: #333333;
}
}
&:hover {
.j-history-tree-title-closer {
opacity: 1;
}
}
}
/deep/ .ant-tree-switcher {
display: none;
}
/deep/ .ant-tree-node-content-wrapper {
width: 100%;
}
}
}
</style>

57
src/components/jeecg/JSwitch.vue

@ -0,0 +1,57 @@
<template>
<a-switch v-model="checkStatus" :disabled="disabled" @change="handleChange"/>
</template>
<script>
export default {
name: 'JSwitch',
props: {
value:{
type: String,
required: false
},
disabled:{
type: Boolean,
required: false,
default: false
},
options:{
type:Array,
required:false,
default:()=>['Y','N']
}
},
data () {
return {
checkStatus: false
}
},
watch: {
value:{
immediate: true,
handler(val){
if(!val){
this.checkStatus = false
this.$emit('change', this.options[1]);
}else{
if(this.options[0]==val){
this.checkStatus = true
}else{
this.checkStatus = false
}
}
}
}
},
methods: {
handleChange(checked){
let flag = checked===false?this.options[1]:this.options[0];
this.$emit('change', flag);
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>

199
src/components/jeecg/JTreeDict.vue

@ -0,0 +1,199 @@
<template>
<a-tree-select
allowClear
labelInValue
style="width: 100%"
:disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="placeholder"
:loadData="asyncLoadTreeData"
:value="treeValue"
:treeData="treeData"
@change="onChange"
@search="onSearch">
</a-tree-select>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JTreeDict',
data(){
return {
treeData:[],
treeValue: null,
url_root:"/sys/category/loadTreeRoot",
url_children:"/sys/category/loadTreeChildren",
url_view:'/sys/category/loadOne',
}
},
props:{
value:{
type: String,
required: false
},
placeholder:{
type: String,
default: '请选择',
required: false
},
parentCode:{
type: String,
default: '',
required: false
},
field:{
type: String,
default: 'id',
required: false
},
root:{
type:Object,
required:false,
default:()=>{
return {
pid:'0'
}
}
},
async:{
type:Boolean,
default:false,
required:false
},
disabled:{
type:Boolean,
default:false,
required:false
}
},
watch:{
root:{
handler(val){
console.log("root-change",val)
},
deep:true
},
parentCode:{
handler(){
this.loadRoot()
}
},
value:{
handler(){
this.loadViewInfo()
}
}
},
created(){
this.loadRoot()
this.loadViewInfo()
},
model: {
prop: 'value',
event: 'change'
},
methods:{
loadViewInfo(){
if(!this.value || this.value=="0"){
this.treeValue = null
}else{
let param = {
field:this.field,
val:this.value
}
getAction(this.url_view,param).then(res=>{
if(res.success){
this.treeValue = {
value:this.value,
label:res.result.name
}
}
})
}
},
loadRoot(){
let param = {
async:this.async,
pcode:this.parentCode
}
getAction(this.url_root,param).then(res=>{
if(res.success){
this.handleTreeNodeValue(res.result)
console.log("aaaa",res.result)
this.treeData = [...res.result]
}else{
this.$message.error(res.message)
}
})
},
asyncLoadTreeData (treeNode) {
return new Promise((resolve) => {
if(!this.async){
resolve()
return
}
if (treeNode.$vnode.children) {
resolve()
return
}
let pid = treeNode.$vnode.key
let param = {
pid:pid
}
getAction(this.url_children,param).then(res=>{
if(res.success){
this.handleTreeNodeValue(res.result)
this.addChildren(pid,res.result,this.treeData)
this.treeData = [...this.treeData]
}
resolve()
})
})
},
addChildren(pid,children,treeArray){
if(treeArray && treeArray.length>0){
for(let item of treeArray){
if(item.key == pid){
if(!children || children.length==0){
item.leaf = true
}else{
item.children = children
}
break
}else{
this.addChildren(pid,children,item.children)
}
}
}
},
handleTreeNodeValue(result){
let storeField = this.field=='code'?'code':'key'
for(let i of result){
i.value = i[storeField]
i.isLeaf = (!i.leaf)?false:true
if(i.children && i.children.length>0){
this.handleTreeNodeValue(i.children)
}
}
},
onChange(value){
console.log(value)
if(!value){
this.$emit('change', '');
}else{
this.$emit('change', value.value);
}
this.treeValue = value
},
onSearch(value){
console.log(value)
},
getCurrTreeData(){
return this.treeData
}
}
}
</script>

260
src/components/jeecg/JTreeSelect.vue

@ -0,0 +1,260 @@
<template>
<a-tree-select
allowClear
labelInValue
:getPopupContainer="(node) => node.parentNode"
style="width: 100%"
:disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="placeholder"
:loadData="asyncLoadTreeData"
:value="treeValue"
:treeData="treeData"
:multiple="multiple"
@change="onChange"
@search="onSearch">
</a-tree-select>
</template>
<script>
/*
* 异步树加载组件 通过传入表名 显示字段 存储字段 加载一个树控件
* <j-tree-select dict="aa_tree_test,aad,id" pid-field="pid" ></j-tree-select>
* */
import { getAction } from '@/api/manage'
export default {
name: 'JTreeSelect',
props: {
value:{
type: String,
required: false
},
placeholder:{
type: String,
default: '请选择',
required: false
},
dict:{
type: String,
default: '',
required: false
},
pidField:{
type: String,
default: 'pid',
required: false
},
pidValue:{
type: String,
default: '',
required: false
},
disabled:{
type:Boolean,
default:false,
required:false
},
hasChildField:{
type: String,
default: '',
required: false
},
condition:{
type:String,
default:'',
required:false
},
//
multiple: {
type: Boolean,
default: false,
},
loadTriggleChange:{
type: Boolean,
default: false,
required:false
}
},
data () {
return {
treeValue: null,
treeData:[],
url:"/sys/dict/loadTreeData",
view:'/sys/dict/loadDictItem/',
tableName:"",
text:"",
code:"",
}
},
watch: {
value () {
this.loadItemByCode()
},
dict(){
this.initDictInfo()
this.loadRoot();
}
},
created(){
this.validateProp().then(()=>{
this.initDictInfo()
this.loadRoot()
this.loadItemByCode()
})
},
methods: {
loadItemByCode(){
if(!this.value || this.value=="0"){
this.treeValue = null
}else{
getAction(`${this.view}${this.dict}`,{key:this.value}).then(res=>{
if(res.success){
let values = this.value.split(',')
this.treeValue = res.result.map((item, index) => ({
key: values[index],
value: values[index],
label: item
}))
this.onLoadTriggleChange(res.result[0]);
}
})
}
},
onLoadTriggleChange(text){
//
if(!this.multiple && this.loadTriggleChange){
this.$emit('change', this.value,text)
}
},
initDictInfo(){
let arr = this.dict.split(",")
this.tableName = arr[0]
this.text = arr[1]
this.code = arr[2]
},
asyncLoadTreeData (treeNode) {
return new Promise((resolve) => {
if (treeNode.$vnode.children) {
resolve()
return
}
let pid = treeNode.$vnode.key
let param = {
pid:pid,
tableName:this.tableName,
text:this.text,
code:this.code,
pidField:this.pidField,
hasChildField:this.hasChildField,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.addChildren(pid,res.result,this.treeData)
this.treeData = [...this.treeData]
}
resolve()
})
})
},
addChildren(pid,children,treeArray){
if(treeArray && treeArray.length>0){
for(let item of treeArray){
if(item.key == pid){
if(!children || children.length==0){
item.isLeaf=true
}else{
item.children = children
}
break
}else{
this.addChildren(pid,children,item.children)
}
}
}
},
loadRoot(){
let param = {
pid:this.pidValue,
tableName:this.tableName,
text:this.text,
code:this.code,
pidField:this.pidField,
hasChildField:this.hasChildField,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success && res.result){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.treeData = [...res.result]
}else{
console.log("数根节点查询结果-else",res)
}
})
},
onChange(value){
if(!value){
this.$emit('change', '');
this.treeValue = null
} else if (value instanceof Array) {
this.$emit('change', value.map(item => item.value).join(','))
this.treeValue = value
} else {
this.$emit('change', value.value,value.label)
this.treeValue = value
}
},
onSearch(value){
console.log(value)
},
getCurrTreeData(){
return this.treeData
},
validateProp(){
let mycondition = this.condition
return new Promise((resolve,reject)=>{
if(!mycondition){
resolve();
}else{
try {
let test=JSON.parse(mycondition);
console.log("aaaaasdsdd",typeof test)
if(typeof test == 'object' && test){
resolve()
}else{
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
reject()
}
} catch(e) {
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
reject()
}
}
})
}
},
//2.2
model: {
prop: 'value',
event: 'change'
}
}
</script>

180
src/components/jeecg/JTreeTable.vue

@ -0,0 +1,180 @@
<template>
<a-table
:rowKey="rowKey"
:columns="columns"
:dataSource="dataSource"
:expandedRowKeys="expandedRowKeys"
v-bind="tableAttrs"
v-on="$listeners"
@expand="handleExpand"
@expandedRowsChange="expandedRowKeys=$event">
<template v-for="(slotItem) of slots" :slot="slotItem" slot-scope="text, record, index">
<slot :name="slotItem" v-bind="{text,record,index}"></slot>
</template>
</a-table>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JTreeTable',
props: {
rowKey: {
type: String,
default: 'id'
},
// id id
queryKey: {
type: String,
default: 'parentId'
},
queryParams: {
type: Object,
default: () => ({})
},
// 00
topValue: {
type: String,
default: null
},
columns: {
type: Array,
required: true
},
url: {
type: String,
required: true
},
childrenUrl: {
type: String,
default: null
},
tableProps: {
type: Object,
default: () => ({})
},
/** 是否在创建组件的时候就查询数据 */
immediateRequest: {
type: Boolean,
default: true
},
condition:{
type:String,
default:'',
required:false
}
},
data() {
return {
dataSource: [],
expandedRowKeys: []
}
},
computed: {
getChildrenUrl() {
if (this.childrenUrl) {
return this.childrenUrl
} else {
return this.url
}
},
slots() {
let slots = []
for (let column of this.columns) {
if (column.scopedSlots && column.scopedSlots.customRender) {
slots.push(column.scopedSlots.customRender)
}
}
return slots
},
tableAttrs() {
return Object.assign(this.$attrs, this.tableProps)
}
},
watch: {
queryParams: {
deep: true,
handler() {
this.loadData()
}
}
},
created() {
if (this.immediateRequest) this.loadData()
},
methods: {
/** 加载数据*/
loadData(id = this.topValue, first = true, url = this.url) {
this.$emit('requestBefore', { first })
if (first) {
this.expandedRowKeys = []
}
let params = Object.assign({}, this.queryParams || {})
params[this.queryKey] = id
if(this.condition && this.condition.length>0){
params['condition'] = this.condition
}
return getAction(url, params).then(res => {
let list = []
if (res.result instanceof Array) {
list = res.result
} else if (res.result.records instanceof Array) {
list = res.result.records
} else {
throw '返回数据类型不识别'
}
let dataSource = list.map(item => {
//
if (item.hasChildren === true) {
// dataIndex
let firstColumn
for (let column of this.columns) {
firstColumn = column.dataIndex
if (firstColumn) break
}
// loading
let loadChild = { id: `${item.id}_loadChild`, [firstColumn]: 'loading...', isLoading: true }
item.children = [loadChild]
}
return item
})
if (first) {
this.dataSource = dataSource
}
this.$emit('requestSuccess', { first, dataSource, res })
return Promise.resolve(dataSource)
}).finally(() => this.$emit('requestFinally', { first }))
},
/** 点击展开图标时触发 */
handleExpand(expanded, record) {
//
if (expanded) {
//
if (record.children[0].isLoading === true) {
this.loadData(record.id, false, this.getChildrenUrl).then(dataSource => {
// children
if (dataSource.length === 0) {
record.children = null
} else {
record.children = dataSource
}
})
}
}
}
}
}
</script>
<style scoped>
</style>

452
src/components/jeecg/JUpload.vue

@ -0,0 +1,452 @@
<template>
<div :id="containerId" style="position: relative">
<!-- ---------------------------- begin 图片左右换位置 ------------------------------------- -->
<div class="movety-container" :style="{top:top+'px',left:left+'px',display:moveDisplay}" style="padding:0 8px;position: absolute;z-index: 91;height: 32px;width: 104px;text-align: center;">
<div :id="containerId+'-mover'" :class="showMoverTask?'uploadty-mover-mask':'movety-opt'" style="margin-top: 12px">
<a @click="moveLast" style="margin: 0 5px;"><a-icon type="arrow-left" style="color: #fff;font-size: 16px"/></a>
<a @click="moveNext" style="margin: 0 5px;"><a-icon type="arrow-right" style="color: #fff;font-size: 16px"/></a>
</div>
</div>
<!-- ---------------------------- end 图片左右换位置 ------------------------------------- -->
<a-upload
name="file"
:multiple="true"
:action="uploadAction"
:headers="headers"
:data="{'biz':bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
@change="handleChange"
:disabled="disabled"
:returnUrl="returnUrl"
:listType="complistType"
@preview="handlePreview"
:class="{'uploadty-disabled':disabled}">
<template>
<div v-if="isImageComp">
<a-icon type="plus" />
<div class="ant-upload-text">{{ text }}</div>
</div>
<a-button v-else-if="buttonVisible">
<a-icon type="upload" />{{ text }}
</a-button>
</template>
</a-upload>
<a-modal :visible="previewVisible" :width="1000" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</div>
</template>
<script>
import Vue from 'vue'
import { getFileAccessHttpUrl } from '@/api/manage';
import { fileSizeLimit } from '@/api/api'
const FILE_TYPE_ALL = "all"
const FILE_TYPE_IMG = "image"
const FILE_TYPE_TXT = "file"
const uidGenerator=()=>{
return '-'+parseInt(Math.random()*10000+1,10);
}
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JUpload',
data(){
return {
uploadAction:window._CONFIG['domianURL']+"/systemConfig/upload",
headers:{},
fileList: [],
newFileList: [],
uploadGoOn:true,
previewVisible: false,
//---------------------------- begin -------------------------------------
previewImage: '',
containerId:'',
top:'',
left:'',
moveDisplay:'none',
showMoverTask:false,
moverHold:false,
currentImg:'',
//---------------------------- end -------------------------------------
sizeLimit: 0
}
},
props:{
text:{
type:String,
required:false,
default:"点击上传"
},
fileType:{
type:String,
required:false,
default:FILE_TYPE_ALL
},
/*这个属性用于控制文件上传的业务路径*/
bizPath:{
type:String,
required:false,
default:"temp"
},
value:{
type:[String,Array],
required:false
},
// update-begin- --- author:wangshuai ------ date:20190929 ---- for:Jupload
disabled:{
type:Boolean,
required:false,
default: false
},
// update-end- --- author:wangshuai ------ date:20190929 ---- for:Jupload
//
triggerChange:{
type: Boolean,
required: false,
default: false
},
/**
* update -- author:lvdandan -- date:20190219 -- for:Jupload组件增加是否返回url
* true仅返回url
* false返回fileName filePath fileSize
*/
returnUrl:{
type:Boolean,
required:false,
default: true
},
number:{
type:Number,
required:false,
default: 0
},
buttonVisible:{
type:Boolean,
required:false,
default: true
},
},
watch:{
value:{
immediate: true,
handler() {
let val = this.value
if (val instanceof Array) {
if(this.returnUrl){
this.initFileList(val.join(','))
}else{
this.initFileListArr(val);
}
} else {
this.initFileList(val)
}
}
}
},
computed:{
isImageComp(){
return this.fileType === FILE_TYPE_IMG
},
complistType(){
return this.fileType === FILE_TYPE_IMG?'picture-card':'text'
}
},
created(){
this.initFileSizeLimit()
const token = Vue.ls.get("");
//---------------------------- begin -------------------------------------
this.headers = {"X-Access-Token":token};
this.containerId = 'container-ty-'+new Date().getTime();
//---------------------------- end -------------------------------------
},
methods:{
initFileSizeLimit() {
fileSizeLimit().then((res)=>{
if(res.code === 200) {
this.sizeLimit = res.data
}
})
},
initFileListArr(val){
if(!val || val.length==0){
this.fileList = [];
return;
}
let fileList = [];
for(var a=0;a<val.length;a++){
let url = getFileAccessHttpUrl(val[a].filePath);
fileList.push({
uid:uidGenerator(),
name:val[a].fileName,
status: 'done',
url: url,
response:{
code:"history",
data:val[a].filePath
}
})
}
this.fileList = fileList
},
initFileList(paths){
if(!paths || paths.length==0){
//return [];
// update-begin- --- author:os_chengtgen ------ date:20190729 ---- for:issues:326,Juploadbug
this.fileList = [];
return;
// update-end- --- author:os_chengtgen ------ date:20190729 ---- for:issues:326,Juploadbug
}
let fileList = [];
let arr = paths.split(",")
for(var a=0;a<arr.length;a++){
let url = getFileAccessHttpUrl(arr[a]);
fileList.push({
uid:uidGenerator(),
name:getFileName(arr[a]),
status: 'done',
url: url,
response:{
code:"history",
data:arr[a]
}
})
}
this.fileList = fileList
},
handlePathChange(){
let uploadFiles = this.fileList
let path = ''
if(!uploadFiles || uploadFiles.length==0){
path = ''
}
let arr = [];
for(var a=0;a<uploadFiles.length;a++){
arr.push(uploadFiles[a].response.data)
}
if(arr.length>0){
path = arr.join(",")
}
this.$emit('change', path);
},
beforeUpload(file){
this.uploadGoOn=true
let fileType = file.type;
let fileSize = file.size;
if(this.fileType===FILE_TYPE_IMG){
if(fileType.indexOf('image')<0){
this.$message.warning('请上传图片');
this.uploadGoOn=false
return false;
}
}
//
if(fileSize>this.sizeLimit) {
let parseSizeLimit = (this.sizeLimit/1024/1024).toFixed(2)
this.$message.warning('抱歉,文件大小不能超过' + parseSizeLimit + 'M');
this.uploadGoOn=false
return false;
}
return true
},
handleChange(info) {
console.log("--文件列表改变--")
if(!info.file.status && this.uploadGoOn === false){
info.fileList.pop();
}
let fileList = info.fileList
if(info.file.status==='done'){
if(this.number>0){
fileList = fileList.slice(-this.number);
}
if(info.file.response.code === 200){
fileList = fileList.map((file) => {
if (file.response) {
let reUrl = file.response.data;
file.url = getFileAccessHttpUrl(reUrl);
}
return file;
});
}
//this.$message.success(`${info.file.name} !`);
}else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`);
}else if(info.file.status === 'removed'){
this.handleDelete(info.file)
}
this.fileList = fileList
if(info.file.status==='done' || info.file.status === 'removed'){
//returnUrltrue
if(this.returnUrl){
this.handlePathChange()
}else{
//returnUrlfalse
this.newFileList = [];
for(var a=0;a<fileList.length;a++){
var fileJson = {
fileName:fileList[a].name,
filePath:fileList[a].response.data,
fileSize:fileList[a].size
};
this.newFileList.push(fileJson);
}
this.$emit('change', this.newFileList);
}
}
},
handleDelete(file){
//
console.log(file)
},
handlePreview(file){
let postfix = file.name.substring(file.name.indexOf('.'))
if(postfix === '.gif' || postfix === '.jpg' || postfix === '.jpeg' || postfix === '.png' ||
postfix === '.GIF' || postfix === '.JPG' || postfix === '.JPEG' || postfix === '.PNG') {
this.previewImage = file.url || file.thumbUrl;
this.previewVisible = true;
}else{
location.href=file.url
}
},
handleCancel(){
this.previewVisible = false;
},
//---------------------------- begin -------------------------------------
moveLast(){
//console.log(ev)
//console.log(this.fileList)
//console.log(this.currentImg)
let index = this.getIndexByUrl();
if(index==0){
this.$message.warn('未知的操作')
}else{
let curr = this.fileList[index].url;
let last = this.fileList[index-1].url;
let arr =[]
for(let i=0;i<this.fileList.length;i++){
if(i==index-1){
arr.push(curr)
}else if(i==index){
arr.push(last)
}else{
arr.push(this.fileList[i].url)
}
}
this.currentImg = last
this.$emit('change',arr.join(','))
}
},
moveNext(){
let index = this.getIndexByUrl();
if(index==this.fileList.length-1){
this.$message.warn('已到最后~')
}else{
let curr = this.fileList[index].url;
let next = this.fileList[index+1].url;
let arr =[]
for(let i=0;i<this.fileList.length;i++){
if(i==index+1){
arr.push(curr)
}else if(i==index){
arr.push(next)
}else{
arr.push(this.fileList[i].url)
}
}
this.currentImg = next
this.$emit('change',arr.join(','))
}
},
getIndexByUrl(){
for(let i=0;i<this.fileList.length;i++){
if(this.fileList[i].url === this.currentImg || encodeURI(this.fileList[i].url) === this.currentImg){
return i;
}
}
return -1;
}
},
mounted(){
const moverObj = document.getElementById(this.containerId+'-mover');
moverObj.addEventListener('mouseover',()=>{
this.moverHold = true
this.moveDisplay = 'block';
});
moverObj.addEventListener('mouseout',()=>{
this.moverHold = false
this.moveDisplay = 'none';
});
let picList = document.getElementById(this.containerId)?document.getElementById(this.containerId).getElementsByClassName('ant-upload-list-picture-card'):[];
if(picList && picList.length>0){
picList[0].addEventListener('mouseover',(ev)=>{
ev = ev || window.event;
let target = ev.target || ev.srcElement;
if('ant-upload-list-item-info' == target.className){
this.showMoverTask=false
let item = target.parentElement
this.left = item.offsetLeft
this.top=item.offsetTop+item.offsetHeight-50;
this.moveDisplay = 'block';
this.currentImg = target.getElementsByTagName('img')[0].src
}
});
picList[0].addEventListener('mouseout',(ev)=>{
ev = ev || window.event;
let target = ev.target || ev.srcElement;
//console.log('',target)
if('ant-upload-list-item-info' == target.className){
this.showMoverTask=true
setTimeout(()=>{
if(this.moverHold === false)
this.moveDisplay = 'none';
},100)
}
if('ant-upload-list-item ant-upload-list-item-done' == target.className || 'ant-upload-list ant-upload-list-picture-card'== target.className){
this.moveDisplay = 'none';
}
})
//---------------------------- end -------------------------------------
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style lang="less">
.uploadty-disabled{
.ant-upload-list-item {
.anticon-close{
display: none;
}
.anticon-delete{
display: none;
}
}
}
//---------------------------- begin -------------------------------------
.uploadty-mover-mask{
background-color: rgba(0, 0, 0, 0.5);
opacity: .8;
color: #fff;
height: 28px;
line-height: 28px;
}
//---------------------------- end -------------------------------------
</style>

509
src/components/jeecg/README.md

@ -0,0 +1,509 @@
# JDate 日期组件 使用文档
###### 说明: antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| placeholder |string | | placeholder |
| readOnly | boolean | | true/false 默认false |
| value | string | | 绑定v-model或是v-decorator后不需要设置 |
| showTime | boolean | | 是否展示时间true/false 默认false |
| dateFormat | string | |日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss) |
| triggerChange | string | |触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true |
使用示例
----
1.组件带有v-model的使用方法
```vue
<j-date v-model="dateStr"></j-date>
```
2.组件带有v-decorator的使用方法
a).设置trigger-change属性为true
```vue
<j-date :trigger-change="true" v-decorator="['dateStr',{}]"></j-date>
```
b).设置decorator的option.trigger为input
```vue
<j-date v-decorator="['dateStr',{trigger:'input'}]"></j-date>
```
3.其他使用
添加style
```vue
<j-date v-model="dateStr" style="width:100%"></j-date>
```
添加placeholder
```vue
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
```
添加readOnly
```vue
<j-date v-model="dateStr" :read-only="true"></j-date>
```
备注:
script内需引入jdate
```vue
<script>
import JDate from '@/components/jeecg/JDate'
export default {
name: "demo",
components: {
JDate
}
//...
}
</script>
```
---
# JSuperQuery 高级查询 使用文档
## 参数配置
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|----|----------------------|
| fieldList | array |✔| 需要查询的列集合示例如下,type类型有:date/datetime/string/int/number |
| callback | array | | 回调函数名称(非必须)默认handleSuperQuery |
fieldList结构示例:
```vue
const superQueryFieldList=[{
type:"date",
value:"birthday",
text:"生日"
},{
type:"string",
value:"name",
text:"用户名"
},{
type:"int",
value:"age",
text:"年龄"
}]
```
页面代码概述:
----
1.import之后再components之内声明
```vue
import JSuperQuery from '@/components/jeecg/JSuperQuery.vue';
export default {
name: "JeecgDemoList",
components: {
JSuperQuery
},
```
2.页面引用
```vue
<!-- 高级查询区域 -->
<j-super-query :fieldList="fieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query>
```
3.list页面data中需要定义三个属性:
```vue
fieldList:superQueryFieldList,
superQueryFlag:false,
superQueryParams:""
```
4.list页面声明回调事件handleSuperQuery(与组件的callback对应即可)
```vue
//高级查询方法
handleSuperQuery(arg) {
if(!arg){
this.superQueryParams=''
this.superQueryFlag = false
}else{
this.superQueryFlag = true
this.superQueryParams=JSON.stringify(arg)
}
this.loadData()
},
```
5.改造list页面方法
```vue
// 获取查询条件
getQueryParams() {
let sqp = {}
if(this.superQueryParams){
sqp['superQueryParams']=encodeURI(this.superQueryParams)
}
var param = Object.assign(sqp, this.queryParam, this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
```
6.打开弹框调用show方法:
```vue
this.$refs.superQueryModal.show();
```
# JEllipsis 字符串超长截取省略号显示
###### 说明: 遇到超长文本展示,通过此标签可以截取省略号显示,鼠标放置会提示全文本
## 参数配置
| 参数 | 类型 | 必填 | 说明 |
|--------|---------|----|----------------|
| value |string | 必填 | 字符串文本|
| length | number | 非必填 | 默认25 |
使用示例
----
1.组件带有v-model的使用方法
```vue
<j-ellipsis :value="text"/>
# Modal弹框实现最大化功能
1.定义modal的宽度:
```vue
<a-modal
:width="modalWidth"
/>
```
2.自定义modal的title,居右显示切换图标
```vue
<template slot="title">
<div style="width: 100%;">
<span>{{ title }}</span>
<span style="display:inline-block;width:calc(100% - 51px);padding-right:10px;text-align: right">
<a-button @click="toggleScreen" icon="appstore" style="height:20px;width:20px;border:0px"></a-button>
</span>
</div>
</template>
```
3.定义toggleScreen事件,用于切换modal宽度
```vue
toggleScreen(){
if(this.modaltoggleFlag){
this.modalWidth = window.innerWidth;
}else{
this.modalWidth = 800;
}
this.modaltoggleFlag = !this.modaltoggleFlag;
},
```
4.data中声明上述用到的属性
```vue
data () {
return {
modalWidth:800,
modaltoggleFlag:true,
```
# <a-select/> 下拉选项滚动错位的解决方法
## 问题描述
当使用了 `a-modal` 或其他带有滚动条的组件时,使用`a-select`组件并打开下拉框时滚动滚动条,就会导致错位的问题产生。
## 解决方法
大多数情况下,在 `a-select` 上添加一个 `getPopupContainer` 属性,值为`node => node.parentNode`即可解决。
但是如果遇到 `a-select` 标签层级过深的情况,可能仍然会显示异常,只需要多加几个`.parentNode` (例:node => node.parentNode.parentNode.parentNode)多尝试几次直到解决问题即可。
### 代码示例
```html
<a-select
placeholder="请选择展示模板"
:options="dicts.displayTemplate"
:getPopupContainer="node => node.parentNode"
/>
```
# JAsyncTreeList 异步数列表组件使用说明
## 引入组件
```js
import JTreeTable from '@/components/jeecg/JTreeTable'
export default {
components: { JTreeTable }
}
```
## 所需参数
| 参数 | 类型 | 必填 | 说明 |
|-------------|--------|--------|--------------------------------------------------------------|
| rowKey | String | 非必填 | 表格行 key 的取值,默认为"id" |
| columns | Array | 必填 | 表格列的配置描述,具体见Antd官方文档 |
| url | String | 必填 | 数据查询url |
| childrenUrl | String | 非必填 | 查询子级时的url,若不填则使用url参数查询子级 |
| queryKey | String | 非必填 | 根据某个字段查询,如果传递 id 就根据 id 查询,默认为parentId |
| queryParams | Object | 非必填 | 查询参数,当查询参数改变的时候会自动重新查询,默认为{} |
| topValue | String | 非必填 | 查询顶级时的值,如果顶级为0,则传0,默认为null |
| tableProps | Object | 非必填 | 自定义给内部table绑定的props |
## 代码示例
```html
<template>
<a-card :bordered="false">
<j-tree-table :url="url" :columns="columns" :tableProps="tableProps"/>
</a-card>
</template>
<script>
import JTreeTable from '@/components/jeecg/JTreeTable'
export default {
name: 'AsyncTreeTable',
components: { JTreeTable },
data() {
return {
url: '/api/asynTreeList',
columns: [
{ title: '菜单名称', dataIndex: 'name' },
{ title: '组件', dataIndex: 'component' },
{ title: '排序', dataIndex: 'orderNum' }
],
selectedRowKeys: []
}
},
computed: {
tableProps() {
let _this = this
return {
// 列表项是否可选择
// 配置项见:https://vue.ant.design/components/table-cn/#rowSelection
rowSelection: {
selectedRowKeys: _this.selectedRowKeys,
onChange: (selectedRowKeys) => _this.selectedRowKeys = selectedRowKeys
}
}
}
}
}
</script>
```
# JCheckbox 使用文档
###### 说明: antd-vue checkbox组件处理的是数组,用起来不是很方便,特二次封装,使用时只需处理字符串即可
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| options |array |✔| checkbox需要配置的项,是个数组,数组中每个对象包含两个属性:label(用于显示)和value(用于存储) |
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="v-model式用法">
<j-checkbox v-model="sport" :options="sportOptions"></j-checkbox><span>{{ sport }}</span>
</a-form-item>
<a-form-item label="v-decorator式用法">
<j-checkbox v-decorator="['sport']" :options="sportOptions"></j-checkbox><span>{{ getFormFieldValue('sport') }}</span>
</a-form-item>
</a-form>
</template>
<script>
import JCheckbox from '@/components/jeecg/JCheckbox'
export default {
components: {JCheckbox},
data() {
return {
form: this.$form.createForm(this),
sport:'',
sportOptions:[
{
label:"足球",
value:"1"
},{
label:"篮球",
value:"2"
},{
label:"乒乓球",
value:"3"
}]
}
},
methods: {
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
# JCodeEditor 使用文档
###### 说明: 一个简易版的代码编辑器,支持语法高亮
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| language |string | | 表示当前编写代码的类型 javascript/html/css/sql |
| placeholder |string | | placeholder |
| lineNumbers |Boolean | | 是否显示行号 |
| fullScreen |Boolean | | 是否显示全屏按钮 |
| zIndex |string | | 全屏以后的z-index |
使用示例
----
```vue
<template>
<div>
<j-code-editor
language="javascript"
v-model="editorValue"
:fullScreen="true"
style="min-height: 100px"/>
{{ editorValue }}
</div>
</template>
<script>
import JCodeEditor from '@/components/jeecg/JCodeEditor'
export default {
components: {JCodeEditor},
data() {
return {
form: this.$form.createForm(this),
editorValue:'',
}
}
}
</script>
```
# JFormContainer 使用文档
###### 说明: 暂用于表单禁用
使用示例
----
```vue
<!-- 在form下直接写这个组件,设置disabled为true就能将此form中的控件禁用 -->
<a-form layout="inline" :form="form" >
<j-form-container disabled>
<!-- 表单内容省略..... -->
</j-form-container>
</a-form>
```
# JImportModal 使用文档
###### 说明: 用于列表页面导入excel功能
使用示例
----
```vue
<template>
<!-- 此处省略部分代码...... -->
<a-button @click="handleImportXls" type="primary" icon="upload">导入</a-button>
<!-- 此处省略部分代码...... -->
<j-import-modal ref="importModal" :url="getImportUrl()" @ok="importOk"></j-import-modal>
<!-- 此处省略部分代码...... -->
</template>
<script>
import JCodeEditor from '@/components/jeecg/JCodeEditor'
export default {
components: {JCodeEditor},
data() {
return {
//省略代码......
}
},
methods:{
//省略部分代码......
handleImportXls(){
this.$refs.importModal.show()
},
getImportUrl(){
return '你自己处理上传业务的后台地址'
},
importOk(){
this.loadData(1)
}
}
}
</script>
```
# JSlider 滑块验证码
使用示例
----
```vue
<template>
<div style="width: 300px">
<j-slider @onSuccess="sliderSuccess"></j-slider>
</div>
</template>
<script>
import JSlider from '@/components/jeecg/JSlider'
export default {
components: {JSlider},
data() {
return {
form: this.$form.createForm(this),
editorValue:'',
}
},
methods:{
sliderSuccess(){
console.log("验证完成")
}
}
}
</script>
```
# JTreeSelect 树形下拉组件
异步加载的树形下拉组件
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| placeholder |string | | placeholder |
| dict |string | ✔| 表名,显示字段名,存储字段名拼接的字符串 |
| pidField |string | ✔| 父ID的字段名 |
| pidValue |string | | 根节点父ID的值 默认'0' 不可以设置为空,如果想使用此组件,而数据库根节点父ID为空,请修改之 |
| multiple |boolean | |是否支持多选 |
使用示例
----
```vue
<template>
<a-form>
<a-form-item label="树形下拉测试" style="width: 300px">
<j-tree-select
v-model="departId"
placeholder="请选择部门"
dict="sys_depart,depart_name,id"
pidField="parent_id">
</j-tree-select>
{{ departId }}
</a-form-item>
</a-form >
</template>
<script>
import JTreeSelect from '@/components/jeecg/JTreeSelect'
export default {
components: {JTreeSelect},
data() {
return {
departId:""
}
}
}
</script>
```

577
src/components/jeecg/README_JEditableTable.md

@ -0,0 +1,577 @@
# JEditableTable 帮助文档
## 参数配置
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|------|---------------------------------------------------------------------------------|
| columns | array | ✔ | 表格列的配置描述,具体项见下表 |
| dataSource | array | ✔ | 表格数据 |
| loading | boolean | | 是否正在加载,加载中不会显示任何行,默认false |
| actionButton | boolean | | 是否显示操作按钮,包括"新增"、"删除",默认false |
| rowNumber | boolean | | 是否显示行号,默认false |
| rowSelection | boolean | | 是否可选择行,默认false |
| dragSort | boolean | | 是否可拖动排序,默认false |
| dragSortKey | string | | 拖动排序存储的Key,无需定义在columns内也能在getValues()时获取到值,默认orderNum |
| maxHeight | number | | 设定最大高度(px),默认400 |
| disabledRows | object | | 设定禁用的行,被禁用的行无法被选择和编辑,配置方法可以查看示例 |
| disabled | boolean | | 是否禁用所有行,默认false |
### columns 参数详解
| 参数 | 类型 | 必填 | 说明 |
|---------------|---------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
| title | string | ✔ | 表格列头显示的问题 |
| key | string | ✔ | 列数据在数据项中对应的 key,必须是唯一的 |
| type | string | ✔ | 表单的类型,可以通过`JEditableTableUtil.FormTypes`赋值 |
| width | string | | 列的宽度,可以是百分比,也可以是`px`或其他单位,建议设置为百分比,且每一列的宽度加起来不应超过100%,否则可能会不能达到预期的效果。留空会自动计算百分比 |
| placeholder | string | | 表单预期值的提示信息,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`) |
| defaultValue | string | | 默认值,在新增一行时生效 |
| validateRules | array | | 表单验证规则,配置方式见[validateRules 配置规则](#validaterules-配置规则) |
| props | object | | 设置添加给表单元素的自定义属性,例如:`props:{title: 'show title'}` |
| disabled | boolean | | 是否禁用当前列,默认false |
#### 当 type=checkbox 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|----------------|---------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| defaultChecked | boolean | | 默认值是否选中 |
| customValue | array | | 自定义值,checkbox需要的是boolean值,如果数据是其他值(例如`'Y' or 'N'`)时,就会导致错误,所以提供了该属性进行转换,例:`customValue: ['Y','N']`,会将`true`转换为`'Y'`,`false`转换为`'N'`,反之亦然 |
#### 当 type=select 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|------------|---------|------|----------------------------------------------------|
| options | array | ✔ | 下拉选项列表,详见下表 |
| allowInput | boolean | | 是否允许用户输入内容,并创建新的内容 |
| dictCode | String | | 数据字典Code,若options也有值,则拼接在options后面 |
##### options 所需参数
| 参数 | 类型 | 必填 | 说明 |
|-----------|------------|------|----------------------------------------------------------------------|
| text | string | ✔ | 显示标题 |
| value | string | ✔ | 真实值 |
| ~~title~~ | ~~string~~ | | ~~显示标题(已废弃,若同时填写了 title 和 text 那么优先使用 text)~~ |
#### 当 type=upload 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|--------------|---------|------|--------------------------------------------------------------------------------------|
| action | string | ✔ | 上传文件路径 |
| token | boolean | | 上传的时候是否传递token |
| responseName | string | ✔ | 若要从上传成功后从response中取出返回的文件名,那么这里填后台返回的包含文件名的字段名 |
#### 当 type=slot 时所需的参数
| 参数 | 类型 | 必填 | 说明 |
|----------|--------|------|------------|
| slotName | string | ✔ | slot的名称 |
### validateRules 配置规则
`validateRules` 需要的是一个数组,数组里每项都是一个规则,规则是object类型,规则的各个参数如下
- `required` 是否必填,可选值为`true`or`false`
- `pattern` 正则表达式验证,只有成功匹配该正则的值才能成功通过验证
- `handler` 自定义函数校验,使用方法请见[示例五](#示例五)
- `message` 当验证未通过时显示的提示文本,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`)
- 配置示例请看[示例二](#示例二)
## 事件
| 事件名 | 触发时机 | 参数 |
|-----------------|----------------------------------------------------|--------------------------------------------------|
| added | 当添加行操作完成后触发 | |
| deleted | 当删除行操作完成后触发(批量删除操作只会触发一次) | `deleteIds` 被逻辑删除的id |
| selectRowChange | 当行被选中或取消选中时触发 | `selectedRowIds` 被选中行的id |
| valueChange | 当数据发生改变的时候触发的事件 | `{ type, row, column, value, target }` Event对象 |
## 方法
关于方法的如何调用的问题,请在**FAQ**中查看[方法如何调用](#方法如何调用)
### initialize
用于初始化表格(清空表格)
- `参数:`
- `返回值:`
### resetScrollTop
重置滚动条Top位置
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|------|--------------------------------------------------------------------------------------------------------|
| top | number | | 新top位置,留空则滚动到上次记录的位置,用于解决切换tab选项卡时导致白屏以及自动将滚动条滚动到顶部的问题 |
- `返回值:`
### add
主动添加行,默认情况下,当用户的滚动条已经在底部的时候,会将滚动条固定在底部,即添加后无需用户手动滚动,而会自动滚动到底部
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|---------------------|---------|------|---------------------------------------------------------------------|
| num | number | | 添加几行,默认为1 |
| forceScrollToBottom | boolean | | 是否在添加后无论用户的滚动条在什么位置都强制滚动到底部,默认为false |
- `返回值:`
### removeRows
主动删除一行或多行
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|--------|-----------------|------|--------------------------------------------------------------------------------------------|
| id | string 或 array | ✔ | 被删除行的id。如果要删除一个,可以直接传id,如果要删除多个,需要将多个id封装成一个数组传入 |
- `返回值:`
### removeSelectedRows
主动删除被选中的行
- `参数:`
- `返回值:`
### getValues
用于获取表格里所有表单的值,可进行表单验证
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|----------|----------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| callback | function | ✔ | 获取值的回调方法,会传入`error`和`values`两个参数。`error`:未通过验证的数量,当等于`0`时代表验证通过;`values`:获取的值(即使未通过验证该字段也有数据) |
| validate | boolean | | 是否进行表单验证,默认为`true`,设为`false`则代表忽略表单验证 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:`
### getValuesSync
`getValues`的同步版,会直接将获取到的数据返回
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|---------|--------|------|------------------------|
| options | object | | 选项,详见下方所需参数 |
- - `options` 所需参数
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|------|----------------------------------------------------------------------------------------------------------------------|
| validate | boolean | | 是否进行表单验证,默认为`true`,设为`false`则代表忽略表单验证 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:` object
- `error` 未通过验证的数量,当等于`0`时代表验证通过
- `values` 获取的值(即使未通过验证该字段也有数据)
- `使用示例`
```js
let { error, values } = this.$refs.editableTable.getValuesSync({ validate: true, rowIds: ['rowId1', 'rowId2'] })
if (error === 0) {
console.log('表单验证通过,数据:', values);
} else {
console.log('未通过表单验证,数据:', values);
}
```
### getValuesPromise
`getValues`的promise版,会在`resolve`中传入获取到的值,会在`reject`中传入失败原因,例如`VALIDATE_NO_PASSED`
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|------|----------------------------------------------------------------------------------------------------------------------|
| validate | boolean | | 同`getValues`的`validate`参数 |
| rowIds | array | | 默认返回所有行的数据,如果传入了`rowIds`,那么就会只返回与该`rowIds`相匹配的数据,如果没有匹配的数据,就会返回空数组 |
- `返回值:` Promise
### getDeleteIds
用于获取被逻辑删除的行的id,返回一个数组,用户可将该数组传入后台,并进行批量删除
- `参数:`
- `返回值:` array
### getAll
获取所有的数据,包括values、deleteIds
会在`resolve`中传入获取到的值:`{values, deleteIds}`
会在`reject`中传入失败原因,例如`VALIDATE_NO_PASSED`
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|----------|---------|------|-------------------------------|
| validate | boolean | | 同`getValues`的`validate`参数 |
- `返回值:` Promise
### setValues
主动设置表格中某行某列的值
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|--------|-------|------|------------------------------------------------------------|
| values | array | | 传入一个数组,数组中的每项都是一行的新值,具体见下面的示例 |
- `返回值:`
- `示例:`
```js
setValues([
{
rowKey: id1, // 行的id
values: { // 在这里 values 中的 name 是你 columns 中配置的 key
'name': 'zhangsan',
'age': '20'
}
},
{
rowKey: id2,
values: {
'name': 'lisi',
'age': '23'
}
}
])
```
### clearSelection
主动清空选择的行
- `参数:`
- `返回值:`
## 内置插槽
| 插槽名 | 说明 |
|--------------|------------------------------------------------------|
| buttonBefore | 在操作按钮的**前面**插入插槽,不受`actionButton`属性的影响 |
| buttonAfter | 在操作按钮的**后面**插入插槽,不受`actionButton`属性的影响 |
## ${...} 变量使用方式
在`placeholder`和`message`这两个属性中可以使用`${...}`变量来替换文本
在[示例二](#示例二)中,配置了`title`为`名称`的一列,而`placeholder`配置成了`请输入${title}`,那么最终显示效果为`请输入名称`
这就是`${...}`变量的使用方式,在`${}`中可以使用的变量有`title`、`key`、`defaultValue`这三个属性的值
## JEditableTableUtil 使用说明
在之前配置`columns`时提到过`JEditableTableUtil`这个工具类,那么如果想要知道详细的使用说明就请看这里
### export 的常量
#### FormTypes
这是配置`columns.type`时用到的常量值,其中包括
- `normal` 默认,直接显示值,不渲染表单
- `input` 显示输入框
- `inputNumber` 显示数字输入框
- `checkbox` 显示多选框
- `select` 显示选择器(下拉框)
- `date` 日期选择器
- `datetime` 日期时间选择器
- `upload` 上传组件(文件域)
- `slot` 自定义插槽
### VALIDATE_NO_PASSED
在判断表单验证是否通过时使用,如果 reject 的值 === VALIDATE_NO_PASSED 则代表表单验证未通过,你可以做相应的其他处理,反之则可能是发生了报错,可以使用 `console.error` 输出
### 封装的方法
#### validateTables
当你的页面中存在多个JEditableTable实例的时候,如果要获取每个实例的值、判断表单验证是否通过,就会让代码变得极其冗余、繁琐,于是我们就将该操作封装成了一个函数供你调用,它可以同时获取并验证多个JEditableTable实例的值,只有当所有实例的表单验证都通过后才会返回值,否则将会告诉你具体哪个实例没有通过验证。具体使用方法请看下面的示例
- `参数:`
| 参数名 | 类型 | 必填 | 说明 |
|--------|-------|------|--------------------------------------------------------|
| cases | array | | 传入一个数组,数组中的每项都是一个JEditableTable的实例 |
- `返回值:` Promise
- `示例:`
```js
import { validateTables, VALIDATE_NO_PASSED } from '@/utils/JEditableTableUtil'
// 封装cases
let cases = []
cases.push(this.$refs.editableTable1)
cases.push(this.$refs.editableTable2)
cases.push(this.$refs.editableTable3)
cases.push(this.$refs.editableTable4)
cases.push(this.$refs.editableTable5)
// 同时验证并获取多个实例的值
validateTables(cases).then((all) => {
// all 是一个数组,每项都对应传入cases的下标,包含values和deleteIds
console.log('所有实例的值:', all)
}).catch((e = {}) => {
// 判断表单验证是否未通过
if (e.error === VALIDATE_NO_PASSED) {
console.log('未通过验证的实例下标:', e.index)
} else {
console.error('发生异常:', e)
}
})
```
## FAQ
### 方法如何调用?
在[示例一](#示例一)中,设定了一个 `ref="editableTable"` 的属性,那么在vue中就可以使用`this.$refs.editableTable`获取到该表格的实例,并调取其中的方法。
假如我要调取`initialize`方法,就可以这么写:`this.$refs.editableTable.initialize()`
### 如何获取表单的值?
使用`getValue`方法进行获取,详见[示例三](#示例三)
### 如何进行表单验证?
在获取值的时候默认会进行表单验证操作,用户在输入的时候也会对正在输入的表单进行验证,只要配置好规则就可以了
### 如何添加或删除一行?
该功能已封装到组件中,你只需要将 `actionButton` 设置为 `true` 即可,当然你也可以在代码中主动调用新增方法或修改,具体见上方的方法介绍。
### 为什么使用了ATab组件后,切换选项卡会导致白屏或滚动条位置会归零?
在ATab组件中确实会导致滚动条位置归零,且不会触发`onscroll`方法,所以无法动态加载行,导致白屏的问题出现。
解决方法是在ATab组件的`onChange`事件触发时执行实例提供的`resetScrollTop()`方法即可,但是需要注意的是:代码主动改变ATab的`activeKey`不会触发`onChange`事件,还需要你手动调用下。
- `示例`
```html
<template>
<a-tabs @change="handleChangeTab">
<a-tab-pane tab="表格1" :forceRender="true" key="1">
<j-editable-table
ref="editableTable1"
:loading="tab1.loading"
:columns="tab1.columns"
:dataSource="tab1.dataSource"/>
</a-tab-pane>
<a-tab-pane tab="表格2" :forceRender="true" key="2">
<j-editable-table
ref="editableTable2"
:loading="tab2.loading"
:columns="tab2.columns"
:dataSource="tab2.dataSource"/>
</a-tab-pane>
</a-tabs>
</template>
```
```js
/*--- 忽略部分代码片段 ---*/
methods: {
/** 切换tab选项卡的时候重置editableTable的滚动条状态 */
handleChangeTab(key) {
this.$refs[`editableTable${key}`].resetScrollTop()
}
}
/*--- 忽略部分代码片段 ---*/
```
### slot(自定义插槽)如何使用?
代码示例请看:[示例四(slot)](#示例四(slot))
----------------------------------------------------------------------------------------
## 示例一
```html
<j-editable-table
ref="editableTable"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:rowNumber="true"
:rowSelection="true"
:actionButton="true"
style="margin-top: 8px;"
@selectRowChange="handleSelectRowChange"/>
```
## 示例二
```js
import { FormTypes } from '@/utils/JEditableTableUtil'
/*--- 忽略部分代码片断 ---*/
columns: [
{
title: '名称',
key: 'name',
type: FormTypes.input,
placeholder: '请输入${title}',
defaultValue: '称名',
// 表单验证规则
validateRules: [
{
required: true, // 必填
message: '${title}不能为空' // 提示的文本
},
{
pattern: /^[a-z|A-Z][a-z|A-Z\d_-]{0,}$/, // 正则
message: '${title}必须以字母开头,可包含数字、下划线、横杠'
}
]
},
{
title: '年龄',
key: 'age',
type: FormTypes.inputNumber,
placeholder: '请输入${title}',
defaultValue: 18,
validateRules: [{required: true, message: '${title}不能为空'}]
}
]
/*--- 忽略部分代码片断 ---*/
```
## 示例三
```js
// 获取被逻辑删除的字段id
let deleteIds = this.$refs.editableTable.getDeleteIds();
// 获取所有表单的值,并进行验证
this.$refs.editableTable.getValues((error, values) => {
// 错误数 = 0 则代表验证通过
if (error === 0) {
this.$message.success('验证通过')
// 将通过后的数组提交到后台或自行进行其他处理
console.log(deleteIds, values)
} else {
this.$message.error('验证未通过')
}
})
```
## 示例四(slot)
```html
<template>
<j-editable-table :columns="columns" :dataSource="dataSource">
<!-- 定义插槽 -->
<!-- 这种定义插槽的写法是vue推荐的新版写法(https://cn.vuejs.org/v2/guide/components-slots.html#具名插槽),旧版已被废弃的写法不再支持 -->
<!-- 若webstorm这样写报错,请看这篇文章:https://blog.csdn.net/lxq_9532/article/details/81870651 -->
<template v-slot:action="props">
<a @click="handleDelete(props)">删除</a>
</template>
</j-editable-table>
</template>
<script>
import { FormTypes } from '@/utils/JEditableTableUtil'
import JEditableTable from '@/components/jeecg/JEditableTable'
export default {
components: { JEditableTable },
data() {
return {
columns: [
// ...
{
title: '操作',
key: 'action',
width: '8%',
type: FormTypes.slot, // 定义该列为 自定义插值列
slotName: 'action' // slot 的名称,对应 v-slot 冒号后面和等号前面的内容
}
]
}
},
methods: {
/* a 标签的点击事件,删除当前选中的行 */
handleDelete(props) {
// 参数解释
// props.index :当前行的下标
// props.text :当前值,可能是defaultValue定义的值,也可能是从dataSource中取出的值
// props.rowId :当前选中行的id,如果是新增行则是临时id
// props.column :当前操作的列
// props.getValue :这是一个function,执行后可以获取当前行的所有值(禁止在template中使用)
// 例:const value = props.getValue()
// props.target :触发当前事件的实例,可直接调用该实例内的方法(禁止在template中使用)
// 例:target.add()
// 使用实例:删除当前操作的行
let { rowId, target } = props
target.removeRows(rowId)
}
}
}
</script>
```
## 示例五
```js
// 该示例是自定义函数校验
columns: [
{
title: '字段名称',
key: 'dbFieldName',
type: FormTypes.input,
defaultValue: '',
validateRules: [
{
// 自定义函数校验 handler
handler(type, value, row, column, callback, target) {
// type 触发校验的类型(input、change、blur)
// value 当前校验的值
// callback(flag, message) 方法必须执行且只能执行一次
// flag = 是否通过了校验,不填写或者填写 null 代表不进行任何操作
// message = 提示的类型,默认使用配置的 message
// target 行编辑的实例对象
if (type === 'blur') {
if (value === 'abc') {
callback(false, '${title}不能是abc') // false = 未通过,可以跟自定义提示
return
}
let { values } = target.getValuesSync({ validate: false })
let count = 0
for (let val of values) {
if (val['dbFieldName'] === value) {
if (++count >= 2) {
callback(false, '${title}不能重复')
return
}
}
}
callback(true) // true = 通过验证
} else {
callback() // 不填写或者填写 null 代表不进行任何操作
}
},
message: '${title}默认提示'
}
]
},
]
```

9
src/components/jeecg/index.js

@ -0,0 +1,9 @@
import JModal from './JModal'
import JFormContainer from './JFormContainer.vue'
export default {
install(Vue) {
Vue.component('JFormContainer', JFormContainer)
Vue.component(JModal.name, JModal)
}
}

113
src/components/jeecg/minipop/JFilePop.vue

@ -0,0 +1,113 @@
<template>
<div>
<a-modal
title="文件上传"
:width="width"
:visible="visible"
@ok="ok"
cancelText="取消"
@cancel="close">
<!--style="top: 20px;"-->
<j-upload :file-type="fileType" :value="filePath" @change="handleChange" :disabled="disabled"></j-upload>
</a-modal>
</div>
</template>
<script>
import JUpload from '@/components/jeecg/JUpload'
import { getFileAccessHttpUrl } from '@/api/manage';
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JFilePop',
components: { JUpload },
props:{
title:{
type:String,
default:'',
required:false
},
position:{
type:String,
default:'right',
required:false
},
height:{
type:Number,
default:200,
required:false
},
width:{
type:Number,
default:520,
required:false
},
popContainer:{
type:String,
default:'',
required:false
},
disabled:{
type:Boolean,
default:false,
required:false
}
},
data(){
return {
visible:false,
filePath:'',
id:'',
fileType:'file'
}
},
methods:{
handleChange(value){
this.filePath = value;
},
show(id,value,flag){
this.id = id;
this.filePath = value;
this.visible=true
if(flag === 'img'){
this.fileType = 'image'
}else{
this.fileType = 'file'
}
},
ok(){
if(!this.filePath){
this.$message.error("未上传任何文件")
return false;
}
let arr = this.filePath.split(",")
let obj = {
name:getFileName(arr[0]),
url:getFileAccessHttpUrl(arr[0]),
path:this.filePath,
status: 'done',
id:this.id
}
this.$emit('ok',obj)
this.visible=false
},
close(){
this.visible=false
}
}
}
</script>
<style scoped>
</style>

98
src/components/jeecg/minipop/JInputPop.vue

@ -0,0 +1,98 @@
<template>
<a-popover trigger="contextmenu" v-model="visible" :placement="position">
<!--"(node) => node.parentNode.parentNode"-->
<div slot="title">
<span>{{ title }}</span>
<span style="float: right" title="关闭">
<a-icon type="close" @click="visible=false"/>
</span>
</div>
<a-input :value="inputContent" @change="handleInputChange">
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
</a-input>
<div slot="content">
<textarea :value="inputContent" @input="handleInputChange" :style="{ height: height + 'px', width: width + 'px' }"></textarea>
</div>
</a-popover>
</template>
<script>
export default {
name: 'JInputPop',
props:{
title:{
type:String,
default:'',
required:false
},
position:{
type:String,
default:'right',
required:false
},
height:{
type:Number,
default:200,
required:false
},
width:{
type:Number,
default:150,
required:false
},
value:{
type:String,
required:false
},
popContainer:{
type:String,
default:'',
required:false
}
},
data(){
return {
visible:false,
inputContent:''
}
},
watch:{
value:{
immediate:true,
handler:function(){
if(this.value && this.value.length>0){
this.inputContent = this.value;
}
}
},
},
model: {
prop: 'value',
event: 'change'
},
methods:{
handleInputChange(event){
this.inputContent = event.target.value
this.$emit('change',this.inputContent)
},
pop(){
this.visible=true
},
getPopupContainer(node){
if(!this.popContainer){
return node.parentNode
}else{
return document.getElementById(this.popContainer)
}
}
}
}
</script>
<style scoped>
</style>

928
src/components/jeecg/modal/JCronModal.vue

@ -0,0 +1,928 @@
<template>
<a-modal
title="corn表达式"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭">
<div class="card-container">
<a-tabs type="card">
<a-tab-pane key="1" type="card">
<span slot="tab"><a-icon type="schedule" /> </span>
<a-radio-group v-model="result.second.cronEvery">
<a-row>
<a-radio value="1">每一秒钟</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.second.incrementIncrement" :min="1" :max="59"></a-input-number>
秒执行
<a-input-number size="small" v-model="result.second.incrementStart" :min="0" :max="59"></a-input-number>
秒开始
</a-radio>
</a-row>
<a-row>
<a-radio value="3">具体秒数(可多选)</a-radio>
<a-select style="width:354px;" size="small" mode="multiple" v-model="result.second.specificSpecific">
<a-select-option v-for="(val,index) in 60" :key="index" :value="index">{{ index }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="4">周期从
<a-input-number size="small" v-model="result.second.rangeStart" :min="1" :max="59"></a-input-number>
<a-input-number size="small" v-model="result.second.rangeEnd" :min="0" :max="59"></a-input-number>
</a-radio>
</a-row>
</a-radio-group>
</a-tab-pane>
<a-tab-pane key="2">
<span slot="tab"><a-icon type="schedule" /></span>
<div class="tabBody">
<a-radio-group v-model="result.minute.cronEvery">
<a-row>
<a-radio value="1">每一分钟</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.minute.incrementIncrement" :min="1" :max="60"></a-input-number>
分执行
<a-input-number size="small" v-model="result.minute.incrementStart" :min="0" :max="59"></a-input-number>
分开始
</a-radio>
</a-row>
<a-row>
<a-radio value="3">具体分钟数(可多选)</a-radio>
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.minute.specificSpecific">
<a-select-option v-for="(val,index) in Array(60)" :key="index" :value="index"> {{ index }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="4">周期从
<a-input-number size="small" v-model="result.minute.rangeStart" :min="1" :max="60"></a-input-number>
<a-input-number size="small" v-model="result.minute.rangeEnd" :min="0" :max="59"></a-input-number>
</a-radio>
</a-row>
</a-radio-group>
</div>
</a-tab-pane>
<a-tab-pane key="3">
<span slot="tab"><a-icon type="schedule" /> </span>
<div class="tabBody">
<a-radio-group v-model="result.hour.cronEvery">
<a-row>
<a-radio value="1">每一小时</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.hour.incrementIncrement" :min="0" :max="23"></a-input-number>
小时执行
<a-input-number size="small" v-model="result.hour.incrementStart" :min="0" :max="23"></a-input-number>
小时开始
</a-radio>
</a-row>
<a-row>
<a-radio class="long" value="3">具体小时数(可多选)</a-radio>
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.hour.specificSpecific">
<a-select-option v-for="(val,index) in Array(24)" :key="index" >{{ index }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="4">周期从
<a-input-number size="small" v-model="result.hour.rangeStart" :min="0" :max="23"></a-input-number>
<a-input-number size="small" v-model="result.hour.rangeEnd" :min="0" :max="23"></a-input-number>
小时
</a-radio>
</a-row>
</a-radio-group>
</div>
</a-tab-pane>
<a-tab-pane key="4">
<span slot="tab"><a-icon type="schedule" /> </span>
<div class="tabBody">
<a-radio-group v-model="result.day.cronEvery">
<a-row>
<a-radio value="1">每一天</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.week.incrementIncrement" :min="1" :max="7"></a-input-number>
周执行
<a-select size="small" v-model="result.week.incrementStart">
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
</a-select>
开始
</a-radio>
</a-row>
<a-row>
<a-radio value="3">每隔
<a-input-number size="small" v-model="result.day.incrementIncrement" :min="1" :max="31"></a-input-number>
天执行
<a-input-number size="small" v-model="result.day.incrementStart" :min="1" :max="31"></a-input-number>
天开始
</a-radio>
</a-row>
<a-row>
<a-radio class="long" value="4">具体星期几(可多选)</a-radio>
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.week.specificSpecific">
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio class="long" value="5">具体天数(可多选)</a-radio>
<a-select style="width:354px;" size="small" mode="multiple" v-model="result.day.specificSpecific">
<a-select-option v-for="(val,index) in Array(31)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="6">在这个月的最后一天</a-radio>
</a-row>
<a-row>
<a-radio value="7">在这个月的最后一个工作日</a-radio>
</a-row>
<a-row>
<a-radio value="8">在这个月的最后一个
<a-select size="small" v-model="result.day.cronLastSpecificDomDay">
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
</a-select>
</a-radio>
</a-row>
<a-row>
<a-radio value="9">
在本月底前
<a-input-number size="small" v-model="result.day.cronDaysBeforeEomMinus" :min="1" :max="31"></a-input-number>
</a-radio>
</a-row>
<a-row>
<a-radio value="10">最近的工作日周一至周五至本月
<a-input-number size="small" v-model="result.day.cronDaysNearestWeekday" :min="1" :max="31"></a-input-number>
</a-radio>
</a-row>
<a-row>
<a-radio value="11">在这个月的第
<a-input-number size="small" v-model="result.week.cronNthDayNth" :min="1" :max="5"></a-input-number>
<a-select size="small" v-model="result.week.cronNthDayDay">
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
</a-select>
</a-radio>
</a-row>
</a-radio-group>
</div>
</a-tab-pane>
<a-tab-pane key="5">
<span slot="tab"><a-icon type="schedule" /> </span>
<div class="tabBody">
<a-radio-group v-model="result.month.cronEvery">
<a-row>
<a-radio value="1">每一月</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.month.incrementIncrement" :min="0" :max="12"></a-input-number>
月执行
<a-input-number size="small" v-model="result.month.incrementStart" :min="0" :max="12"></a-input-number>
月开始
</a-radio>
</a-row>
<a-row>
<a-radio class="long" value="3">具体月数(可多选)</a-radio>
<a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.month.specificSpecific">
<a-select-option v-for="(val,index) in Array(12)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="4">
<a-input-number size="small" v-model="result.month.rangeStart" :min="1" :max="12"></a-input-number>
<a-input-number size="small" v-model="result.month.rangeEnd" :min="1" :max="12"></a-input-number>
月之间的每个月
</a-radio>
</a-row>
</a-radio-group>
</div>
</a-tab-pane>
<a-tab-pane key="6">
<span slot="tab"><a-icon type="schedule" /> </span>
<div class="tabBody">
<a-radio-group v-model="result.year.cronEvery">
<a-row>
<a-radio value="1">每一年</a-radio>
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.year.incrementIncrement" :min="1" :max="99"></a-input-number>
年执行
<a-input-number size="small" v-model="result.year.incrementStart" :min="2019" :max="2119"></a-input-number>
年开始
</a-radio>
</a-row>
<a-row>
<a-radio class="long" value="3">具体年份(可多选)</a-radio>
<a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.year.specificSpecific">
<a-select-option v-for="(val,index) in Array(100)" :key="index" :value="2019+index">{{ 2019+index }}</a-select-option>
</a-select>
</a-row>
<a-row>
<a-radio value="4">
<a-input-number size="small" v-model="result.year.rangeStart" :min="2019" :max="2119"></a-input-number>
<a-input-number size="small" v-model="result.year.rangeEnd" :min="2019" :max="2119"></a-input-number>
年之间的每一年
</a-radio>
</a-row>
</a-radio-group>
</div>
</a-tab-pane>
</a-tabs>
<div class="bottom">
<span class="value">{{this.cron }}</span>
</div>
</div>
</a-modal>
</template>
<script>
export default {
name:'VueCron',
props:['data'],
data(){
return {
visible: false,
confirmLoading:false,
size:'large',
weekDays:['天','一','二','三','四','五','六'].map(val=>'星期'+val),
result: {
second:{},
minute:{},
hour:{},
day:{},
week:{},
month:{},
year:{}
},
defaultValue: {
second:{
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:0,
specificSpecific:[],
},
minute:{
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:'0',
specificSpecific:[],
},
hour:{
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:'0',
rangeEnd:'0',
specificSpecific:[],
},
day:{
cronEvery:'',
incrementStart:1,
incrementIncrement:'1',
rangeStart:'',
rangeEnd:'',
specificSpecific:[],
cronLastSpecificDomDay:1,
cronDaysBeforeEomMinus:1,
cronDaysNearestWeekday:1,
},
week:{
cronEvery:'',
incrementStart:1,
incrementIncrement:1,
specificSpecific:[],
cronNthDayDay:1,
cronNthDayNth:1,
},
month:{
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:1,
specificSpecific:[],
},
year:{
cronEvery:'',
incrementStart:2017,
incrementIncrement:1,
rangeStart:2019,
rangeEnd: 2019,
specificSpecific:[],
},
label:''
}
}
},
computed: {
modalWidth(){
return 608;
},
secondsText() {
let seconds = '';
let cronEvery=this.result.second.cronEvery||'';
switch (cronEvery.toString()){
case '1':
seconds = '*';
break;
case '2':
seconds = this.result.second.incrementStart+'/'+this.result.second.incrementIncrement;
break;
case '3':
this.result.second.specificSpecific.map(val=> {seconds += val+','});
seconds = seconds.slice(0, -1);
break;
case '4':
seconds = this.result.second.rangeStart+'-'+this.result.second.rangeEnd;
break;
}
return seconds;
},
minutesText() {
let minutes = '';
let cronEvery=this.result.minute.cronEvery||'';
switch (cronEvery.toString()){
case '1':
minutes = '*';
break;
case '2':
minutes = this.result.minute.incrementStart+'/'+this.result.minute.incrementIncrement;
break;
case '3':
this.result.minute.specificSpecific.map(val=> {
minutes += val+','
});
minutes = minutes.slice(0, -1);
break;
case '4':
minutes = this.result.minute.rangeStart+'-'+this.result.minute.rangeEnd;
break;
}
return minutes;
},
hoursText() {
let hours = '';
let cronEvery=this.result.hour.cronEvery||'';
switch (cronEvery.toString()){
case '1':
hours = '*';
break;
case '2':
hours = this.result.hour.incrementStart+'/'+this.result.hour.incrementIncrement;
break;
case '3':
this.result.hour.specificSpecific.map(val=> {
hours += val+','
});
hours = hours.slice(0, -1);
break;
case '4':
hours = this.result.hour.rangeStart+'-'+this.result.hour.rangeEnd;
break;
}
return hours;
},
daysText() {
let days='';
let cronEvery=this.result.day.cronEvery||'';
switch (cronEvery.toString()){
case '1':
break;
case '2':
case '4':
case '11':
days = '?';
break;
case '3':
days = this.result.day.incrementStart+'/'+this.result.day.incrementIncrement;
break;
case '5':
this.result.day.specificSpecific.map(val=> {
days += val+','
});
days = days.slice(0, -1);
break;
case '6':
days = "L";
break;
case '7':
days = "LW";
break;
case '8':
days = this.result.day.cronLastSpecificDomDay + 'L';
break;
case '9':
days = 'L-' + this.result.day.cronDaysBeforeEomMinus;
break;
case '10':
days = this.result.day.cronDaysNearestWeekday+"W";
break
}
return days;
},
weeksText() {
let weeks = '';
let cronEvery=this.result.day.cronEvery||'';
switch (cronEvery.toString()){
case '1':
case '3':
case '5':
weeks = '?';
break;
case '2':
weeks = this.result.week.incrementStart+'/'+this.result.week.incrementIncrement;
break;
case '4':
this.result.week.specificSpecific.map(val=> {
weeks += val+','
});
weeks = weeks.slice(0, -1);
break;
case '6':
case '7':
case '8':
case '9':
case '10':
weeks = "?";
break;
case '11':
weeks = this.result.week.cronNthDayDay+"#"+this.result.week.cronNthDayNth;
break;
}
return weeks;
},
monthsText() {
let months = '';
let cronEvery=this.result.month.cronEvery||'';
switch (cronEvery.toString()){
case '1':
months = '*';
break;
case '2':
months = this.result.month.incrementStart+'/'+this.result.month.incrementIncrement;
break;
case '3':
this.result.month.specificSpecific.map(val=> {
months += val+','
});
months = months.slice(0, -1);
break;
case '4':
months = this.result.month.rangeStart+'-'+this.result.month.rangeEnd;
break;
}
return months;
},
yearsText() {
let years = '';
let cronEvery=this.result.year.cronEvery||'';
switch (cronEvery.toString()){
case '1':
years = '*';
break;
case '2':
years = this.result.year.incrementStart+'/'+this.result.year.incrementIncrement;
break;
case '3':
this.result.year.specificSpecific.map(val=> {
years += val+','
});
years = years.slice(0, -1);
break;
case '4':
years = this.result.year.rangeStart+'-'+this.result.year.rangeEnd;
break;
}
return years;
},
cron(){
return `${this.secondsText||'*'} ${this.minutesText||'*'} ${this.hoursText||'*'} ${this.daysText||'*'} ${this.monthsText||'*'} ${this.weeksText||'?'} ${this.yearsText||'*'}`
},
},
watch:{
visible:{
handler() {
// if(this.data){
// //this. result = Object.keys(this.data.value).length>0?this.deepCopy(this.data.value):this.deepCopy(this.defaultValue);
// //this.result = Object.keys(this.data.value).length>0?clone(this.data.value):clone(this.defaultValue);
// //this.result = Object.keys(this.data.value).length>0?clone(JSON.parse(this.data.value)):clone(this.defaultValue);
// this.result = Object.keys(this.data.value).length>0?JSON.parse(this.data.value):JSON.parse(JSON.stringify(this.defaultValue));
// }else{
// //this.result = this.deepCopy(this.defaultValue);
// //this.result = clone(this.defaultValue);
// this.result = JSON.parse(JSON.stringify(this.defaultValue));
// }
let label = this.data;
if(label){
this.secondsReverseExp(label)
this.minutesReverseExp(label);
this.hoursReverseExp(label);
this.daysReverseExp(label);
this.daysReverseExp(label);
this.monthsReverseExp(label);
this.yearReverseExp(label);
JSON.parse(JSON.stringify(label));
}else {
this.result = JSON.parse(JSON.stringify(this.defaultValue));
}
}
}
},
methods: {
show(){
this.visible = true;
// console.log('secondsReverseExp',this.secondsReverseExp(this.data));
// console.log('minutesReverseExp',this.minutesReverseExp(this.data));
// console.log('hoursReverseExp',this.hoursReverseExp(this.data));
// console.log('daysReverseExp',this.daysReverseExp(this.data));
// console.log('monthsReverseExp',this.monthsReverseExp(this.data));
// console.log('yearReverseExp',this.yearReverseExp(this.data));
},
handleSubmit(){
this.$emit('ok',this.cron);
this.close();
this.visible = false;
},
close(){
this.visible = false;
},
secondsReverseExp(seconds) {
let val = seconds.split(" ")[0];
//alert(val);
let second = {
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:0,
specificSpecific:[]
};
switch (true) {
case val.includes('*'):
second.cronEvery = '1';
break;
case val.includes('/'):
second.cronEvery = '2';
second.incrementStart = val.split('/')[0];
second.incrementIncrement = val.split('/')[1];
break;
case val.includes(','):
second.cronEvery = '3';
second.specificSpecific = val.split(',').map(Number).sort();
break;
case val.includes('-'):
second.cronEvery = '4';
second.rangeStart = val.split('-')[0];
second.rangeEnd = val.split('-')[1];
break;
default:
second.cronEvery = '1';
}
this.result.second = second;
},
minutesReverseExp(minutes) {
let val = minutes.split(" ")[1];
let minute = {
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:0,
specificSpecific:[],
}
switch (true) {
case val.includes('*'):
minute.cronEvery = '1';
break;
case val.includes('/'):
minute.cronEvery = '2';
minute.incrementStart = val.split('/')[0];
minute.incrementIncrement = val.split('/')[1];
break;
case val.includes(','):
minute.cronEvery = '3';
minute.specificSpecific = val.split(',').map(Number).sort();
break;
case val.includes('-'):
minute.cronEvery = '4';
minute.rangeStart = val.split('-')[0];
minute.rangeEnd = val.split('-')[1];
break;
default:
minute.cronEvery = '1';
}
this.result.minute = minute;
},
hoursReverseExp(hours) {
let val = hours.split(" ")[2];
let hour ={
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:'0',
specificSpecific:[],
};
switch (true) {
case val.includes('*'):
hour.cronEvery = '1';
break;
case val.includes('/'):
hour.cronEvery = '2';
hour.incrementStart = val.split('/')[0];
hour.incrementIncrement = val.split('/')[1];
break;
case val.includes(','):
hour.cronEvery = '3';
hour.specificSpecific = val.split(',').map(Number).sort();
break;
case val.includes('-'):
hour.cronEvery = '4';
hour.rangeStart = val.split('-')[0];
hour.rangeEnd = val.split('-')[1];
break;
default:
hour.cronEvery = '1';
}
this.result.hour = hour;
},
daysReverseExp(cron) {
let days = cron.split(" ")[3];
let weeks = cron.split(" ")[5];
let day ={
cronEvery:'',
incrementStart:1,
incrementIncrement:1,
rangeStart:1,
rangeEnd:1,
specificSpecific:[],
cronLastSpecificDomDay:1,
cronDaysBeforeEomMinus:1,
cronDaysNearestWeekday:1,
};
let week = {
cronEvery:'',
incrementStart:1,
incrementIncrement:1,
specificSpecific:[],
cronNthDayDay:1,
cronNthDayNth:'1',
};
if (!days.includes('?')) {
switch (true) {
case days.includes('*'):
day.cronEvery = '1';
break;
case days.includes('?'):
// 2411
break;
case days.includes('/'):
day.cronEvery = '3';
day.incrementStart = days.split('/')[0];
day.incrementIncrement = days.split('/')[1];
break;
case days.includes(','):
day.cronEvery = '5';
day.specificSpecific = days.split(',').map(Number).sort();
// day.specificSpecific.forEach(function (value, index) {
// day.specificSpecific[index] = value -1;
// });
break;
case days.includes('LW'):
day.cronEvery = '7';
break;
case days.includes('L-'):
day.cronEvery = '9';
day.cronDaysBeforeEomMinus = days.split('L-')[1];
break;
case days.includes('L'):
//alert(days);
if(days.len == 1){
day.cronEvery = '6';
day.cronLastSpecificDomDay = '1';
}
else
{
day.cronEvery = '8';
day.cronLastSpecificDomDay = Number(days.split('L')[0]);
}
break;
case days.includes('W'):
day.cronEvery = '10';
day.cronDaysNearestWeekday = days.split('W')[0];
break;
default:
day.cronEvery = '1';
}
}else {
switch (true){
case weeks.includes('/'):
day.cronEvery = '2';
week.incrementStart = weeks.split("/")[0];
week.incrementIncrement = weeks.split("/")[1];
break;
case weeks.includes(','):
day.cronEvery = '4';
week.specificSpecific = weeks.split(',').map(Number).sort();
break;
case '#':
day.cronEvery = '11';
week.cronNthDayDay = weeks.split("#")[0];
week.cronNthDayNth = weeks.split("#")[1];
break;
default:
day.cronEvery = '1';
week.cronEvery = '1';
}
}
this.result.day = day;
this.result.week = week;
},
monthsReverseExp(cron) {
let months = cron.split(" ")[4];
let month = {
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:1,
rangeEnd:1,
specificSpecific:[],
};
switch (true){
case months.includes('*'):
month.cronEvery = '1';
break;
case months.includes('/'):
month.cronEvery = '2';
month.incrementStart = months.split('/')[0];
month.incrementIncrement = months.split('/')[1];
break;
case months.includes(','):
month.cronEvery = '3';
month.specificSpecific = months.split(',').map(Number).sort();
break;
case months.includes('-'):
month.cronEvery = '4';
month.rangeStart = months.split('-')[0];
month.rangeEnd = months.split('-')[1];
break;
default:
month.cronEvery = '1';
}
this.result.month = month;
},
yearReverseExp(cron) {
let years = cron.split(" ")[6];
let year = {
cronEvery:'',
incrementStart:3,
incrementIncrement:5,
rangeStart:2019,
rangeEnd:2019,
specificSpecific:[],
};
switch (true){
case years.includes('*'):
year.cronEvery = '1';
break;
case years.includes('/'):
year.cronEvery = '2';
year.incrementStart = years.split('/')[0];
year.incrementIncrement = years.split('/')[1];
break;
case years.includes(','):
year.cronEvery = '3';
year.specificSpecific = years.split(',').map(Number).sort();
break;
case years.includes('-'):
year.cronEvery = '4';
year.rangeStart = years.split('-')[0];
year.rangeEnd = years.split('-')[1];
break;
default:
year.cronEvery = '1';
}
this.result.year = year;
}
}
}
</script>
<style lang="less">
.card-container {
background: #fff;
overflow: hidden;
padding: 12px;
position: relative;
width: 100%;
.ant-tabs{
border:1px solid #e6ebf5;
padding: 0;
.ant-tabs-bar {
margin: 0;
outline: none;
border-bottom: none;
.ant-tabs-nav-container{
margin: 0;
.ant-tabs-tab {
padding: 0 24px!important;
background-color: #f5f7fa!important;
margin-right: 0px!important;
border-radius: 0;
line-height: 38px;
border: 1px solid transparent!important;
border-bottom: 1px solid #e6ebf5!important;
}
.ant-tabs-tab-active.ant-tabs-tab{
color: #409eff;
background-color: #fff!important;
border-right:1px solid #e6ebf5!important;
border-left:1px solid #e6ebf5!important;
border-bottom:1px solid #fff!important;
font-weight: normal;
transition:none!important;
}
}
}
.ant-tabs-tabpane{
padding: 15px;
.ant-row{
margin: 10px 0;
}
.ant-select,.ant-input-number{
width: 100px;
}
}
}
}
</style>
<style lang="less" scoped>
.container-widthEn{
width: 755px;
}
.container-widthCn{
width: 608px;
}
.language{
text-align: center;
position: absolute;
right: 13px;
top: 13px;
border: 1px solid transparent;
height: 40px;
line-height: 38px;
font-size: 16px;
color: #409eff;
z-index: 1;
background: #f5f7fa;
outline: none;
width: 47px;
border-bottom: 1px solid #e6ebf5;
border-radius: 0;
}
.card-container{
.bottom{
display: flex;
justify-content: center;
padding: 10px 0 0 0;
.cronButton{
margin: 0 10px;
line-height: 40px;
}
}
}
.tabBody{
.a-row{
margin: 10px 0;
.long{
.a-select{
width:354px;
}
}
.a-input-number{
width: 110px;
}
}
}
</style>

334
src/components/jeecgbiz/JSelectBizComponent/JSelectBizComponentModal.vue

@ -0,0 +1,334 @@
<template>
<a-modal
centered
:title="name + '选择'"
:width="width"
:visible="visible"
@ok="handleOk"
@cancel="close"
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="24">
<a-col :span="14">
<a-form-item :label="(queryParamText||name)">
<a-input v-model="queryParam[queryParamCode||valueKey]" :placeholder="'请输入' + (queryParamText||name)" @pressEnter="searchQuery"/>
</a-form-item>
</a-col>
<a-col :span="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<a-table
size="small"
bordered
:rowKey="rowKey"
:columns="innerColumns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:scroll="{ y: 240 }"
:rowSelection="{selectedRowKeys, onChange: onSelectChange, type: multiple ? 'checkbox':'radio'}"
:customRow="customRowFn"
@change="handleTableChange">
</a-table>
</a-col>
<a-col :span="8">
<a-card :title="'已选' + name" :bordered="false" :head-style="{padding:0}" :body-style="{padding:0}">
<a-table size="small" :rowKey="rowKey" bordered v-bind="selectedTable">
<span slot="action" slot-scope="text, record, index">
<a @click="handleDeleteSelected(record, index)">删除</a>
</span>
</a-table>
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import Ellipsis from '@/components/Ellipsis'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { cloneObject, pushIfNotExist } from '@/utils/util'
export default {
name: 'JSelectBizComponentModal',
mixins: [JeecgListMixin],
components: { Ellipsis },
props: {
value: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false
},
valueKey: {
type: String,
required: true
},
multiple: {
type: Boolean,
default: true
},
width: {
type: Number,
default: 900
},
name: {
type: String,
default: ''
},
listUrl: {
type: String,
required: true,
default: ''
},
// value username realname
valueUrl: {
type: String,
default: ''
},
displayKey: {
type: String,
default: null
},
columns: {
type: Array,
required: true,
default: () => []
},
// Code
queryParamCode: {
type: String,
default: null
},
//
queryParamText: {
type: String,
default: null
},
rowKey: {
type: String,
default: 'id'
},
// -1
ellipsisLength: {
type: Number,
default: 12
},
},
data() {
return {
innerValue: [],
//
selectedTable: {
pagination: false,
scroll: { y: 240 },
columns: [
{
...this.columns[0],
width: this.columns[0].widthRight || this.columns[0].width,
},
{ title: '操作', dataIndex: 'action', align: 'center', width: 60, scopedSlots: { customRender: 'action' }, }
],
dataSource: [],
},
renderEllipsis: (value) => (<ellipsis length={this.ellipsisLength}>{value}</ellipsis>),
url: { list: this.listUrl },
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
options: [],
dataSourceMap: {},
}
},
computed: {
//
innerColumns() {
let columns = cloneObject(this.columns)
columns.forEach(column => {
//
if (this.ellipsisLength !== -1) {
column.customRender = (text) => this.renderEllipsis(text)
}
})
return columns
},
},
watch: {
value: {
deep: true,
immediate: true,
handler(val) {
this.innerValue = cloneObject(val)
this.selectedRowKeys = []
this.valueWatchHandler(val)
this.queryOptionsByValue(val)
}
},
dataSource: {
deep: true,
handler(val) {
this.emitOptions(val)
this.valueWatchHandler(this.innerValue)
}
},
selectedRowKeys: {
immediate: true,
deep: true,
handler(val) {
this.selectedTable.dataSource = val.map(key => {
for (let data of this.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
for (let data of this.selectedTable.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
console.warn('未找到选择的行信息,key:' + key)
return {}
})
},
}
},
methods: {
/** 关闭弹窗 */
close() {
this.$emit('update:visible', false)
},
valueWatchHandler(val) {
val.forEach(item => {
this.dataSource.concat(this.selectedTable.dataSource).forEach(data => {
if (data[this.valueKey] === item) {
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
}
})
})
},
queryOptionsByValue(value) {
if (!value || value.length === 0) {
return
}
// optionsvalue
let notExist = false
for (let val of value) {
let find = false
for (let option of this.options) {
if (val === option.value) {
find = true
break
}
}
if (!find) {
notExist = true
break
}
}
if (!notExist) return
getAction(this.valueUrl || this.listUrl, {
// , 使 in
[this.valueKey]: value.join(',') + ',',
pageNo: 1,
pageSize: value.length
}).then((res) => {
if (res.success) {
let dataSource = res.result
if (!(dataSource instanceof Array)) {
dataSource = res.result.records
}
this.emitOptions(dataSource, (data) => {
pushIfNotExist(this.innerValue, data[this.valueKey])
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
})
}
})
},
emitOptions(dataSource, callback) {
dataSource.forEach(data => {
let key = data[this.valueKey]
this.dataSourceMap[key] = data
pushIfNotExist(this.options, { label: data[this.displayKey || this.valueKey], value: key }, 'value')
typeof callback === 'function' ? callback(data) : ''
})
this.$emit('options', this.options, this.dataSourceMap)
},
/** 完成选择 */
handleOk() {
let value = this.selectedTable.dataSource.map(data => data[this.valueKey])
this.$emit('input', value)
this.close()
},
/** 删除已选择的 */
handleDeleteSelected(record, index) {
this.selectedRowKeys.splice(this.selectedRowKeys.indexOf(record[this.rowKey]), 1)
this.selectedTable.dataSource.splice(index, 1)
},
customRowFn(record) {
return {
on: {
click: () => {
let key = record[this.rowKey]
if (!this.multiple) {
this.selectedRowKeys = [key]
this.selectedTable.dataSource = [record]
} else {
let index = this.selectedRowKeys.indexOf(key)
if (index === -1) {
this.selectedRowKeys.push(key)
this.selectedTable.dataSource.push(record)
} else {
this.handleDeleteSelected(record, index)
}
}
}
}
}
},
}
}
</script>
<style lang="less" scoped>
</style>

36
src/components/jeecgbiz/JSelectBizComponent/README.md

@ -0,0 +1,36 @@
# JSelectBizComponent
Jeecg 选择组件的公共可复用组件
## 引用方式
```js
import JSelectBizComponent from '@/src/components/jeecgbiz/JSelectBizComponent'
export default {
components: { JSelectBizComponent }
}
```
## 参数
### 配置参数
| 参数名 | 类型 | 必填 | 默认值 | 备注 |
|-----------------------|---------|------|--------------|--------------------------------------------------------------------------------------|
| rowKey | String | | "id" | 唯一标识的字段名 |
| value(v-model) | String | | "" | 默认选择的数据,多个用半角逗号分割 |
| name | String | | "" | 显示名字,例如选择用户就填写"用户" |
| listUrl | String | 是 | | 数据请求地址,必须是封装了分页的地址 |
| valueUrl | String | | "" | 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname |
| displayKey | String | | null | 显示在标签上的字段 key ,不传则直接显示数据 |
| returnKeys | Array | | ['id', 'id'] | v-model 绑定的 keys,是个数组,默认使用第二项,当配置了 `returnId=true` 就返回第一项 |
| returnId | Boolean | | false | 返回ID,设为true后将返回配置的 `returnKeys` 中的第一项 |
| selectButtonText | String | | "选择" | 选择按钮的文字 |
| queryParamText | String | | null | 查询条件显示文字,不传则使用 `name` |
| columns | Array | 是 | | 列配置项,与antd的table的配置完全一致。列的第一项会被配置成右侧已选择的列表上 |
| columns[0].widthRight | String | | null | 仅列的第一项可以应用此配置,表示右侧已选择列表的宽度,建议 `70%`,不传则应用`width` |
| placeholder | String | | "请选择" | 占位符 |
| disabled | Boolean | | false | 是否禁用 |
| multiple | Boolean | | false | 是否可多选 |
| buttons | Boolean | | true | 是否显示"选择"按钮,如果不显示,可以直接点击文本框打开选择界面 |

165
src/components/jeecgbiz/JSelectBizComponent/index.vue

@ -0,0 +1,165 @@
<template>
<a-row class="j-select-biz-component-box" type="flex" :gutter="8">
<a-col class="left" :class="{'full': !buttons}">
<slot name="left">
<a-select
mode="multiple"
:placeholder="placeholder"
v-model="selectValue"
:options="selectOptions"
allowClear
:disabled="disabled"
:open="selectOpen"
style="width: 100%;"
@dropdownVisibleChange="handleDropdownVisibleChange"
@click.native="visible=(buttons?visible:true)"
/>
</slot>
</a-col>
<a-col v-if="buttons" class="right">
<a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
</a-col>
<j-select-biz-component-modal
v-model="selectValue"
:visible.sync="visible"
v-bind="modalProps"
@options="handleOptions"
/>
</a-row>
</template>
<script>
import JSelectBizComponentModal from './JSelectBizComponentModal'
export default {
name: 'JSelectBizComponent',
components: { JSelectBizComponentModal },
props: {
value: {
type: String,
default: ''
},
/** 是否返回 id,默认 false,返回 code */
returnId: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
// true
multiple: {
type: Boolean,
default: true
},
// true
buttons: {
type: Boolean,
default: true
},
// Key
displayKey: {
type: String,
default: null
},
// key
returnKeys: {
type: Array,
default: () => ['id', 'id']
},
//
selectButtonText: {
type: String,
default: '选择'
},
},
data() {
return {
selectValue: [],
selectOptions: [],
dataSourceMap: {},
visible: false,
selectOpen: false,
}
},
computed: {
valueKey() {
return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
},
modalProps() {
return Object.assign({
valueKey: this.valueKey,
multiple: this.multiple,
returnKeys: this.returnKeys,
displayKey: this.displayKey || this.valueKey
}, this.$attrs)
},
},
watch: {
value: {
immediate: true,
handler(val) {
if (val) {
this.selectValue = val.split(',')
} else {
this.selectValue = []
}
}
},
selectValue: {
deep: true,
handler(val) {
let rows = val.map(key => this.dataSourceMap[key])
this.$emit('select', rows)
let data = val.join(',')
this.$emit('input', data)
this.$emit('change', data)
}
}
},
methods: {
handleOptions(options, dataSourceMap) {
this.selectOptions = options
this.dataSourceMap = dataSourceMap
},
handleDropdownVisibleChange() {
// antdvbug open false open
this.selectOpen = true
this.$nextTick(() => {
this.selectOpen = false
})
},
}
}
</script>
<style lang="less" scoped>
.j-select-biz-component-box {
@width: 82px;
.left {
width: calc(100% - @width - 8px);
}
.right {
width: @width;
}
.full {
width: 100%;
}
/deep/ .ant-select-search__field {
display: none !important;
}
}
</style>

122
src/components/jeecgbiz/JSelectDepart.vue

@ -0,0 +1,122 @@
<template>
<div class="components-input-demo-presuffix">
<!---->
<a-input @click="openModal" placeholder="请点击选择部门" v-model="departNames" readOnly :disabled="disabled">
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
<a-icon v-if="departIds" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<j-select-depart-modal
ref="innerDepartSelectModal"
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="departIds"
@ok="handleOK"
@initComp="initComp"/>
</div>
</template>
<script>
import JSelectDepartModal from './modal/JSelectDepartModal'
export default {
name: 'JSelectDepart',
components:{
JSelectDepartModal
},
props:{
modalWidth:{
type:Number,
default:500,
required:false
},
multi:{
type:Boolean,
default:false,
required:false
},
rootOpened:{
type:Boolean,
default:true,
required:false
},
value:{
type:String,
required:false
},
disabled:{
type: Boolean,
required: false,
default: false
},
// id
customReturnField: {
type: String,
default: 'id'
}
},
data(){
return {
visible:false,
confirmLoading:false,
departNames:"",
departIds:''
}
},
mounted(){
this.departIds = this.value
},
watch:{
value(val){
if (this.customReturnField === 'id') {
this.departIds = val
}
}
},
methods:{
initComp(departNames){
this.departNames = departNames
},
openModal(){
this.$refs.innerDepartSelectModal.show()
},
handleOK(rows, idstr) {
let value = ''
if (!rows && rows.length <= 0) {
this.departNames = ''
this.departIds = ''
} else {
value = rows.map(row => row[this.customReturnField]).join(',')
this.departNames = rows.map(row => row['departName']).join(',')
this.departIds = idstr
}
this.$emit("change", value)
},
getDepartNames(){
return this.departNames
},
handleEmpty(){
this.handleOK('')
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>

327
src/components/jeecgbiz/JSelectList.vue

@ -0,0 +1,327 @@
<template>
<div>
<!-- <a-input-search v-if="kind === 'material'" v-model="names" placeholder="请选择" @search="onSearch"></a-input-search> -->
<a-select
:value="value"
v-if="kind === 'material'"
placeholder="编码 名称"
show-search
style="width: 100px"
dropdownClassName="drop-down-Style"
option-label-prop="value"
:dropdownMatchSelectWidth="false"
:default-active-first-option="false"
:show-arrow="false"
:filter-option="false"
:not-found-content="fetching ? undefined : null"
@search="fetchUser"
@change="handleChange"
@focus="loadData()"
>
<div slot="notFoundContent">
<a-spin v-if="fetching" size="small" />
</div>
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0" />
<div
@mousedown="(e) => e.preventDefault()"
style="display: flex; justify-content: space-between; margin: 0 5px"
>
<a-pagination
:default-current="6"
:current.sync="queryParam.current"
:pageSize.sync="queryParam.pageSize"
@change="changeCurrent"
:total="queryParam.total"
style="margin-bottom: 4px"
/>
<a-button type="primary" @click="confirmSelected">确定</a-button>
</div>
</div>
<a-select-opt-group v-if="materailList.length">
<span slot="label">
<div class="select-option">
<a-checkbox style="margin-right:5px" :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange">
</a-checkbox>
<span class="mr10" style="width: 100px; font-size: 16px">物料编码</span>
<span class="mr10" style="width: 400px; font-size: 16px;">名称</span>
<span class="mr10" style="width: 100px; font-size: 16px">类别</span>
<span class="mr10" style="width: 100px; font-size: 16px">单位</span>
<span class="mr10" style="width: 100px; font-size: 16px">多属性</span>
<span class="mr10" style="width: 100px; font-size: 16px">库存</span>
<span class="mr10" style="width: 100px; font-size: 16px">扩展信息</span>
</div>
</span>
<a-select-option
style="width: 400px"
:value="item.mBarCode"
v-for="(item, index) in materailList"
:key="item.id"
>
<div class="select-option">
<div style="margin-left: -8px;">
<a-checkbox
style="margin-right:5px"
:id="item.mBarCode"
:key="index"
:checked="checkedList.indexOf(item.mBarCode) !== -1"
@change="(e) => onchange(e, item)"
@click.stop.native="() => {}"
>
</a-checkbox>
</div>
<span class="mr10" style="width: 100px">{{ item.mBarCode }}</span>
<span class="mr10" style="width: 400px;width-space:nowrap;text-overflow:ellipsis;overflow: hidden;white-space:nowrap;">{{ item.name }}</span>
<span class="mr10" style="width: 100px">{{ item.categoryName }}</span>
<span class="mr10" style="width: 100px">{{ item.unit }}</span>
<span class="mr10" style="width: 100px">{{ item.sku }}</span>
<span class="mr10" style="width: 100px">{{ item.stock }}</span>
<span class="mr10" style="width: 100px">{{ item.expand }}</span>
</div>
</a-select-option>
</a-select-opt-group>
</a-select>
<a-input-search
v-if="kind === 'batch' || kind === 'sn'"
v-model="names"
placeholder="请选择"
readOnly
@search="onSearch"
></a-input-search>
<j-select-material-modal
v-if="kind === 'material'"
ref="selectModal"
:modal-width="modalWidth"
:rows="rows"
:multi="multi"
:bar-code="value"
@ok="selectOK"
@initComp="initComp"
/>
<j-select-batch-modal
v-if="kind === 'batch'"
ref="selectModal"
:modal-width="modalWidth"
:rows="rows"
:multi="multi"
:bar-code="value"
@ok="selectOK"
@initComp="initComp"
/>
<j-select-sn-modal
v-if="kind === 'sn'"
ref="selectModal"
:modal-width="modalWidth"
:rows="rows"
:multi="multi"
:bar-code="value"
@ok="selectOK"
@initComp="initComp"
/>
</div>
</template>
<script>
import JSelectMaterialModal from './modal/JSelectMaterialModal'
import JSelectBatchModal from './modal/JSelectBatchModal'
import JSelectSnModal from './modal/JSelectSnModal'
import { filterObj, getMpListShort } from '@/utils/util'
import { getMaterialByBarCode, getMaterialBySelect } from '@/api/api'
import Vue from 'vue'
export default {
name: 'JSelectList',
components: {
JSelectMaterialModal,
JSelectBatchModal,
JSelectSnModal,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
},
},
props: {
modalWidth: {
type: Number,
default: 1300,
required: false,
},
value: {
type: String,
required: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
rows: {
type: String,
required: false,
},
kind: {
type: String,
required: false,
},
multi: {
type: Boolean,
default: true,
required: false,
},
},
data() {
return {
ids: '',
names: '',
materailList: [],
fetching: false,
queryParam: {
q: '',
current: 1,
pageSize: 10,
total: 0,
},
checkedList: [],
checkAll: false,
indeterminate: false,
}
},
mounted() {
this.ids = this.value
// this.loadData()
},
watch: {
value(val) {
this.ids = val
},
},
model: {
prop: 'value',
event: 'change',
},
methods: {
onCheckAllChange(e) {
Object.assign(this, {
checkedList: e.target.checked ? this.materailList.map((item) => item.mBarCode) : [],
indeterminate: false,
checkAll: e.target.checked,
})
},
onchange(event, item) {
let index = this.checkedList.indexOf(item.mBarCode)
if (index !== -1) {
this.checkedList.splice(index, 1)
} else {
this.checkedList.push(item.mBarCode)
}
this.indeterminate = !!this.checkedList.length && this.checkedList.length < this.materailList.length
this.checkAll = this.checkedList.length === this.materailList.length
},
confirmSelected() {
let ids = this.checkedList.join(',')
this.$emit('change', ids)
this.open = false
this.loadData()
},
changeCurrent(current) {
this.queryParam.current = current
this.loadData()
},
fetchUser(value) {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.queryParam.q = value
this.loadData(1)
}, 800)
},
getQueryParams() {
let param = {}
param.mpList = getMpListShort(Vue.ls.get('materialPropertyList')) //
param.page = this.queryParam.current
param.rows = this.queryParam.pageSize
param.q = this.queryParam.q
return filterObj(param)
},
async loadData(arg) {
if (arg === 1) {
this.queryParam.current = 1
}
this.fetching = true
let params = this.getQueryParams() //
await getMaterialBySelect(params)
.then((res) => {
if (res) {
this.checkedList = []
this.materailList = res.rows
this.queryParam.total = res.total || 0
}
})
.finally(() => {
this.fetching = false
})
},
initComp(name) {
this.names = name
},
onSearch() {
if (this.kind === 'material') {
let param = {
barCode: this.names,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')), //
prefixNo: this.prefixNo,
}
getMaterialByBarCode(param).then((res) => {
if (res && res.code === 200) {
let mList = res.data
if (mList && mList.length === 1) {
//
this.$emit('change', this.names)
} else {
this.$refs.selectModal.showModal(this.names)
}
}
})
} else {
this.$refs.selectModal.showModal()
}
},
selectOK(rows, idstr) {
console.log('选中id', idstr)
if (!rows) {
this.ids = ''
} else {
this.names = idstr
this.ids = idstr
}
this.$emit('change', this.ids)
},
handleChange(value) {
this.names = value
this.$emit('change', value)
},
focus() {
if (this.names) {
this.queryParam.q = ''
this.loadData()
}
},
},
}
</script>
<style lang="less" scoped>
/deep/ .drop-down-Style {
width: auto;
}
.select-option {
display: flex;
justify-content: space-between;
padding-top: 5px;
width: 1000px;
}
.mr10 {
margin-right: 10px;
}
</style>

87
src/components/jeecgbiz/JSelectMaterial.vue

@ -0,0 +1,87 @@
<template>
<div>
<a-input-search
v-model="materialNames"
placeholder="请选择商品"
readOnly
@search="onSearchMaterial">
</a-input-search>
<j-select-material-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" :bar-code="value" @ok="selectOK" @initComp="initComp"/>
</div>
</template>
<script>
import JSelectMaterialModal from './modal/JSelectMaterialModal'
export default {
name: 'JSelectMaterial',
components: {JSelectMaterialModal},
props: {
modalWidth: {
type: Number,
default: 1300,
required: false
},
value: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
multi: {
type: Boolean,
default: true,
required: false
}
},
data() {
return {
materialIds: "",
materialNames: "",
}
},
mounted() {
this.materialIds = this.value
},
watch: {
value(val) {
this.materialIds = val
}
},
model: {
prop: 'value',
event: 'change'
},
methods: {
initComp(barCode) {
this.materialNames = barCode
},
onSearchMaterial() {
this.$refs.selectModal.showModal()
},
selectOK(rows, idstr) {
console.log("选中商品", rows)
console.log("选中商品id", idstr)
if (!rows) {
this.materialNames = ''
this.materialIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.mBarCode
}
this.materialNames = temp.substring(1)
this.materialIds = idstr
}
this.$emit("change", this.materialIds)
}
}
}
</script>
<style scoped>
</style>

47
src/components/jeecgbiz/JSelectMultiUser.vue

@ -0,0 +1,47 @@
<template>
<!-- 定义在这里的参数都是不可在外部覆盖的防止出现问题 -->
<j-select-biz-component
:value="value"
:ellipsisLength="25"
:listUrl="url.list"
:columns="columns"
v-on="$listeners"
v-bind="attrs"
/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectMultiUser',
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
url: { list: '/sys/user/list' },
columns: [
{ title: '姓名', align: 'center', width: '25%', widthRight: '70%', dataIndex: 'realname' },
{ title: '账号', align: 'center', width: '25%', dataIndex: 'username' },
{ title: '电话', align: 'center', width: '20%', dataIndex: 'phone' },
{ title: '出生日期', align: 'center', width: '20%', dataIndex: 'birthday' }
],
// 使
default: {
name: '用户',
width: 1200,
displayKey: 'realname',
returnKeys: ['id', 'username'],
queryParamText: '账号',
}
}
},
computed: {
attrs() {
return Object.assign(this.default, this.$attrs)
}
}
}
</script>
<style lang="less" scoped></style>

37
src/components/jeecgbiz/JSelectPosition.vue

@ -0,0 +1,37 @@
<template>
<j-select-biz-component :width="1000" v-bind="configs" v-on="$listeners"/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectPosition',
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
settings: {
name: '职务',
displayKey: 'name',
returnKeys: ['id', 'code'],
listUrl: '/sys/position/list',
queryParamCode: 'name',
queryParamText: '职务名称',
columns: [
{ title: '职务名称', dataIndex: 'name', align: 'center', width: '30%', widthRight: '70%' },
{ title: '职务编码', dataIndex: 'code', align: 'center', width: '35%' },
{ title: '职级', dataIndex: 'rank_dictText', align: 'center', width: '25%' }
]
}
}
},
computed: {
configs() {
return Object.assign({ value: this.value }, this.settings, this.$attrs)
}
}
}
</script>
<style lang="less" scoped></style>

38
src/components/jeecgbiz/JSelectRole.vue

@ -0,0 +1,38 @@
<template>
<j-select-biz-component
:value="value"
name="角色"
displayKey="roleName"
:returnKeys="returnKeys"
:listUrl="url.list"
:columns="columns"
queryParamText="角色编码"
v-on="$listeners"
v-bind="$attrs"
/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectMultiUser',
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
returnKeys: ['id', 'roleCode'],
url: { list: '/sys/role/list' },
columns: [
{ title: '角色名称', dataIndex: 'roleName', align: 'center', width: 120 },
{ title: '角色编码', dataIndex: 'roleCode', align: 'center', width: 120 }
]
}
}
}
</script>
<style lang="less" scoped></style>

87
src/components/jeecgbiz/JSelectSerialMaterial.vue

@ -0,0 +1,87 @@
<template>
<div>
<a-input-search
v-model="materialNames"
placeholder="请选择序列号商品"
readOnly
@search="onSearchMaterial">
</a-input-search>
<j-select-serial-material-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" :bar-code="value" @ok="selectOK" @initComp="initComp"/>
</div>
</template>
<script>
import JSelectSerialMaterialModal from './modal/JSelectSerialMaterialModal'
export default {
name: 'JSelectSerialMaterial',
components: {JSelectSerialMaterialModal},
props: {
modalWidth: {
type: Number,
default: 1100,
required: false
},
value: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
multi: {
type: Boolean,
default: true,
required: false
}
},
data() {
return {
materialIds: "",
materialNames: "",
}
},
mounted() {
this.materialIds = this.value
},
watch: {
value(val) {
this.materialIds = val
}
},
model: {
prop: 'value',
event: 'change'
},
methods: {
initComp(barCode) {
this.materialNames = barCode
},
onSearchMaterial() {
this.$refs.selectModal.showModal()
},
selectOK(rows, idstr) {
console.log("选中序列号商品", rows)
console.log("选中序列号商品id", idstr)
if (!rows) {
this.materialNames = ''
this.materialIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.mBarCode
}
this.materialNames = temp.substring(1)
this.materialIds = idstr
}
this.$emit("change", this.materialIds)
}
}
}
</script>
<style scoped>
</style>

89
src/components/jeecgbiz/JSelectUserByDep.vue

@ -0,0 +1,89 @@
<template>
<div>
<a-input-search
v-model="userNames"
placeholder="请先选择用户"
readOnly
unselectable="on"
@search="onSearchDepUser">
<a-button slot="enterButton" :disabled="disabled">选择用户</a-button>
</a-input-search>
<j-select-user-by-dep-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" @ok="selectOK" :user-ids="value" @initComp="initComp"/>
</div>
</template>
<script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
export default {
name: 'JSelectUserByDep',
components: {JSelectUserByDepModal},
props: {
modalWidth: {
type: Number,
default: 1250,
required: false
},
value: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
multi: {
type: Boolean,
default: true,
required: false
},
},
data() {
return {
userIds: "",
userNames: ""
}
},
mounted() {
this.userIds = this.value
},
watch: {
value(val) {
this.userIds = val
}
},
model: {
prop: 'value',
event: 'change'
},
methods: {
initComp(userNames) {
this.userNames = userNames
},
onSearchDepUser() {
this.$refs.selectModal.showModal()
},
selectOK(rows, idstr) {
console.log("当前选中用户", rows)
console.log("当前选中用户ID", idstr)
if (!rows) {
this.userNames = ''
this.userIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.realname
}
this.userNames = temp.substring(1)
this.userIds = idstr
}
this.$emit("change", this.userIds)
}
}
}
</script>
<style scoped>
</style>

137
src/components/jeecgbiz/README.md

@ -0,0 +1,137 @@
# JSelectDepart 部门选择组件
选择部门组件,存储部门ID,显示部门名称
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| modalWidth |Number | | 弹框宽度 默认500 |
| multi |Boolean | | 是否多选 默认false |
| rootOpened |Boolean | | 是否展开根节点 默认true |
| disabled |Boolean | | 是否禁用 默认false|
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="部门选择v-decorator" style="width: 300px">
<j-select-depart v-decorator="['bumen']"/>
{{ getFormFieldValue('bumen') }}
</a-form-item>
<a-form-item label="部门选择v-model" style="width: 300px">
<j-select-depart v-model="bumen"/>
{{ bumen }}
</a-form-item>
<a-form-item label="部门多选v-model" style="width: 300px">
<j-select-depart v-model="bumens" :multi="true"/>
{{ bumens }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectDepart from '@/components/jeecgbiz/JSelectDepart'
export default {
components: {JSelectDepart},
data() {
return {
form: this.$form.createForm(this),
bumen:"",
bumens:""
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
# JSelectMultiUser 用户多选组件
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="用户选择v-decorator" style="width: 500px">
<j-select-multi-user v-decorator="['users']"/>
{{ getFormFieldValue('users') }}
</a-form-item>
<a-form-item label="用户选择v-model" style="width: 500px">
<j-select-multi-user v-model="users" ></j-select-multi-user>
{{ users }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectMultiUser from '@/components/jeecgbiz/JSelectMultiUser'
export default {
components: {JSelectMultiUser},
data() {
return {
form: this.$form.createForm(this),
users:"",
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
# JSelectUserByDep 根据部门选择用户
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| modalWidth |Number | | 弹框宽度 默认1250 |
| disabled |Boolean | | 是否禁用 |
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="用户选择v-decorator" style="width: 500px">
<j-select-user-by-dep v-decorator="['users']"/>
{{ getFormFieldValue('users') }}
</a-form-item>
<a-form-item label="用户选择v-model" style="width: 500px">
<j-select-user-by-dep v-model="users" ></j-select-user-by-dep>
{{ users }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectUserByDep from '@/components/jeecgbiz/JSelectUserByDep'
export default {
components: {JSelectUserByDep},
data() {
return {
form: this.$form.createForm(this),
users:"",
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```

248
src/components/jeecgbiz/modal/JSelectBatchModal.vue

@ -0,0 +1,248 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="top:5%;height: 100%;overflow-y: hidden"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="padding: 10px; margin: -10px">
<a-col :md="24" :sm="24">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="onSearch">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="批号">
<a-input placeholder="请输入批号" v-model="queryParam.name"></a-input>
</a-form-item>
</a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="onSearch">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset(1)">重置</a-button>
</a-col>
</span>
</a-row>
</a-form>
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
:customRow="rowAction">
</a-table>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import {getBatchNumberList} from '@/api/api'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
export default {
name: 'JSelectBatchModal',
mixins:[JeecgListMixin],
components: {},
props: ['modalWidth', 'rows', 'multi', 'barCode'],
data() {
return {
queryParam: {
name: "",
depotId: '',
barCode: ''
},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
categoryTree:[],
columns: [
{dataIndex: 'batchNumber', title: '批号', width: 100, align: 'left'},
{dataIndex: 'barCode', title: '物料编码', width: 100},
{dataIndex: 'name', title: '名称', width: 100},
{dataIndex: 'standard', title: '规格', width: 80},
{dataIndex: 'model', title: '型号', width: 80},
{dataIndex: 'expirationDateStr', title: '有效期至', width: 80},
{dataIndex: 'totalNum', title: '库存', width: 80}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectRows: [],
selectIds: [],
title: '选择批号',
isorter: {
column: 'createTime',
order: 'desc'
},
departTree: [],
depotList: [],
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
barCode: {
immediate: true,
handler() {
this.initBarCode()
}
},
},
created() {
this.loadData()
},
methods: {
initBarCode() {
if (this.barCode) {
this.$emit('initComp', this.barCode)
} else {
// JSelectUserByDepbug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if(this.rows) {
if(JSON.parse(this.rows).depotId && JSON.parse(this.rows).barCode ){
this.queryParam.depotId = JSON.parse(this.rows).depotId-0
this.queryParam.barCode = JSON.parse(this.rows).barCode
}
}
if (arg === 1) {
this.ipagination.current = 1;
}
this.loading = true
let params = this.getQueryParams()//
await getBatchNumberList(params).then((res) => {
if (res && res.code === 200) {
this.dataSource = res.data.rows
this.ipagination.total = res.data.total
}
}).finally(() => {
this.loading = false
})
},
showModal() {
this.visible = true;
this.loadData();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
return param;
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
if(this.rows) {
this.queryParam.name=''
if(JSON.parse(this.rows).depotId && JSON.parse(this.rows).barCode ){
this.queryParam.depotId = JSON.parse(this.rows).depotId-0
this.queryParam.barCode = JSON.parse(this.rows).barCode
}
}
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectIds = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleSubmit() {
let that = this;
this.getSelectRows();
that.$emit('ok', that.selectRows, that.selectIds);
that.searchReset(0)
that.close();
},
//
getSelectRows(rowId) {
let dataSource = this.dataSource;
let ids = "";
this.selectRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectRows.push(dataSource[i]);
ids = ids + "," + dataSource[i].batchNumber
}
}
this.selectIds = ids.substring(1);
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
modalFormOk() {
this.loadData();
},
rowAction(record, index) {
return {
on: {
click: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
},
dblclick: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
this.handleSubmit()
}
}
}
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>

241
src/components/jeecgbiz/modal/JSelectDepartModal.vue

@ -0,0 +1,241 @@
<template>
<a-modal
title="选择部门"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel"
cancelText="关闭">
<a-spin tip="Loading..." :spinning="false">
<a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" />
<a-tree
checkable
:treeData="treeData"
:checkStrictly="true"
@check="onCheck"
@select="onSelect"
@expand="onExpand"
:autoExpandParent="autoExpandParent"
:expandedKeys="expandedKeys"
:checkedKeys="checkedKeys">
<template slot="title" slot-scope="{title}">
<span v-if="title.indexOf(searchValue) > -1">
{{title.substr(0, title.indexOf(searchValue))}}
<span style="color: #f50">{{searchValue}}</span>
{{title.substr(title.indexOf(searchValue) + searchValue.length)}}
</span>
<span v-else>{{title}}</span>
</template>
</a-tree>
</a-spin>
</a-modal>
</template>
<script>
import { queryDepartTreeList } from '@/api/api'
export default {
name: 'JSelectDepartModal',
props:['modalWidth','multi','rootOpened','departId'],
data(){
return {
visible:false,
confirmLoading:false,
treeData:[],
autoExpandParent:true,
expandedKeys:[],
dataList:[],
checkedKeys:[],
checkedRows:[],
searchValue:""
}
},
created(){
this.loadDepart();
},
watch:{
departId(){
this.initDepartComponent()
},
visible: {
handler() {
if (this.departId) {
this.checkedKeys = this.departId.split(",");
// console.log('this.departId', this.departId)
} else {
this.checkedKeys = [];
}
}
}
},
methods:{
show(){
this.visible=true
this.checkedRows=[]
this.checkedKeys=[]
},
loadDepart(){
queryDepartTreeList().then(res=>{
if(res.success){
let arr = [...res.result]
this.reWriterWithSlot(arr)
this.treeData = arr
this.initDepartComponent()
if(this.rootOpened){
this.initExpandedKeys(res.result)
}
}
})
},
initDepartComponent(){
let names = ''
if(this.departId){
let currDepartId = this.departId
for(let item of this.dataList){
if(currDepartId.indexOf(item.key)>=0){
names+=","+item.title
}
}
if(names){
names = names.substring(1)
}
}
this.$emit("initComp",names)
},
reWriterWithSlot(arr){
for(let item of arr){
if(item.children && item.children.length>0){
this.reWriterWithSlot(item.children)
let temp = Object.assign({},item)
temp.children = {}
this.dataList.push(temp)
}else{
this.dataList.push(item)
item.scopedSlots={ title: 'title' }
}
}
},
initExpandedKeys(arr){
if(arr && arr.length>0){
let keys = []
for(let item of arr){
if(item.children && item.children.length>0){
keys.push(item.id)
}
}
this.expandedKeys=[...keys]
}else{
this.expandedKeys=[]
}
},
onCheck (checkedKeys,info) {
if(!this.multi){
let arr = checkedKeys.checked.filter(item => this.checkedKeys.indexOf(item) < 0)
this.checkedKeys = [...arr]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
}else{
this.checkedKeys = checkedKeys.checked
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onSelect(selectedKeys,info) {
let keys = []
keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
this.checkedKeys = [...keys]
this.checkedRows=[info.node.dataRef]
}else{
let currKey = info.node.dataRef.key
if(this.checkedKeys.indexOf(currKey)>=0){
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
}else{
this.checkedKeys.push(...keys)
}
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
handleSubmit(){
if(!this.checkedKeys || this.checkedKeys.length==0){
this.$emit("ok",'')
}else{
this.$emit("ok",this.checkedRows,this.checkedKeys.join(","))
}
this.handleClear()
},
handleCancel(){
this.handleClear()
},
handleClear(){
this.visible=false
this.checkedKeys=[]
},
getParentKey(currKey,treeData){
let parentKey
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i]
if (node.children) {
if (node.children.some(item => item.key === currKey)) {
parentKey = node.key
} else if (this.getParentKey(currKey, node.children)) {
parentKey = this.getParentKey(currKey, node.children)
}
}
}
return parentKey
},
onSearch(value){
const expandedKeys = this.dataList.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key,this.treeData)
}
return null
}).filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true,
})
},
// checkedKeys rows
getCheckedRows(checkedKeys) {
const forChildren = (list, key) => {
for (let item of list) {
if (item.id === key) {
return item
}
if (item.children instanceof Array) {
let value = forChildren(item.children, key)
if (value != null) {
return value
}
}
}
return null
}
let rows = []
for (let key of checkedKeys) {
let row = forChildren(this.treeData, key)
if (row != null) {
rows.push(row)
}
}
return rows
}
}
}
</script>
<style scoped>
</style>

370
src/components/jeecgbiz/modal/JSelectMaterialModal.vue

@ -0,0 +1,370 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="top:5%;height: 100%;overflow-y: hidden"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="padding: 10px; margin: -10px">
<a-col :md="24" :sm="24">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="onSearch">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="商品" :labelCol="{span: 5}" :wrapperCol="{span: 18, offset: 1}">
<a-input ref="material" placeholder="物料编码、名称、规格、型号" v-model="queryParam.q"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="类别">
<a-tree-select style="width:100%" :dropdownStyle="{maxHeight:'200px',overflow:'auto'}" allow-clear
:treeData="categoryTree" v-model="queryParam.categoryId" placeholder="请选择类别">
</a-tree-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="仓库">
<a-select placeholder="选择仓库" v-model="queryParam.depotId" @change="onDepotChange"
:dropdownMatchSelectWidth="false" showSearch optionFilterProp="children" allow-clear>
<a-select-option v-for="(item,index) in depotList" :key="index" :value="item.id">
{{ item.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="loadData(1)">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset(1)">重置</a-button>
</a-col>
</span>
</a-row>
</a-form>
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
:customRow="rowAction"
@change="handleTableChange">
<template slot="customRenderEnableSerialNumber" slot-scope="enableSerialNumber">
<a-tag v-if="enableSerialNumber==1" color="green"></a-tag>
<a-tag v-if="enableSerialNumber==0" color="orange"></a-tag>
</template>
<template slot="customRenderEnableBatchNumber" slot-scope="enableBatchNumber">
<a-tag v-if="enableBatchNumber==1" color="green"></a-tag>
<a-tag v-if="enableBatchNumber==0" color="orange"></a-tag>
</template>
</a-table>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { httpAction, getAction } from '@/api/manage'
import {filterObj, getMpListShort} from '@/utils/util'
import {getMaterialBySelect, queryMaterialCategoryTreeList} from '@/api/api'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import Vue from 'vue'
export default {
name: 'JSelectMaterialModal',
mixins:[JeecgListMixin],
components: {},
props: ['modalWidth', 'rows', 'multi', 'barCode'],
data() {
return {
queryParam: {
q: '',
depotId: ''
},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
categoryTree:[],
columns: [
{dataIndex: 'mBarCode', title: '物料编码', width: 100, align: 'left'},
{dataIndex: 'name', title: '名称', width: 120, ellipsis:true},
{dataIndex: 'categoryName', title: '类别', width: 80},
{dataIndex: 'standard', title: '规格', width: 80},
{dataIndex: 'model', title: '型号', width: 80},
{dataIndex: 'color', title: '颜色', width: 80},
{dataIndex: 'unit', title: '单位', width: 70, ellipsis:true},
{dataIndex: 'sku', title: '多属性', width: 80},
{dataIndex: 'stock', title: '库存', width: 60},
{dataIndex: 'expand', title: '扩展信息', width: 80, ellipsis:true},
{dataIndex: 'enableSerialNumber', title: '序列号', width: 60, align: "center",
scopedSlots: { customRender: 'customRenderEnableSerialNumber' }
},
{dataIndex: 'enableBatchNumber', title: '批号', width: 50, align: "center",
scopedSlots: { customRender: 'customRenderEnableBatchNumber' }
}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectMaterialRows: [],
selectMaterialIds: [],
title: '选择商品',
ipagination: {
current: 1,
pageSize: 2,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
departTree: [],
depotList: [],
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
barCode: {
immediate: true,
handler() {
this.initBarCode()
}
},
},
created() {
//
this.resetScreenSize()
// this.loadTreeData()
// this.getDepotList()
},
methods: {
initBarCode() {
if (this.barCode) {
this.$emit('initComp', this.barCode)
} else {
// JSelectUserByDepbug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
this.loading = true
let params = this.getQueryParams()//
if(this.visible) {
await getMaterialBySelect(params).then((res) => {
if (res) {
this.dataSource = res.rows
this.ipagination.total = res.total
if(res.total ===1) {
this.title = '选择商品【再次回车可以直接选中】'
this.$nextTick(() =>{
this.$refs.material.focus()
});
} else {
this.title = '选择商品'
}
}
}).finally(() => {
this.loading = false
})
}
},
loadTreeData(){
let that = this;
let params = {};
params.id='';
queryMaterialCategoryTreeList(params).then((res)=>{
if(res){
that.categoryTree = [];
for (let i = 0; i < res.length; i++) {
let temp = res[i];
that.categoryTree.push(temp);
}
}
})
},
//
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = {x: 800};
} else {
this.scrollTrigger = {};
}
},
showModal(barCode) {
this.visible = true;
this.title = '选择商品'
this.queryParam.q = barCode
this.$nextTick(() => this.$refs.material.focus());
this.initDepotSelect()
this.loadData();
this.loadTreeData()
this.getDepotList()
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.mpList = getMpListShort(Vue.ls.get('materialPropertyList')) //
param.page = this.ipagination.current;
param.rows = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectMaterialIds = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
this.getSelectMaterialRows();
that.$emit('ok', that.selectMaterialRows, that.selectMaterialIds);
that.searchReset(0)
that.close();
},
//
getSelectMaterialRows(rowId) {
let dataSource = this.dataSource;
let materialIds = "";
this.selectMaterialRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectMaterialRows.push(dataSource[i]);
materialIds = materialIds + "," + dataSource[i].mBarCode
}
}
this.selectMaterialIds = materialIds.substring(1);
},
getDepotList() {
let that = this;
getAction('/depot/findDepotByCurrentUser').then((res) => {
if(res.code === 200){
that.depotList = res.data
}
})
},
initDepotSelect() {
if(this.rows) {
if(JSON.parse(this.rows).depotId){
this.queryParam.depotId = JSON.parse(this.rows).depotId-0
}
}
},
onDepotChange(value) {
this.queryParam.depotId = value
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
if(this.dataSource && this.dataSource.length===1) {
if(this.queryParam.q === this.dataSource[0].mBarCode||
this.queryParam.q === this.dataSource[0].name||
this.queryParam.q === this.dataSource[0].standard||
this.queryParam.q === this.dataSource[0].model) {
let arr = []
arr.push(this.dataSource[0].id)
this.selectedRowKeys = arr
this.handleSubmit()
} else {
this.loadData(1)
}
} else {
this.loadData(1)
}
},
modalFormOk() {
this.loadData()
},
rowAction(record, index) {
return {
on: {
click: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
},
dblclick: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
this.handleSubmit()
}
}
}
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>

262
src/components/jeecgbiz/modal/JSelectSerialMaterialModal.vue

@ -0,0 +1,262 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="margin-top: -70px"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="padding: 10px; margin: -10px">
<a-col :md="24" :sm="24">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="onSearch">
<a-row :gutter="24">
<a-col :md="12" :sm="12">
<a-form-item label="商品信息" :labelCol="{span: 5}" :wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入条码、名称、规格、型号" v-model="queryParam.q"></a-input>
</a-form-item>
</a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="12" :sm="12">
<a-button type="primary" @click="onSearch">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset(1)">重置</a-button>
</a-col>
</span>
</a-row>
</a-form>
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
:customRow="rowAction"
@change="handleTableChange">
</a-table>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import {filterObj} from '@/utils/util'
import {getSerialMaterialBySelect} from '@/api/api'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
export default {
name: 'JSelectSerialMaterialModal',
mixins:[JeecgListMixin],
components: {},
props: ['modalWidth', 'multi', 'barCode'],
data() {
return {
queryParam: {
q: ''
},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
columns: [
{dataIndex: 'mBarCode', title: '物料编码', width: 100, align: 'left'},
{dataIndex: 'name', title: '名称', width: 100},
{dataIndex: 'standard', title: '规格', width: 80},
{dataIndex: 'model', title: '型号', width: 80}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectMaterialRows: [],
selectMaterialIds: [],
title: '选择序列号商品',
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
barCode: {
immediate: true,
handler() {
this.initBarCode()
}
},
},
created() {
//
this.resetScreenSize()
this.loadData()
},
methods: {
initBarCode() {
if (this.barCode) {
this.$emit('initComp', this.barCode)
} else {
// JSelectUserByDepbug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
this.loading = true
let params = this.getQueryParams()//
await getSerialMaterialBySelect(params).then((res) => {
if (res) {
this.dataSource = res.rows
this.ipagination.total = res.total
}
}).finally(() => {
this.loading = false
})
},
//
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = {x: 800};
} else {
this.scrollTrigger = {};
}
},
showModal() {
this.visible = true;
this.loadData();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.page = this.ipagination.current;
param.rows = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectMaterialIds = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
this.getSelectMaterialRows();
that.$emit('ok', that.selectMaterialRows, that.selectMaterialIds);
that.searchReset(0)
that.close();
},
//
getSelectMaterialRows(rowId) {
let dataSource = this.dataSource;
let materialIds = "";
this.selectMaterialRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectMaterialRows.push(dataSource[i]);
materialIds = materialIds + "," + dataSource[i].mBarCode
}
}
this.selectMaterialIds = materialIds.substring(1);
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
modalFormOk() {
this.loadData();
},
rowAction(record, index) {
return {
on: {
click: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
},
dblclick: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
this.handleSubmit()
}
}
}
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>

265
src/components/jeecgbiz/modal/JSelectSnModal.vue

@ -0,0 +1,265 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="top:5%;height: 100%;overflow-y: hidden"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="padding: 10px; margin: -10px">
<a-col :md="24" :sm="24">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="onSearch">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="序列号">
<a-input placeholder="请输入序列号" v-model="queryParam.name"></a-input>
</a-form-item>
</a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="onSearch">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset(1)">重置</a-button>
</a-col>
</span>
</a-row>
</a-form>
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
:customRow="rowAction"
@change="handleTableChange">
</a-table>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import {getEnableSerialNumberList} from '@/api/api'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
export default {
name: 'JSelectSnModal',
mixins:[JeecgListMixin],
components: {},
props: ['modalWidth', 'rows', 'multi', 'barCode'],
data() {
return {
queryParam: {
name: "",
depotId: '',
barCode: ''
},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
categoryTree:[],
columns: [
{dataIndex: 'serialNumber', title: '序列号', width: 100, align: 'left'}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectRows: [],
selectIds: [],
title: '选择序列号',
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30', '100', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
departTree: [],
depotList: [],
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
barCode: {
immediate: true,
handler() {
this.initBarCode()
}
},
},
created() {
this.loadData()
},
methods: {
initBarCode() {
if (this.barCode) {
this.$emit('initComp', this.barCode)
} else {
// JSelectUserByDepbug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if(this.rows) {
if(JSON.parse(this.rows).depotId && JSON.parse(this.rows).barCode ){
this.queryParam.depotId = JSON.parse(this.rows).depotId-0
this.queryParam.barCode = JSON.parse(this.rows).barCode
}
}
if (arg === 1) {
this.ipagination.current = 1;
}
this.loading = true
let params = this.getQueryParams()//
await getEnableSerialNumberList(params).then((res) => {
if (res && res.code === 200) {
this.dataSource = res.data.rows
this.ipagination.total = res.data.total
}
}).finally(() => {
this.loading = false
})
},
showModal() {
this.visible = true;
this.loadData();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.page = this.ipagination.current;
param.rows = this.ipagination.pageSize;
return param;
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
if(this.rows) {
this.queryParam.name=''
if(JSON.parse(this.rows).depotId && JSON.parse(this.rows).barCode ){
this.queryParam.depotId = JSON.parse(this.rows).depotId-0
this.queryParam.barCode = JSON.parse(this.rows).barCode
}
}
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectIds = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
this.getSelectRows();
that.$emit('ok', that.selectRows, that.selectIds);
that.searchReset(0)
that.close();
},
//
getSelectRows(rowId) {
let dataSource = this.dataSource;
let ids = "";
this.selectRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectRows.push(dataSource[i]);
ids = ids + "," + dataSource[i].serialNumber
}
}
this.selectIds = ids.substring(1);
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
modalFormOk() {
this.loadData();
},
rowAction(record, index) {
return {
on: {
click: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
},
dblclick: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
this.handleSubmit()
}
}
}
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>

329
src/components/jeecgbiz/modal/JSelectUserByDepModal.vue

@ -0,0 +1,329 @@
<template>
<a-modal
:width="modalWidth"
:visible="visible"
:title="title"
@ok="handleSubmit"
@cancel="close"
cancelText="关闭"
style="margin-top: -70px"
wrapClassName="ant-modal-cust-warp"
>
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
<a-col :md="6" :sm="24">
<a-card :bordered="false">
<!--组织机构-->
<a-directory-tree
selectable
:selectedKeys="selectedDepIds"
:checkStrictly="true"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
:expandAction="false"
:expandedKeys.sync="expandedKeys"
@select="onDepSelect"
/>
</a-card>
</a-col>
<a-col :md="18" :sm="24">
<a-card :bordered="false">
用户账号:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入账号"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
<a-button @click="searchReset(1)" style="margin-left: 20px" icon="redo">重置</a-button>
<!--用户列表-->
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
@change="handleTableChange">
</a-table>
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import {filterObj} from '@/utils/util'
import {queryDepartTreeList, getUserList, queryUserByDepId} from '@/api/api'
export default {
name: 'JSelectUserByDepModal',
components: {},
props: ['modalWidth', 'multi', 'userIds'],
data() {
return {
queryParam: {
username: "",
},
columns: [
{
title: '用户账号',
align: 'center',
dataIndex: 'username'
},
{
title: '用户姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
customRender: function (text) {
if (text === 1) {
return '男'
} else if (text === 2) {
return '女'
} else {
return text
}
}
},
{
title: '手机',
align: 'center',
dataIndex: 'phone'
},
{
title: '部门',
align: 'center',
dataIndex: 'orgCode'
}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectUserRows: [],
selectUserIds: [],
title: '根据部门选择用户',
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
selectedDepIds: [],
departTree: [],
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
userIds: {
immediate: true,
handler() {
this.initUserNames()
}
},
},
created() {
//
this.resetScreenSize();
this.loadData()
},
methods: {
initUserNames() {
if (this.userIds) {
// , 使 in
let values = this.userIds.split(',') + ','
getUserList({
username: values,
pageNo: 1,
pageSize: values.length
}).then((res) => {
if (res.success) {
let selectedRowKeys = []
let realNames = []
res.result.records.forEach(user => {
realNames.push(user['realname'])
selectedRowKeys.push(user['id'])
})
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', realNames.join(','))
}
})
} else {
// JSelectUserByDepbug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
if (this.selectedDepIds && this.selectedDepIds.length > 0) {
await this.initQueryUserByDepId(this.selectedDepIds)
} else {
this.loading = true
let params = this.getQueryParams()//
await getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records
this.ipagination.total = res.result.total
}
}).finally(() => {
this.loading = false
})
}
},
//
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = {x: 800};
} else {
this.scrollTrigger = {};
}
},
showModal() {
this.visible = true;
this.queryDepartTree();
this.initUserNames()
this.loadData();
this.form.resetFields();
},
getQueryParams() {
let param = Object.assign({}, this.queryParam, this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
let str = 'id,';
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex;
}
return str;
},
searchReset(num) {
let that = this;
if (num !== 0) {
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectUserIds = [];
that.selectedDepIds = [];
},
close() {
this.searchReset(0);
this.visible = false;
},
handleTableChange(pagination, filters, sorter) {
//TODO
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc';
}
this.ipagination = pagination;
this.loadData();
},
handleSubmit() {
let that = this;
this.getSelectUserRows();
that.$emit('ok', that.selectUserRows, that.selectUserIds);
that.searchReset(0)
that.close();
},
//
getSelectUserRows(rowId) {
let dataSource = this.dataSource;
let userIds = "";
this.selectUserRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectUserRows.push(dataSource[i]);
userIds = userIds + "," + dataSource[i].username
}
}
this.selectUserIds = userIds.substring(1);
},
// ,
onDepSelect(selectedDepIds) {
if (selectedDepIds[0] != null) {
this.initQueryUserByDepId(selectedDepIds); // id
if (this.selectedDepIds[0] !== selectedDepIds[0]) {
this.selectedDepIds = [selectedDepIds[0]];
}
}
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onSearch() {
this.loadData(1);
},
// id
initQueryUserByDepId(selectedDepIds) {
this.loading = true
return queryUserByDepId({id: selectedDepIds.toString()}).then((res) => {
if (res.success) {
this.dataSource = res.result;
this.ipagination.total = res.result.length;
}
}).finally(() => {
this.loading = false
})
},
queryDepartTree() {
queryDepartTreeList().then((res) => {
if (res.success) {
this.departTree = res.result;
//
this.expandedKeys = this.departTree.map(item => item.id)
}
})
},
modalFormOk() {
this.loadData();
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>

122
src/components/jeecgbiz/modal/SelectUserListModal.vue

@ -0,0 +1,122 @@
<template>
<a-modal
title="用户列表"
:width="1000"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel">
<a-table
ref="table"
bordered
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"></a-table>
</a-modal>
</template>
<script>
import {getUserList} from '@/api/api'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
export default {
name: "SelectUserListModal",
mixins: [JeecgListMixin],
data() {
return {
title: "操作",
visible: false,
model: {},
confirmLoading: false,
url: {
add: "/act/model/create",
list: "/sys/user/list"
},
columns: [
{
title: '用户账号',
align: "center",
dataIndex: 'username',
fixed: 'left',
width: 200
},
{
title: '用户姓名',
align: "center",
dataIndex: 'realname',
},
{
title: '性别',
align: "center",
dataIndex: 'sex_dictText'
},
{
title: '手机号码',
align: "center",
dataIndex: 'phone'
},
{
title: '邮箱',
align: "center",
dataIndex: 'email'
},
{
title: '状态',
align: "center",
dataIndex: 'status_dictText'
}
]
}
},
created() {
//Step.2
getUserList().then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.ipagination.total = res.result.total;
}
})
},
methods: {
open() {
this.visible = true;
//Step.1
this.selectedRowKeys = []
this.selectedRows = []
},
close() {
this.$emit('close');
this.visible = false;
},
handleChange(info) {
let file = info.file;
if (file.response.success) {
this.$message.success(file.response.message);
this.$emit('ok');
this.close()
} else {
this.$message.warn(file.response.message);
this.close()
}
},
handleCancel() {
this.close()
},
handleSubmit() {
this.$emit('ok', this.selectionRows);
this.close()
},
}
}
</script>
<style>
</style>

36
src/components/tools/DynamicNotice.vue

@ -0,0 +1,36 @@
<template>
<component
:is="comp"
:formData="formData"
ref="compModel"
v-if="comp">
</component>
</template>
<script>
export default {
name: 'DynamicNotice',
data () {
return {
compName: this.path
}
},
computed: {
comp: function () {
if(!this.path){
return null;
}
return () => import(`@/views/${this.path}.vue`)
}
},
props: ['path','formData'],
methods: {
detail () {
setTimeout(() => {
if(this.path){
this.$refs.compModel.view(this.formData);
}
}, 200)
},
}
}
</script>

139
src/components/tools/ShowAnnouncement.vue

@ -0,0 +1,139 @@
<template>
<j-modal
:title="title"
:width="modelStyle.width"
:visible="visible"
:bodyStyle ="bodyStyle"
:switchFullscreen="switchFullscreen"
@cancel="handleCancel"
>
<template slot="footer">
<a-button key="back" @click="handleCancel">关闭</a-button>
<a-button v-if="record.openType==='url'" type="primary" @click="toHandle">去处理</a-button>
</template>
<a-card class="daily-article" :loading="loading">
<a-card-meta
:title="record.titile"
:description="'标题:'+record.msgTitle">
</a-card-meta>
<a-divider />
<span v-html="record.msgContent" class="article-content"></span>
</a-card>
</j-modal>
</template>
<script>
export default {
name: "SysAnnouncementModal",
components: {
},
data () {
return {
title:"通知消息",
record: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
visible: false,
switchFullscreen: true,
loading: false,
bodyStyle:{
padding: "0",
height:(window.innerHeight*0.6)+"px",
"overflow-y":"auto",
},
modelStyle:{
width: '60%',
style: { top: '20px' },
fullScreen: false
}
}
},
created () {
},
methods: {
detail (record) {
this.visible = true;
this.record = record;
},
handleCancel () {
this.visible = false;
},
/** 切换全屏显示 */
handleClickToggleFullScreen() {
let mode = !this.modelStyle.fullScreen
if (mode) {
this.modelStyle.width = '100%'
this.modelStyle.style.top = '20px'
} else {
this.modelStyle.width = '60%'
this.modelStyle.style.top = '50px'
}
this.modelStyle.fullScreen = mode
},
toHandle(){
if(this.record.openType==='url'){
this.visible = false;
//
this.$router.push({path: this.record.openPage})
}
},
}
}
</script>
<style lang="less">
.announcementCustomModal{
.ant-modal-header {
border: none;
display: inline-block;
position: absolute;
z-index: 1;
right: 56px;
padding: 0;
.ant-modal-title{
.custom-btn{
width: 56px;
height: 56px;
border: none;
box-shadow: none;
}
}
}
.daily-article{
border-bottom: 0;
}
}
</style>
<style scoped lang="less">
.daily-article {
.article-button {
font-size: 1.2rem !important;
}
.ant-card-body {
padding: 18px !important;
}
.ant-card-head {
padding: 0 1rem;
}
.ant-card-meta {
margin-bottom: 1rem;
}
.article-content {
p {
word-wrap: break-word;
word-break: break-all;
text-overflow: initial;
white-space: normal;
font-size: .9rem !important;
margin-bottom: .8rem;
}
}
}
</style>

15
src/main.js

@ -4,7 +4,7 @@ import Cookies from 'js-cookie'
import Element from 'element-ui' import Element from 'element-ui'
import './assets/styles/element-variables.scss' import './assets/styles/element-variables.scss'
import Storage from 'vue-ls'
import '@/assets/styles/index.scss' // global css import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css import '@/assets/styles/ruoyi.scss' // ruoyi css
import App from './App' import App from './App'
@ -16,6 +16,11 @@ import plugins from './plugins' // plugins
// 权限指令 // 权限指令
import permission from './components/Permission' import permission from './components/Permission'
import Antd from 'ant-design-vue'
import JeecgComponents from '@/components/jeecg/index'
// import Viser from 'viser-vue'
import 'ant-design-vue/dist/antd.less'; // or 'ant-design-vue/dist/antd.less'
import './assets/icons' // icon import './assets/icons' // icon
import './permission' // permission control import './permission' // permission control
// import './tongji' // 百度统计 // import './tongji' // 百度统计
@ -43,6 +48,12 @@ Vue.prototype.getDictDataLabel = getDictDataLabel
Vue.prototype.DICT_TYPE = DICT_TYPE Vue.prototype.DICT_TYPE = DICT_TYPE
Vue.prototype.handleTree = handleTree Vue.prototype.handleTree = handleTree
Vue.use(Storage, {
namespace: 'pro__', // key prefix
name: 'ls', // name variable Vue.[ls] or this.[$ls],
storage: 'local', // storage name session, local, memory
})
// 全局组件挂载 // 全局组件挂载
Vue.component('DictTag', DictTag) Vue.component('DictTag', DictTag)
Vue.component('DocAlert', DocAlert) Vue.component('DocAlert', DocAlert)
@ -54,10 +65,12 @@ import DocAlert from '@/components/DocAlert'
// 头部标签插件 // 头部标签插件
import VueMeta from 'vue-meta' import VueMeta from 'vue-meta'
Vue.use(Antd)
Vue.use(directive) Vue.use(directive)
Vue.use(plugins) Vue.use(plugins)
Vue.use(VueMeta) Vue.use(VueMeta)
Vue.use(permission) Vue.use(permission)
Vue.use(JeecgComponents);
// Vue.use(hljs.vuePlugin); // Vue.use(hljs.vuePlugin);
// bpmnProcessDesigner 需要引入 // bpmnProcessDesigner 需要引入

173
src/mixins/JEditableTableMixin.js

@ -0,0 +1,173 @@
import JEditableTable from '@/components/jeecg/JEditableTable'
import { VALIDATE_NO_PASSED, getRefPromise, validateFormAndTables } from '@/utils/JEditableTableUtil'
import { httpAction, getAction } from '@/api/manage'
export const JEditableTableMixin = {
components: {
JEditableTable
},
data() {
return {
title: '操作',
visible: false,
form: this.$form.createForm(this),
confirmLoading: false,
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 }
}
}
},
methods: {
/** 获取所有的editableTable实例 */
getAllTable() {
if (!(this.refKeys instanceof Array)) {
throw this.throwNotArray('refKeys')
}
let values = this.refKeys.map(key => getRefPromise(this, key))
return Promise.all(values)
},
/** 遍历所有的JEditableTable实例 */
eachAllTable(callback) {
// 开始遍历
this.getAllTable().then(tables => {
tables.forEach((item, index) => {
if (typeof callback === 'function') {
callback(item, index)
}
})
})
},
/** 当点击新增按钮时调用此方法 */
add() {
// addBefore 没有地方用到
if (typeof this.addBefore === 'function') this.addBefore()
// 默认新增空数据
let rowNum = this.addDefaultRowNum
if (typeof rowNum !== 'number') {
rowNum = 1
console.warn('由于你没有在 data 中定义 addDefaultRowNum 或 addDefaultRowNum 不是数字,所以默认添加一条空数据,如果不想默认添加空数据,请将定义 addDefaultRowNum 为 0')
}
this.eachAllTable((item) => {
item.add(rowNum)
})
if (typeof this.addAfter === 'function') this.addAfter(this.model)
this.edit({})
},
/** 当点击了编辑(修改)按钮时调用此方法 */
edit(record) {
if (typeof this.editBefore === 'function') this.editBefore(record)
this.visible = true
this.activeKey = this.refKeys[0]
this.form.resetFields()
this.model = Object.assign({}, record)
if (typeof this.editAfter === 'function') this.editAfter(this.model)
},
/** 关闭弹窗,并将所有JEditableTable实例回归到初始状态 */
close() {
this.visible = false
this.eachAllTable((item) => {
item.initialize()
})
this.$emit('close')
},
/** 查询某个tab的数据 */
requestSubTableData(url, params, tab, success) {
tab.loading = true
getAction(url, params).then(res => {
if(res && res.code === 200){
tab.dataSource = res.data.rows
typeof success === 'function' ? success(res) : ''
}
}).finally(() => {
tab.loading = false
})
},
/** 发起请求,自动判断是执行新增还是修改操作 */
request(formData) {
let url = this.url.add, method = 'post'
if (this.model.id) {
url = this.url.edit
method = 'put'
}
this.confirmLoading = true
httpAction(url, formData, method).then((res) => {
if(res.code === 200){
this.$emit('ok')
this.confirmLoading = false
this.close()
} else {
this.$message.warning(res.msg);
this.confirmLoading = false
}
}).finally(() => {
})
},
/* --- handle 事件 --- */
/** ATab 选项卡切换事件 */
handleChangeTabs(key) {
// 自动重置scrollTop状态,防止出现白屏
getRefPromise(this, key).then(editableTable => {
editableTable.resetScrollTop()
})
},
/** 关闭按钮点击事件 */
handleCancel() {
this.close()
},
/** 确定按钮点击事件 */
handleOk() {
/** 触发表单验证 */
this.getAllTable().then(tables => {
let inputValues = tables[0].inputValues
let ids = []
inputValues.forEach((item) => {
if(!item.barCode && !item.operNumber) {
ids.push(item.id)
}
})
tables[0].removeRows(ids)
/** 一次性验证主表和所有的次表 */
return validateFormAndTables(this.form, tables)
}).then(allValues => {
if (typeof this.classifyIntoFormData !== 'function') {
throw this.throwNotFunction('classifyIntoFormData')
}
let formData = this.classifyIntoFormData(allValues)
// 发起请求
return this.request(formData)
}).catch(e => {
if (e.error === VALIDATE_NO_PASSED) {
// 如果有未通过表单验证的子表,就自动跳转到它所在的tab
this.activeKey = e.index == null ? this.activeKey : this.refKeys[e.index]
} else {
console.error(e)
}
})
},
/* --- throw --- */
/** not a function */
throwNotFunction(name) {
return `${name} 未定义或不是一个函数`
},
/** not a array */
throwNotArray(name) {
return `${name} 未定义或不是一个数组`
}
}
}

438
src/mixins/JeecgListMixin.js

@ -0,0 +1,438 @@
/**
* 新增修改完成调用 modalFormOk方法 编辑弹框组件ref定义为modalForm
* 高级查询按钮调用 superQuery方法 高级查询组件ref定义为superQueryModal
* data中url定义 list为查询列表 delete为删除单条记录 deleteBatch为批量删除
*/
import { filterObj } from '@/utils/util';
import { deleteAction, getAction, postAction, downFile, getFileAccessHttpUrl } from '@/api/manage'
import Vue from 'vue'
import {mixinDevice} from '@/utils/mixin.js'
export const JeecgListMixin = {
mixins: [mixinDevice],
data(){
return {
//token header
tokenHeader: {'X-Access-Token': ""},
/*卡片样式 */
cardStyle: '',
/* 查询条件-请不要在queryParam中声明非字符串值的属性 */
queryParam: {},
/* 数据源 */
dataSource:[],
/* 分页参数 */
ipagination:{
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30', '50', '100'],
showTotal: (total, range) => {
return range[0] + "-" + range[1] + " 共" + total + "条"
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
/* 控制table高度 */
scroll: {
x:1300
},
/* 排序参数 */
isorter:{
column: 'createTime',
order: 'desc',
},
/* 筛选参数 */
filters: {},
/* table加载状态 */
loading:false,
/* table选中keys*/
selectedRowKeys: [],
/* table选中records*/
selectionRows: [],
/* 查询折叠 */
toggleSearchStatus:false,
/* 高级查询条件生效状态 */
superQueryFlag:false,
/* 高级查询条件 */
superQueryParams: '',
/** 高级查询拼接方式 */
superQueryMatchType: 'and',
/** 是否加载时就执行 */
disableMixinCreated: false,
/* 按钮权限 */
btnEnableList: ''
}
},
created() {
if(this.isDesktop()) {
this.cardStyle = 'height:' + (document.documentElement.clientHeight-125) + 'px'
}
if(!this.disableMixinCreated){
//console.log(' -- mixin created -- ')
this.loadData();
//初始化字典配置 在自己页面定义
this.initDictConfig();
//初始化按钮权限
this.initActiveBtnStr();
}
},
mounted () {
this.initScroll()
},
methods:{
loadData(arg) {
if(!this.url.list){
this.$message.error("请设置url.list属性!")
return
}
//加载数据 若传入参数1则加载第一页的内容
if (arg === 1) {
this.ipagination.current = 1;
}
var params = this.getQueryParams();//查询条件
this.loading = true;
getAction(this.url.list, params).then((res) => {
if (res.code===200) {
this.dataSource = res.data.rows;
this.ipagination.total = Number(res.data.total);
this.tableAddTotalRow(this.columns, this.dataSource)
}
if(res.code===510){
this.$message.warning(res.data)
}
this.loading = false;
})
},
initDictConfig(){
//console.log("--这是一个假的方法!")
},
handleSuperQuery(params, matchType) {
//高级查询方法
if(!params){
this.superQueryParams=''
this.superQueryFlag = false
}else{
this.superQueryFlag = true
this.superQueryParams=JSON.stringify(params)
this.superQueryMatchType = matchType
}
this.loadData(1)
},
getQueryParams() {
//获取查询条件
let sqp = {}
if(this.superQueryParams){
sqp['superQueryParams']=encodeURI(this.superQueryParams)
sqp['superQueryMatchType'] = this.superQueryMatchType
}
let searchObj = {}
searchObj.search = JSON.stringify(this.queryParam);
var param = Object.assign(sqp, searchObj, this.isorter ,this.filters);
param.field = this.getQueryField();
param.currentPage = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField() {
var str = "id,";
this.columns.forEach(function (value) {
str += "," + value.dataIndex;
});
return str;
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
onClearSelected() {
this.selectedRowKeys = [];
this.selectionRows = [];
},
searchQuery() {
this.loadData(1);
},
superQuery() {
this.$refs.superQueryModal.show();
},
searchReset() {
this.queryParam = {}
this.loadData(1);
},
batchSetStatus: function (status) {
if(!this.url.batchSetStatusUrl){
this.$message.error("请设置url.batchSetStatusUrl属性!")
return
}
if (this.selectedRowKeys.length <= 0) {
this.$message.warning('请选择一条记录!');
return;
} else {
var ids = "";
for (var a = 0; a < this.selectedRowKeys.length; a++) {
ids += this.selectedRowKeys[a] + ",";
}
var that = this;
this.$confirm({
title: "确认操作",
content: "是否操作选中数据?",
onOk: function () {
that.loading = true;
postAction(that.url.batchSetStatusUrl, {status: status, ids: ids}).then((res) => {
if(res.code === 200){
that.loadData();
that.onClearSelected();
} else {
that.$message.warning(res.msg);
}
}).finally(() => {
that.loading = false;
});
}
});
}
},
batchDel: function () {
if(!this.url.deleteBatch){
this.$message.error("请设置url.deleteBatch属性!")
return
}
if (this.selectedRowKeys.length <= 0) {
this.$message.warning('请选择一条记录!');
return;
} else {
var ids = "";
for (var a = 0; a < this.selectedRowKeys.length; a++) {
ids += this.selectedRowKeys[a] + ",";
}
var that = this;
this.$confirm({
title: "确认删除",
content: "是否删除选中数据?",
onOk: function () {
that.loading = true;
deleteAction(that.url.deleteBatch, {ids: ids}).then((res) => {
if(res.code === 200){
that.loadData();
that.onClearSelected();
} else {
that.$message.warning(res.msg);
}
}).finally(() => {
that.loading = false;
});
}
});
}
},
handleDelete: function (id) {
if(!this.url.delete){
this.$message.error("请设置url.delete属性!")
return
}
var that = this;
deleteAction(that.url.delete, {id: id}).then((res) => {
if(res.code === 200){
that.loadData();
} else {
that.$message.warning(res.msg);
}
});
},
handleEdit: function (record) {
this.$refs.modalForm.edit(record);
this.$refs.modalForm.title = "编辑";
this.$refs.modalForm.disableSubmit = false;
},
handleAdd: function () {
this.$refs.modalForm.add();
this.$refs.modalForm.title = "新增";
this.$refs.modalForm.disableSubmit = false;
},
handleTableChange(pagination, filters, sorter) {
//分页、排序、筛选变化时触发
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field;
this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
}
if(pagination && pagination.current) {
this.ipagination = pagination;
}
this.loadData();
},
handleToggleSearch(){
this.toggleSearchStatus = !this.toggleSearchStatus;
},
// 给popup查询使用(查询区域不支持回填多个字段,限制只返回一个字段)
getPopupField(fields){
return fields.split(',')[0]
},
modalFormOk() {
// 新增/修改 成功时,重载列表
this.loadData();
},
handleDetail:function(record, type){
this.$refs.modalDetail.show(record, type);
this.$refs.modalDetail.title=type+"-详情";
},
/* 导出 */
handleExportXls2(){
let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
let url = `${window._CONFIG['domianURL']}/${this.url.exportXlsUrl}?paramsStr=${paramsStr}`;
window.location.href = url;
},
handleExportXls(fileName){
if(!fileName || typeof fileName != "string"){
fileName = "导出文件"
}
let param = {...this.queryParam};
if(this.selectedRowKeys && this.selectedRowKeys.length>0){
param['selections'] = this.selectedRowKeys.join(",")
}
console.log("导出参数",param)
downFile(this.url.exportXlsUrl,param).then((data)=>{
if (!data) {
this.$message.warning("文件下载失败")
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data],{type: 'application/vnd.ms-excel'}), fileName+'.xls')
}else{
let url = window.URL.createObjectURL(new Blob([data],{type: 'application/vnd.ms-excel'}))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName+'.xls')
document.body.appendChild(link)
link.click()
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
}
})
},
/* 导入 */
handleImportExcel(info){
console.log(info,'info')
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
if (info.file.response) {
// this.$message.success(`${info.file.name} 文件上传成功`);
if (info.file.response.code === 200) {
this.$message.success(info.file.response.data || `${info.file.name} 文件上传成功`)
} else {
this.$message.warning(info.file.response.data)
}
this.loadData()
} else {
this.$message.error(`${info.file.name} ${info.file.response.data}.`);
}
} else if (info.file.status === 'error') {
this.$message.error(`文件上传失败: ${info.file.msg} `);
}
},
/* 图片预览 */
getImgView(text){
if(text && text.indexOf(",")>0){
text = text.substring(0,text.indexOf(","))
}
return getFileAccessHttpUrl(text)
},
/* 文件下载 */
uploadFile(text){
if(!text){
this.$message.warning("未知的文件")
return;
}
if(text.indexOf(",")>0){
text = text.substring(0,text.indexOf(","))
}
let url = getFileAccessHttpUrl(text)
window.open(url);
},
/* 按钮权限 */
initActiveBtnStr() {
let funId = Vue.ls.get('funId'); //功能id
let btnStrList = Vue.ls.get('winBtnStrList'); //按钮功能列表 JSON字符串
this.btnEnableList = [1,2,3,4,5,,6,7]; //按钮列表
if (funId && btnStrList) {
for (let i = 0; i < btnStrList.length; i++) {
if (btnStrList[i].funId == funId) {
if (btnStrList[i].btnStr) {
this.btnEnableList = btnStrList[i].btnStr;
}
}
}
}
},
/* 初始化表格横向或纵向滚动 */
initScroll() {
if (this.isMobile()) {
this.scroll.y = ''
} else {
let basicLength = 274
let searchWrapperDomLen=0, operatorDomLen=0
//搜索区域
let searchWrapperDom = document.getElementsByClassName('table-page-search-wrapper')
//操作按钮区域
let operatorDom = document.getElementsByClassName('table-operator')
if(searchWrapperDom && searchWrapperDom[0]) {
searchWrapperDomLen = searchWrapperDom[0].offsetHeight
}
if(operatorDom && operatorDom[0]) {
operatorDomLen = operatorDom[0].offsetHeight+10
}
this.scroll.y = document.documentElement.clientHeight-searchWrapperDomLen-operatorDomLen-basicLength
}
},
/** 表格增加合计行 */
tableAddTotalRow(columns, dataSource) {
if(dataSource.length>0 && this.ipagination.pageSize%10===1) {
//分页条数为11、21、31等的时候增加合计行
let numKey = 'rowIndex'
let totalRow = { [numKey]: '合计' }
//需要合计的列
let parseCols = 'initialStock,currentStock,currentStockPrice,initialAmount,thisMonthAmount,currentAmount,inSum,inSumPrice,inOutSumPrice,' +
'outSum,outSumPrice,outInSumPrice,operNumber,allPrice,numSum,priceSum,prevSum,thisSum,thisAllPrice,billMoney,changeAmount,' +
'allPrice,currentNumber,lowSafeStock,highSafeStock,lowCritical,highCritical,initialPrice,intoPrice,intoStock,outPrice,outStock'
columns.forEach(column => {
let { key, dataIndex } = column
if (![key, dataIndex].includes(numKey)) {
let total = 0
dataSource.forEach(data => {
if(parseCols.indexOf(dataIndex)>-1) {
if(data[dataIndex]) {
total += Number.parseFloat(data[dataIndex])
} else {
total += 0
}
} else {
total = '-'
}
})
if (total !== '-') {
total = total.toFixed(2)
}
totalRow[dataIndex] = total
}
})
dataSource.push(totalRow)
//总数要增加合计的行数,每页都有一行合计,所以总数要加上
let size = Math.ceil(this.ipagination.total/(this.ipagination.pageSize-1))
this.ipagination.total = this.ipagination.total + size
}
},
paginationChange(page, pageSize) {
this.ipagination.current = page
this.ipagination.pageSize = pageSize
this.loadData(this.ipagination.current);
},
paginationShowSizeChange(current, size) {
this.ipagination.current = current
this.ipagination.pageSize = size
this.loadData(this.ipagination.current);
}
}
}

82
src/mixins/tableDragResizeMixin.js

@ -0,0 +1,82 @@
//mixins/tableDragResize.js
import Vue from 'vue'
import VueDraggableResizable from 'vue-draggable-resizable'
Vue.component('vue-draggable-resizable', VueDraggableResizable)
/**
* @param { 表格columns } tbCols
*/
function initDrag(tbCols) {
const draggingMap = {}
tbCols.forEach((col) => {
const key = col.dataIndex || col.key//这儿要求表格数据中要有这两个属性
draggingMap[key] = col.width || 0
})
const draggingState = Vue.observable(draggingMap)
return (h, props, children) => {
let thDom = null
const { key, ...restProps } = props
let col
if (key === 'selection-column') {
//表格加了复选框,不加这个判断col会是undefided
col = {}
} else {
col = tbCols.find((item) => {
const k = item.dataIndex || item.key
return k === key
})
}
if (!col.width) {//这儿要求表格数据中要有宽width属性,若是没有是不会执行下面的拖拽的
return <th {...restProps}>{children}</th>
}
const onDrag = (x) => {
draggingState[key] = 0
col.width = Math.max(x, 1)
}
const onDragstop = () => {
draggingState[key] = thDom.getBoundingClientRect().width
}
return (
<th
{...restProps}
v-ant-ref={(r) => {
thDom = r
}}
width={draggingState[key]}
class="resize-table-th"
>
{children}
<vue-draggable-resizable
key={col.dataIndex || col.key}
class="table-draggable-handle"
w={10}
x={col.width || draggingState[key]}
z={1}
axis="x"
draggable={true}
resizable={false}
onDragging={onDrag}
onDragstop={onDragstop}
></vue-draggable-resizable>
</th>
)
}
}
export default {
methods: {
/**
* https://github.com/mauricius/vue-draggable-resizable
* 表格列可拖拽
* 表格上使用:components="drag(columns)"
* tips:columns中需包含dataIndex或者key和width(Number)
*/
drag(columns) {
return {
header: {
cell: initDrag(columns),
},
}
},
},
}

176
src/utils/JEditableTableUtil.js

@ -0,0 +1,176 @@
const FormTypes = {
normal: 'normal',
input: 'input',
inputNumber: 'inputNumber',
checkbox: 'checkbox',
select: 'select',
date: 'date',
datetime: 'datetime',
upload: 'upload',
file: 'file',
image: 'image',
popup:'popup',
popupJsh:'popupJsh',
list_multi:"list_multi",
sel_search:"sel_search",
radio:'radio',
checkbox_meta:"checkbox_meta",
input_pop:'input_pop',
slot: 'slot',
hidden: 'hidden'
}
const VALIDATE_NO_PASSED = Symbol()
export { FormTypes, VALIDATE_NO_PASSED }
/**
* 获取指定的 $refs 对象
* 有时候可能会遇到组件未挂载到页面中的情况导致无法获取 $refs 中的某个对象
* 这个方法可以等待挂载完成之后再返回 $refs 的对象避免报错
* @author sunjianlei
**/
export function getRefPromise(vm, name) {
return new Promise((resolve) => {
(function next() {
let ref = vm.$refs[name]
if (ref) {
resolve(ref)
} else {
setTimeout(() => {
next()
}, 10)
}
})()
})
}
/**
* 一次性验证主表单和所有的次表单
* @param form 主表单 form 对象
* @param cases 接收一个数组每项都是一个JEditableTable实例
* @returns {Promise<any>}
* @author sunjianlei
*/
export function validateFormAndTables(form, cases) {
if (!(form && typeof form.validateFields === 'function')) {
throw `form 参数需要的是一个form对象,而传入的却是${typeof form}`
}
let options = {}
return new Promise((resolve, reject) => {
// 验证主表表单
form.validateFields((err, values) => {
err ? reject({ error: VALIDATE_NO_PASSED }) : resolve(values)
})
}).then(values => {
Object.assign(options, { formValue: values })
// 验证所有子表的表单
return validateTables(cases)
}).then(all => {
Object.assign(options, { tablesValue: all })
return Promise.resolve(options)
}).catch(error => {
return Promise.reject(error)
})
}
/**
* 验证并获取一个或多个表格的所有值
* @param cases 接收一个数组每项都是一个JEditableTable实例
* @param deleteTempId 是否删除临时ID如果设为true行编辑就不返回新增行的IDID需要后台生成
* @author sunjianlei
*/
export function validateTables(cases, deleteTempId) {
if (!(cases instanceof Array)) {
throw `'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`
}
return new Promise((resolve, reject) => {
let tables = []
let index = 0;
if(!cases || cases.length==0){
resolve()
}
(function next() {
let vm = cases[index]
vm.getAll(true, deleteTempId).then(all => {
tables[index] = all
// 判断校验是否全部完成,完成返回成功,否则继续进行下一步校验
if (++index === cases.length) {
resolve(tables)
} else (
next()
)
}, error => {
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败并跳转到该表格
if (error === VALIDATE_NO_PASSED) {
reject({ error: VALIDATE_NO_PASSED, index })
}
reject(error)
})
})()
})
}
/**
* 一次性验证主表单和所有的次表单-只校验单号
* @param form 主表单 form 对象
* @param cases 接收一个数组每项都是一个JEditableTable实例
* @returns {Promise<any>}
* @author sunjianlei
*/
export function getListData(form, cases) {
let options = {}
return new Promise((resolve, reject) => {
// 验证主表表单
form.validateFields(['number'],(err, values) => {
err ? reject({ error: VALIDATE_NO_PASSED }) : resolve(values)
})
}).then(values => {
Object.assign(options, { formValue: values })
// 验证所有子表的表单
return getListTables(cases)
}).then(all => {
Object.assign(options, { tablesValue: all })
return Promise.resolve(options)
}).catch(error => {
return Promise.reject(error)
})
}
/**
* 不验证直接获取一个或多个表格的所有值
* @param cases 接收一个数组每项都是一个JEditableTable实例
* @param deleteTempId 是否删除临时ID如果设为true行编辑就不返回新增行的IDID需要后台生成
* @author sunjianlei
*/
export function getListTables(cases, deleteTempId) {
if (!(cases instanceof Array)) {
throw `'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`
}
return new Promise((resolve, reject) => {
let tables = []
let index = 0;
if(!cases || cases.length==0){
resolve()
}
(function next() {
let vm = cases[index]
vm.getAll(false, deleteTempId).then(all => {
tables[index] = all
// 判断校验是否全部完成,完成返回成功,否则继续进行下一步校验
if (++index === cases.length) {
resolve(tables)
} else (
next()
)
}, error => {
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败并跳转到该表格
if (error === VALIDATE_NO_PASSED) {
reject({ error: VALIDATE_NO_PASSED, index })
}
reject(error)
})
})()
})
}

40
src/utils/mixin.js

@ -0,0 +1,40 @@
// import Vue from 'vue'
import { mapState } from "vuex";
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
const mixin = {
computed: {
...mapState({
layoutMode: state => state.app.layout,
navTheme: state => state.app.theme,
primaryColor: state => state.app.color,
colorWeak: state => state.app.weak,
multipage: state => state.app.multipage,//多页签设置
fixedHeader: state => state.app.fixedHeader,
fixSiderbar: state => state.app.fixSiderbar,
contentWidth: state => state.app.contentWidth,
autoHideHeader: state => state.app.autoHideHeader,
sidebarOpened: state => state.app.sidebar.opened
})
}
}
const mixinDevice = {
computed: {
...mapState({
device: state => state.app.device,
})
},
methods: {
isMobile () {
return this.device === 'mobile'
},
isDesktop () {
return this.device === 'desktop'
}
}
}
export { mixin, mixinDevice }

115
src/utils/props-util.js

@ -0,0 +1,115 @@
/**
* 该文件截取自 "ant-design-vue/es/_util/props-util.js" 文件并对其做出特殊修改
*/
function classNames() {
let classes = []
for (let i = 0; i < arguments.length; i++) {
let arg = arguments[i]
if (!arg) continue
let argType = typeof arg
if (argType === 'string' || argType === 'number') {
classes.push(arg)
} else if (Array.isArray(arg) && arg.length) {
let inner = classNames.apply(null, arg)
if (inner) {
classes.push(inner)
}
} else if (argType === 'object') {
for (let key in arg) {
if (arg.hasOwnProperty(key) && arg[key]) {
classes.push(key)
}
}
}
}
return classes.join(' ')
}
const camelizeRE = /-(\w)/g
function camelize(str) {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
}
function objectCamelize(obj) {
let res = {}
Object.keys(obj).forEach(k => (res[camelize(k)] = obj[k]))
return res
}
function parseStyleText(cssText = '', camel) {
const res = {}
const listDelimiter = /;(?![^(]*\))/g
const propertyDelimiter = /:(.+)/
cssText.split(listDelimiter).forEach(function (item) {
if (item) {
const tmp = item.split(propertyDelimiter)
if (tmp.length > 1) {
const k = camel ? camelize(tmp[0].trim()) : tmp[0].trim()
res[k] = tmp[1].trim()
}
}
})
return res
}
export function getClass(ele) {
let data = {}
if (ele.data) {
data = ele.data
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
}
const tempCls = data.class || {}
const staticClass = data.staticClass
let cls = {}
staticClass &&
staticClass.split(' ').forEach(c => {
cls[c.trim()] = true
})
if (typeof tempCls === 'string') {
tempCls.split(' ').forEach(c => {
cls[c.trim()] = true
})
} else if (Array.isArray(tempCls)) {
classNames(tempCls)
.split(' ')
.forEach(c => {
cls[c.trim()] = true
})
} else {
cls = { ...cls, ...tempCls }
}
return cls
}
export function getStyle(ele, camel) {
getClass(ele)
let data = {}
if (ele.data) {
data = ele.data
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
}
// update-begin-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
let style = data.style || {}
let staticStyle = data.staticStyle
staticStyle = staticStyle ? objectCamelize(data.staticStyle) : {}
// update-end-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
if (typeof style === 'string') {
style = parseStyleText(style, camel)
} else if (camel && style) {
// 驼峰化
style = objectCamelize(style)
}
return { ...staticStyle, ...style }
}

669
src/utils/util.js

@ -0,0 +1,669 @@
import api from '@/api/api'
import isURL from '@/utils/validate'
import XLSX from 'xlsx'
export function timeFix() {
const time = new Date()
const hour = time.getHours()
return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
}
export function welcome() {
const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
let index = Math.floor((Math.random()*arr.length))
return arr[index]
}
/**
* 触发 window.resize
*/
export function triggerWindowResizeEvent() {
let event = document.createEvent('HTMLEvents')
event.initEvent('resize', true, true)
event.eventType = 'message'
window.dispatchEvent(event)
}
/**
* 过滤对象中为空的属性
* @param obj
* @returns {*}
*/
export function filterObj(obj) {
if (!(typeof obj == 'object')) {
return;
}
for ( let key in obj) {
if (obj.hasOwnProperty(key)
&& (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
delete obj[key];
}
}
return obj;
}
/**
* 时间格式化
* @param value
* @param fmt
* @returns {*}
*/
export function formatDate(value, fmt) {
let regPos = /^\d+(\.\d+)?$/;
if(regPos.test(value)){
//如果是数字
let getDate = new Date(value);
let o = {
'M+': getDate.getMonth() + 1,
'd+': getDate.getDate(),
'h+': getDate.getHours(),
'm+': getDate.getMinutes(),
's+': getDate.getSeconds(),
'q+': Math.floor((getDate.getMonth() + 3) / 3),
'S': getDate.getMilliseconds()
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
}
}
return fmt;
}else{
//TODO
value = value.trim();
return value.substr(0,fmt.length);
}
}
// 生成首页路由
// 生成嵌套路由(子路由)
function generateChildRouters (data) {
const routers = [];
for (let item of data) {
let componentPath = "";
item.route = "1";
if(item.component.indexOf("layouts")>=0){
componentPath = () => import('@/components'+item.component);
} else {
componentPath = () => import('@/views'+item.component);
}
// eslint-disable-next-line
let URL = (item.url|| '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)) // URL支持{{ window.xxx }}占位符变量
if (isURL(URL)) {
item.url = URL;
}
let componentName =''
if(item.component) {
let index = item.component.lastIndexOf("\/");
componentName = item.component.substring(index + 1, item.component.length);
}
let menu = {
path: item.url,
name: item.text,
component: componentPath,
meta: {
id: item.id,
title: item.text,
icon: item.icon,
url: item.url,
componentName:componentName,
internalOrExternal:true,
keepAlive: true
// permissionList:""
}
}
if (item.children && item.children.length > 0) {
menu.children = [...generateChildRouters( item.children)];
}
//--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
//判断是否生成路由
if(item.route && item.route === '0'){
//console.log(' 不生成路由 item.route: '+item.route);
//console.log(' 不生成路由 item.path: '+item.path);
}else{
routers.push(menu);
}
//--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
}
return routers
}
/**
* 深度克隆对象数组
* @param obj 被克隆的对象
* @return 克隆后的对象
*/
export function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj))
}
/**
* 随机生成数字
*
* 示例生成长度为 12 的随机数randomNumber(12)
* 示例生成 3~23 之间的随机数randomNumber(3, 23)
*
* @param1 最小值 | 长度
* @param2 最大值
* @return int 生成后的数字
*/
export function randomNumber() {
// 生成 最小值 到 最大值 区间的随机数
const random = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min)
}
if (arguments.length === 1) {
let [length] = arguments
// 生成指定长度的随机数字,首位一定不是 0
let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
return parseInt(nums.join(''))
} else if (arguments.length >= 2) {
let [min, max] = arguments
return random(min, max)
} else {
return Number.NaN
}
}
/**
* 随机生成字符串
* @param length 字符串的长度
* @param chats 可选字符串区间只会生成传入的字符串中的字符
* @return string 生成的字符串
*/
export function randomString(length, chats) {
if (!length) length = 1
if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
let str = ''
for (let i = 0; i < length; i++) {
let num = randomNumber(0, chats.length - 1)
str += chats[num]
}
return str
}
/**
* 随机生成uuid
* @return string 生成的uuid
*/
export function randomUUID() {
let chats = '0123456789abcdef'
return randomString(32, chats)
}
/**
* 下划线转驼峰
* @param string
* @returns {*}
*/
export function underLine2CamelCase(string){
return string.replace( /_([a-z])/g, function( all, letter ) {
return letter.toUpperCase();
});
}
/**
* 判断是否显示办理按钮
* @param bpmStatus
* @returns {*}
*/
export function showDealBtn(bpmStatus){
if(bpmStatus!="1"&&bpmStatus!="3"&&bpmStatus!="4"){
return true;
}
return false;
}
/**
* 增强CSS可以在页面上输出全局css
* @param css 要增强的css
* @param id style标签的id可以用来清除旧样式
*/
export function cssExpand(css, id) {
let style = document.createElement('style')
style.type = "text/css"
style.innerHTML = `@charset "UTF-8"; ${css}`
// 清除旧样式
if (id) {
let $style = document.getElementById(id)
if ($style != null) $style.outerHTML = ''
style.id = id
}
// 应用新样式
document.head.appendChild(style)
}
/** 用于js增强事件,运行JS代码,可以传参 */
// options 所需参数:
// 参数名 类型 说明
// vm VueComponent vue实例
// event Object event对象
// jsCode String 待执行的js代码
// errorMessage String 执行出错后的提示(控制台)
export function jsExpand(options = {}) {
// 绑定到window上的keyName
let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
if (typeof window[windowKeyName] != 'object') {
window[windowKeyName] = {}
}
// 随机生成JS增强的执行id,防止冲突
let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
// 封装按钮点击事件
let code = `
(function (o_${id}) {
try {
(function (globalEvent, vm) {
${options.jsCode}
})(o_${id}.event, o_${id}.vm)
} catch (e) {
o_${id}.error(e)
}
o_${id}.done()
})(window['${windowKeyName}']['EVENT_${id}'])
`
// 创建script标签
const script = document.createElement('script')
// 将需要传递的参数挂载到window对象上
window[windowKeyName]['EVENT_' + id] = {
vm: options.vm,
event: options.event,
// 当执行完成时,无论如何都会调用的回调事件
done() {
// 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
script.outerHTML = ''
delete window[windowKeyName]['EVENT_' + id]
},
// 当js运行出错的时候调用的事件
error(e) {
console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}${new Date()}`)
console.error(e)
console.groupEnd()
}
}
// 将事件挂载到document中
script.innerHTML = code
document.body.appendChild(script)
}
/**
* 重复值验证工具方法
*
* 使用示例
* { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
*
* @param tableName 被验证的表名
* @param fieldName 被验证的字段名
* @param fieldVal 被验证的值
* @param dataId 数据ID可空
* @param callback
*/
export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
if (fieldVal) {
let params = { tableName, fieldName, fieldVal, dataId }
api.duplicateCheck(params).then(res => {
res['success'] ? callback() : callback(res['message'])
}).catch(err => {
callback(err.message || err)
})
} else {
callback()
}
}
/**
* 根据编码校验规则code校验传入的值是否合法
*
* 使用示例
* { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
*
* @param ruleCode 编码校验规则 code
* @param value 被验证的值
* @param callback
*/
export function validateCheckRule(ruleCode, value, callback) {
if (ruleCode && value) {
value = encodeURIComponent(value)
api.checkRuleByCode({ ruleCode, value }).then(res => {
res['success'] ? callback() : callback(res['message'])
}).catch(err => {
callback(err.message || err)
})
} else {
callback()
}
}
/**
* 如果值不存在就 push 进数组反之不处理
* @param array 要操作的数据
* @param value 要添加的值
* @param key 可空如果比较的是对象可能存在地址不一样但值实际上是一样的情况可以传此字段判断对象中唯一的字段例如 id不传则直接比较实际值
* @returns {boolean} 成功 push 返回 true不处理返回 false
*/
export function pushIfNotExist(array, value, key) {
for (let item of array) {
if (key && (item[key] === value[key])) {
return false
} else if (item === value) {
return false
}
}
array.push(value)
return true
}
/**
* 可用于判断是否成功
* @type {symbol}
*/
export const succeedSymbol = Symbol()
/**
* 可用于判断是否失败
* @type {symbol}
*/
export const failedSymbol = Symbol()
/**
* 使 promise 无论如何都会 resolve除非传入的参数不是一个Promise对象或返回Promise对象的方法
* 一般用在 Promise.all
*
* @param promise 可传Promise对象或返回Promise对象的方法
* @returns {Promise<any>}
*/
export function alwaysResolve(promise) {
return new Promise((resolve, reject) => {
let p = promise
if (typeof promise === 'function') {
p = promise()
}
if (p instanceof Promise) {
p.then(data => {
resolve({ type: succeedSymbol, data })
}).catch(error => {
resolve({ type: failedSymbol, error })
})
} else {
reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
}
})
}
/**
* 简单实现防抖方法
*
* 防抖(debounce)函数在第一次触发给定的函数时不立即执行函数而是给出一个期限值(delay)比如100ms
* 如果100ms内再次执行函数就重新开始计时直到计时结束后再真正执行函数
* 这样做的好处是如果短时间内大量触发同一事件只会执行一次函数
*
* @param fn 要防抖的函数
* @param delay 防抖的毫秒数
* @returns {Function}
*/
export function simpleDebounce(fn, delay = 100) {
let timer = null
return function () {
let args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(null, args)
}, delay)
}
}
/**
* 不用正则的方式替换所有值
* @param text 被替换的字符串
* @param checker 替换前的内容
* @param replacer 替换后的内容
* @returns {String} 替换后的字符串
*/
export function replaceAll(text, checker, replacer) {
let lastText = text
text = text.replace(checker, replacer)
if (lastText !== text) {
return replaceAll(text, checker, replacer)
}
return text
}
/**
* 转换商品扩展字段的格式
* @param thisRows
* @param checker
* @param replacer
* @returns {string}
*/
export function getMpListShort(thisRows, checker, replacer) {
let mPropertyListShort = ''
let nativeNameStr = ''
for (let i = 0; i < thisRows.length; i++) {
if (thisRows[i].enabled) {
nativeNameStr += thisRows[i].nativeName + ",";
}
}
if (nativeNameStr) {
mPropertyListShort = nativeNameStr.substring(0, nativeNameStr.length - 1);
}
return mPropertyListShort
}
/**
* js获取当前月份 格式yyyy-MM
*/
export function getNowFormatMonth() {
var date = new Date();
var seperator1 = "-";
var month = date.getMonth() + 1;
if (month >= 1 && month <= 9) {
month = "0" + month;
}
var currentdate = date.getFullYear() + seperator1 + month;
return currentdate;
}
/**
* js获取当前日期 格式yyyy-MM-dd
*/
export function getFormatDate() {
var date = new Date();
var seperator1 = "-";
var year = date.getFullYear();
var month = date.getMonth() + 1;
var strDate = date.getDate();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate;
}
var currentdate = year + seperator1 + month + seperator1 + strDate;
return currentdate;
}
/**
* js获取当前时间 格式yyyy-MM-dd HH:MM:SS
*/
export function getNowFormatDateTime() {
var date = new Date();
var seperator1 = "-";
var seperator2 = ":";
var month = date.getMonth() + 1;
var strDate = date.getDate();
var strHours = date.getHours();
var strMinutes = date.getMinutes();
var strSeconds = date.getSeconds();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate;
}
if (strHours >= 0 && strHours <= 9) {
strHours = "0" + strHours;
}
if (strMinutes >= 0 && strMinutes <= 9) {
strMinutes = "0" + strMinutes;
}
if (strSeconds >= 0 && strSeconds <= 9) {
strSeconds = "0" + strSeconds;
}
var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
+ " " + strHours + seperator2 + strMinutes
+ seperator2 + strSeconds;
return currentdate;
}
/**
* js获取当前时间 格式MMddHHMMSS
*/
export function getNowFormatStr() {
var date = new Date();
var month = date.getMonth() + 1;
var strDate = date.getDate();
var strHours = date.getHours();
var strMinutes = date.getMinutes();
var strSeconds = date.getSeconds();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate;
}
if (strHours >= 0 && strHours <= 9) {
strHours = "0" + strHours;
}
if (strMinutes >= 0 && strMinutes <= 9) {
strMinutes = "0" + strMinutes;
}
if (strSeconds >= 0 && strSeconds <= 9) {
strSeconds = "0" + strSeconds;
}
var currentdate = month + strDate + strHours + strMinutes + strSeconds;
console.log(currentdate,'currentdate')
return currentdate;
}
/**
* JS中根据指定值删除数组中的元素
* @param arrylist
* @param val
*/
export function removeByVal(arrylist, val) {
for(var i = 0; i < arrylist .length; i++) {
if(arrylist [i] == val) {
arrylist .splice(i, 1);
break;
}
}
}
/**
* 将数组单个金额中的数值转为负数
* @param arr
* @returns {Array}
*/
export function changeListFmtMinus(arr) {
var newArr = new Array();
for(var i=0; i<arr.length; i++) {
if(arr[i] < 0){
newArr.push((arr[i]-0).toString());
}
else {
newArr.push((0 - arr[i]).toString());
}
}
return newArr;
}
/**
通用的打开下载对话框方法没有测试过具体兼容性
@param url 下载地址也可以是一个blob对象必选
@param saveName 保存文件名可选
*/
export function openDownloadDialog (url, saveName) {
if (typeof url === 'object' && url instanceof Blob) {
url = URL.createObjectURL(url) // 创建blob地址
}
let aLink = document.createElement('a')
aLink.href = url
saveName = saveName + '_' + getNowFormatStr() + '.xlsx'
aLink.download = saveName || '' // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
let event
if (window.MouseEvent) event = new MouseEvent('click')
else {
event = document.createEvent('MouseEvents')
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
}
aLink.dispatchEvent(event)
}
/**
* 将一个sheet转成最终的excel文件的blob对象然后利用URL.createObjectURL下载
* @param sheet
* @param sheetName
* @returns {Blob}
*/
export function sheet2blob (aoa, sheetName) {
let sheet = XLSX.utils.aoa_to_sheet(aoa)
sheetName = sheetName || 'sheet1'
let workbook = {
SheetNames: [sheetName],
Sheets: {}
}
workbook.Sheets[sheetName] = sheet
// 生成excel的配置项
let wopts = {
bookType: 'xlsx', // 要生成的文件类型
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
type: 'binary'
}
let wbout = XLSX.write(workbook, wopts)
let blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' })
// 字符串转ArrayBuffer
function s2ab (s) {
let buf = new ArrayBuffer(s.length)
let view = new Uint8Array(buf)
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF
return buf
}
return blob
}
/**
* 回车后自动跳到下一个input
*/
export function autoJumpNextInput(domInfo) {
let domIndex = 0
let inputs = document.getElementById(domInfo).getElementsByTagName('input')
inputs[domIndex].focus()
document.getElementById(domInfo).addEventListener('keydown',function(e){
if(e.keyCode === 13){
domIndex++
if(domIndex === inputs.length) {
domIndex = 0
}
inputs[domIndex].focus()
}
})
for(let i=0; i<inputs.length; i++){
//这个index就是做个介质,来获取当前的i是第几个
inputs[i].index = i;
inputs[i].onclick = function () {
domIndex = this.index
}
}
}

8
src/utils/validate.js

@ -81,3 +81,11 @@ export function isArray(arg) {
} }
return Array.isArray(arg) return Array.isArray(arg)
} }
/**
* URL地址
* @param {*} s
*/
export function isURL (s) {
return /^http[s]?:\/\/.*/.test(s)
}

2
src/views/bpm/processInstance/index.vue

@ -58,7 +58,7 @@
</el-table-column> </el-table-column>
<el-table-column label="当前审批任务" align="center" prop="tasks"> <el-table-column label="当前审批任务" align="center" prop="tasks">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-for="task in scope.row.tasks" type="text" @click="handleFormDetail(task.id)"> <el-button v-for="task in scope.row.tasks" :key="task" type="text" @click="handleFormDetail(task.id)">
<span>{{ task.name }}</span> <span>{{ task.name }}</span>
</el-button> </el-button>
</template> </template>

216
src/views/erp/bill/AllocationOutList.vue

@ -0,0 +1,216 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于两个仓库之间的商品调拨,调拨单会影响库存。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '调拨出库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<allocation-out-modal ref="modalForm" @ok="modalFormOk"></allocation-out-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by jishenghua-->
<script>
import AllocationOutModal from './modules/AllocationOutModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "AllocationOutList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
AllocationOutModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "出库",
subType: "调拨",
roleType: Vue.ls.get('roleType'),
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '单据编号', dataIndex: 'number',width:160, align: 'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align: 'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align: 'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align: 'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align: 'center',},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created() {
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

216
src/views/erp/bill/AssembleList.vue

@ -0,0 +1,216 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于将多种商品合并成一个商品,被合并的商品库存减少,合并后的商品库存增加。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '组装单')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<assemble-modal ref="modalForm" @ok="modalFormOk"></assemble-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by ji sheng hua-->
<script>
import AssembleModal from './modules/AssembleModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "AssembleList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
AssembleModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "其它",
subType: "组装单",
roleType: Vue.ls.get('roleType'),
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created() {
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

216
src/views/erp/bill/DisassembleList.vue

@ -0,0 +1,216 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于将一个商品拆分成多种商品,被拆分的商品库存增加,拆分后的商品库存减少。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '拆卸单')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<disassemble-modal ref="modalForm" @ok="modalFormOk"></disassemble-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by jisheng hua-->
<script>
import DisassembleModal from './modules/DisassembleModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "DisassembleList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
DisassembleModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "其它",
subType: "拆卸单",
roleType: Vue.ls.get('roleType'),
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '单据编号', dataIndex: 'number',width:160,
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145},
{ title: '操作员', dataIndex: 'userName',width:80, ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created() {
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

224
src/views/erp/bill/InventoryReview.vue

@ -0,0 +1,224 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="可以进行库存初始化,生产管理模块的成品入库。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '其它入库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<inventory-review-modal ref="modalForm" @ok="modalFormOk"></inventory-review-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by ji shenghua-->
<script>
import InventoryReviewModal from './modules/InventoryReviewModal.vue'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "OtherInList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
InventoryReviewModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "其它",
subType: "盘点复盘",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initSupplier()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

234
src/views/erp/bill/OtherInList.vue

@ -0,0 +1,234 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="供应商" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择供应商" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in supList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="可以进行库存初始化,生产管理模块的成品入库。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '其它入库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<other-in-modal ref="modalForm" @ok="modalFormOk"></other-in-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by ji shenghua-->
<script>
import OtherInModal from './modules/OtherInModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "OtherInList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
OtherInModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "入库",
subType: "其它",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '供应商', dataIndex: 'organName',width:120, align:'center', ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initSupplier()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

234
src/views/erp/bill/OtherOutList.vue

@ -0,0 +1,234 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="客户" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择客户" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in cusList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="可以进行库存初始化,生产管理模块的领料出库。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '其它出库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<other-out-modal ref="modalForm" @ok="modalFormOk"></other-out-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!--power by j i s h e n g h u a-->
<script>
import OtherOutModal from './modules/OtherOutModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "OtherOutList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
OtherOutModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "出库",
subType: "其它",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '客户', dataIndex: 'organName',width:120, align:'center', ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '状态', dataIndex: 'status', width: 80, align:'center', align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created() {
this.initCustomer()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

246
src/views/erp/bill/PurchaseBackList.vue

@ -0,0 +1,246 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="供应商" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择供应商" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in supList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于采购入库单据的退货。采购退货单可以由采购出库单转过来,也可以单独创建。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '采购退货出库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<purchase-back-modal ref="modalForm" @ok="modalFormOk"></purchase-back-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!-- by jisheng hua-->
<script>
import PurchaseBackModal from './modules/PurchaseBackModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "PurchaseBackList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
PurchaseBackModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "出库",
subType: "采购退货",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '供应商', dataIndex: 'organName',width:120, ellipsis:true,align:'center',},
{ title: '单据编号', dataIndex: 'number',width:160,align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220,align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145,align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80,align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80,align:'center',},
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80,align:'center',
customRender:function (text,record,index) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
}
},
{ title: '待退金额', dataIndex: 'needBackMoney',width:80,align:'center',
customRender:function (text,record,index) {
let needBackMoney = record.discountLastMoney + record.otherMoney
return needBackMoney? needBackMoney.toFixed(2):''
}
},
{ title: '退款', dataIndex: 'changeAmount',width:50,align:'center',},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 160,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initSupplier()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

255
src/views/erp/bill/PurchaseInList.vue

@ -0,0 +1,255 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="供应商" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择供应商" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in supList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联订单" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联订单" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="采购入库单可以由采购订单转过来也可以单独创建
采购入库单据中的仓库列表只显示当前用户有权限的仓库采购入库单可以使用多账户付款
勾选单据之后可以进行批量操作删除审核反审核" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '采购入库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<purchase-in-modal ref="modalForm" @ok="modalFormOk"></purchase-in-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!-- by ji sheng hua-->
<script>
import PurchaseInModal from './modules/PurchaseInModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "PurchaseInList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
PurchaseInModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "入库",
subType: "采购",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '供应商', dataIndex: 'organName',width:120, ellipsis:true, align:'center',},
{ title: '单据编号', dataIndex: 'number',width:160,align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[订]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, ellipsis:true,align:'center',
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145,align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, ellipsis:true,align:'center',},
{ title: '金额合计', dataIndex: 'totalPrice',width:80,align:'center',},
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80,align:'center',
customRender:function (text,record,index) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
}
},
{ title: '待付金额', dataIndex: 'needInMoney',width:80,align:'center',
customRender:function (text,record,index) {
let needInMoney = record.discountLastMoney + record.otherMoney
return needInMoney? needInMoney.toFixed(2):''
}
},
{ title: '付款', dataIndex: 'changeAmount',width:60,align:'center',},
{ title: '欠款', dataIndex: 'debt',width:60,align:'center',
customRender:function (text,record,index) {
let debt = record.discountLastMoney + record.otherMoney - record.changeAmount
return debt? debt.toFixed(2):''
}
},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",align:'center',
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 180,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initSupplier()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

230
src/views/erp/bill/PurchaseOrderList.vue

@ -0,0 +1,230 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="供应商" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择供应商" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in supList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="采购订单不涉及付款金额采购订单可以转采购入库单但需要先对采购订单进行审核
勾选单据之后可以进行批量操作删除审核反审核" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '采购订单')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
<a-tag v-if="status == '2'" color="cyan">完成采购</a-tag>
<a-tag v-if="status == '3'" color="blue">部分采购</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<purchase-order-modal ref="modalForm" @ok="modalFormOk"></purchase-order-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!-- by ji sheng hua-->
<script>
import PurchaseOrderModal from './modules/PurchaseOrderModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "PurchaseOrderList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
PurchaseOrderModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "其它",
subType: "采购订单",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '供应商', dataIndex: 'organName',width:120, align:'center', ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145,align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, ellipsis:true,align:'center',},
{ title: '金额合计', dataIndex: 'totalPrice',width:80,align:'center',},
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80,align:'center',
customRender:function (text,record,index) {
if(record.discountLastMoney) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
} else {
return record.totalPrice;
}
}
},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",align:'center',
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
created() {
this.initSupplier()
this.initUser()
},
computed: {
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

244
src/views/erp/bill/RetailBackList.vue

@ -0,0 +1,244 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="会员卡号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择会员卡号" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in retailList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于零售出库单据的退货。零售退货单可以由零售出库单转过来,也可以单独创建。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '零售退货入库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<retail-back-modal ref="modalForm" @ok="modalFormOk"></retail-back-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!-- by jisheng h u a-->
<script>
import RetailBackModal from './modules/RetailBackModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "RetailBackList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
RetailBackModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "入库",
subType: "零售退货",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '会员', dataIndex: 'organName',width:120, ellipsis:true, align:'center'},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}, align:'center'
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center'},
{ title: '操作员', dataIndex: 'userName',width:80, ellipsis:true, align:'center'},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center'},
{ title: '付款金额', dataIndex: 'getAmount',width:80, align:'center',
customRender:function (text,record,index) {
if(record.backAmount) {
return record.changeAmount + record.backAmount
} else {
return record.changeAmount
}
}
},
{ title: '找零', dataIndex: 'backAmount',width:50, align:'center'},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initRetail()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

238
src/views/erp/bill/RetailOutList.vue

@ -0,0 +1,238 @@
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="会员卡号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择会员卡号" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in retailList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于非会员和会员的单据录入,主要是用于散户使用,不能欠款。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '零售出库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<retail-out-modal ref="modalForm" @ok="modalFormOk"></retail-out-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<!-- create ji sheng hua-->
<script>
import RetailOutModal from './modules/RetailOutModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "RetailOutList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
RetailOutModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "出库",
subType: "零售",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '会员', dataIndex: 'organName',width:120, ellipsis:true, align:'center'},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, ellipsis:true, align:'center',
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center'},
{ title: '操作员', dataIndex: 'userName',width:80, ellipsis:true, align:'center'},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center'},
{ title: '收款金额', dataIndex: 'getAmount',width:80, align:'center',
customRender:function (text,record,index) {
if(record.backAmount) {
return record.changeAmount + record.backAmount
} else {
return record.changeAmount
}
}
},
{ title: '找零', dataIndex: 'backAmount',width:50, align:'center'},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }, align:'center'
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initRetail()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

246
src/views/erp/bill/SaleBackList.vue

@ -0,0 +1,246 @@
<!-- create ji sheng hua-->
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入条码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="客户" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择客户" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in cusList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联单据" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联单据" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="用于销售出库单据的退货。销售退货单可以由销售出库单转过来,也可以单独创建。" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '销售退货入库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<sale-back-modal ref="modalForm" @ok="modalFormOk"></sale-back-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<script>
import SaleBackModal from './modules/SaleBackModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "SaleBackList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
SaleBackModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "入库",
subType: "销售退货",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '客户', dataIndex: 'organName',width:120, align:"center", ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:"center",
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:"center", ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:"center", },
{ title: '操作员', dataIndex: 'userName',width:80, align:"center", ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:"center", },
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80, align:"center",
customRender:function (text,record,index) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
}
},
{ title: '待退金额', dataIndex: 'needBackMoney',width:80, align:"center",
customRender:function (text,record,index) {
let needBackMoney = record.discountLastMoney + record.otherMoney
return needBackMoney? needBackMoney.toFixed(2):''
}
},
{ title: '退款', dataIndex: 'changeAmount',width:50, align:"center", },
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 160,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created () {
this.initCustomer()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

245
src/views/erp/bill/SaleOrderList.vue

@ -0,0 +1,245 @@
<!-- create jishenghua-->
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="客户" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择客户" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in cusList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="销售订单不涉及收款金额销售订单可以转销售出库单但需要先对销售订单进行审核
勾选单据之后可以进行批量操作删除审核反审核" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '销售订单')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
<a-tag v-if="status == '2'" color="cyan">完成销售</a-tag>
<a-tag v-if="status == '3'" color="blue">部分销售</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<sale-order-modal ref="modalForm" @ok="modalFormOk"></sale-order-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<script>
import SaleOrderModal from './modules/SaleOrderModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "SaleOrderList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
SaleOrderModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "其它",
subType: "销售订单",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '客户', dataIndex: 'organName',width:120, align:'center', ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[转]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80, align:'center',
customRender:function (text,record,index) {
if(record.discountLastMoney) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
} else {
return record.totalPrice;
}
}
},
{ title: '状态', dataIndex: 'status', width: 70, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 150,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
created() {
this.initCustomer()
this.initUser()
},
computed: {
},
methods: {
myHandleEdit(record) {
if(record.status === '0') {
this.$refs.modalForm.action = "edit";
this.handleEdit(record);
} else {
this.$message.warning("抱歉,只有未审核的单据才能编辑!")
}
},
myHandleDelete(record) {
if(record.status === '0') {
this.handleDelete(record.id)
} else {
this.$message.warning("抱歉,只有未审核的单据才能删除!")
}
}
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

254
src/views/erp/bill/SaleOutList.vue

@ -0,0 +1,254 @@
<!-- create j i s h e n g h u a -->
<template>
<a-row :gutter="24">
<a-col :md="24">
<a-card :style="cardStyle" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<!-- 搜索区域 -->
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="24">
<a-form-item label="单据编号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入单据编号" v-model="queryParam.number"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="商品信息" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入物料编码、名称、规格、型号" v-model="queryParam.materialParam"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="单据日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
style="width:100%"
v-model="queryParam.createTimeRange"
format="YYYY-MM-DD"
:placeholder="['开始时间', '结束时间']"
@change="onDateChange"
@ok="onDateOk"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="24">
<a-form-item label="客户" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择客户" showSearch optionFilterProp="children" v-model="queryParam.organId">
<a-select-option v-for="(item,index) in cusList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="仓库名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="请选择仓库" showSearch optionFilterProp="children" v-model="queryParam.depotId">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="操作员" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select placeholder="选择操作员" showSearch optionFilterProp="children" v-model="queryParam.creator">
<a-select-option v-for="(item,index) in userList" :key="index" :value="item.id">
{{ item.userName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item label="关联订单" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="请输入关联订单" v-model="queryParam.linkNumber"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button style="margin-left: 8px" @click="searchReset">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</a-col>
</span>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator" style="margin-top: 5px">
<a-button v-if="btnEnableList.indexOf(1)>-1" @click="myHandleAdd" type="primary" icon="plus">新增</a-button>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" v-if="btnEnableList.indexOf(1)>-1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
<a-menu-item key="2" v-if="btnEnableList.indexOf(2)>-1" @click="batchSetStatus(1)"><a-icon type="check"/>审核</a-menu-item>
<a-menu-item key="3" v-if="btnEnableList.indexOf(7)>-1" @click="batchSetStatus(0)"><a-icon type="stop"/>反审核</a-menu-item>
</a-menu>
<a-button>
批量操作 <a-icon type="down" />
</a-button>
</a-dropdown>
<a-tooltip placement="left" title="销售出库单可以由销售订单转过来也可以单独创建
销售出库单据中的仓库列表只显示当前用户有权限的仓库销售出库单可以使用多账户收款
勾选单据之后可以进行批量操作删除审核反审核" slot="action">
<a-icon v-if="btnEnableList.indexOf(1)>-1" type="question-circle" style="font-size:20px;float:right;" />
</a-tooltip>
</div>
<!-- table区域-begin -->
<a-row>
<a-col :md="2.5" :sm="24">
<list-columns-setter v-model="columns" :def-columns="columns" style="float: right;"/>
</a-col>
</a-row>
<div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:components="drag(columns)"
:dataSource="dataSource"
:pagination="ipagination"
:scroll="scroll"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="myHandleDetail(record, '销售出库')">查看</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleEdit(record)">编辑</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a v-if="btnEnableList.indexOf(1)>-1" @click="myHandleCopyAdd(record)">复制</a>
<a-divider v-if="btnEnableList.indexOf(1)>-1" type="vertical" />
<a-popconfirm v-if="btnEnableList.indexOf(1)>-1" title="确定删除吗?" @confirm="() => myHandleDelete(record)">
<a>删除</a>
</a-popconfirm>
</span>
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status == '0'" color="red">未审核</a-tag>
<a-tag v-if="status == '1'" color="green">已审核</a-tag>
</template>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<sale-out-modal ref="modalForm" @ok="modalFormOk"></sale-out-modal>
<bill-detail ref="modalDetail"></bill-detail>
</a-card>
</a-col>
</a-row>
</template>
<script>
import SaleOutModal from './modules/SaleOutModal'
import BillDetail from './dialog/BillDetail'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { BillListMixin } from './mixins/BillListMixin'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
import ListColumnsSetter from '@/components/ListColumnsSetter'
import tableDragResizeMixin from '@/mixins/tableDragResizeMixin'
export default {
name: "SaleOutList",
mixins:[JeecgListMixin,BillListMixin,tableDragResizeMixin],
components: {
SaleOutModal,
BillDetail,
JDate,
ListColumnsSetter
},
data () {
return {
//
queryParam: {
number: "",
materialParam: "",
type: "出库",
subType: "销售",
roleType: Vue.ls.get('roleType'),
organId: "",
depotId: "",
creator: "",
linkNumber: ""
},
labelCol: {
span: 5
},
wrapperCol: {
span: 18,
offset: 1
},
//
columns: [
{ title: '客户', dataIndex: 'organName',width:120, align:'center', ellipsis:true},
{ title: '单据编号', dataIndex: 'number',width:160, align:'center',
customRender:function (text,record,index) {
if(record.linkNumber) {
return text + "[订]";
} else {
return text;
}
}
},
{ title: '商品信息', dataIndex: 'materialsList',width:220, align:'center', ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145, align:'center',},
{ title: '操作员', dataIndex: 'userName',width:80, align:'center', ellipsis:true},
{ title: '金额合计', dataIndex: 'totalPrice',width:80, align:'center',},
{ title: '含税合计', dataIndex: 'totalTaxLastMoney',width:80, align:'center',
customRender:function (text,record,index) {
return (record.discountMoney + record.discountLastMoney).toFixed(2);
}
},
{ title: '待收金额', dataIndex: 'needOutMoney',width:80, align:'center',
customRender:function (text,record,index) {
let needOutMoney = record.discountLastMoney + record.otherMoney
return needOutMoney? needOutMoney.toFixed(2):''
}
},
{ title: '收款', dataIndex: 'changeAmount',width:60, align:'center',},
{ title: '欠款', dataIndex: 'debt',width:60, align:'center',
customRender:function (text,record,index) {
let debt = record.discountLastMoney + record.otherMoney - record.changeAmount
return debt? debt.toFixed(2):''
}
},
{ title: '状态', dataIndex: 'status', width: 80, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
},
{
title: '操作',
dataIndex: 'action',
align:"center", width: 180,
scopedSlots: { customRender: 'action' },
}
],
url: {
list: "/erp/depotHead/list",
delete: "/erp/depotHead/delete",
deleteBatch: "/erp/depotHead/deleteBatch",
batchSetStatusUrl: "/erp/depotHead/batchSetStatus"
}
}
},
computed: {
},
created() {
this.initCustomer()
this.getDepotData()
this.initUser()
},
methods: {
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
</style>

112
src/views/erp/bill/dialog/BatchSetDepot.vue

@ -0,0 +1,112 @@
<template>
<a-modal
:title="title"
:width="500"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭"
wrapClassName="ant-modal-cust-warp"
style="top:30%;height: 35%;overflow-y: hidden">
<template slot="footer">
<a-button key="back" v-if="isReadOnly" @click="handleCancel">
关闭
</a-button>
</template>
<a-spin :spinning="confirmLoading">
<a-form :form="form" id="batchSetDepot">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="仓库名称">
<a-select placeholder="请选择仓库" v-decorator="[ 'depotId', validatorRules.depotId ]" showSearch optionFilterProp="children">
<a-select-option v-for="(depot,index) in depotList" :key="index" :value="depot.id">
{{ depot.depotName }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
import { getAction } from '@/api/manage'
export default {
name: "BatchSetDepot",
data () {
return {
title:"操作",
visible: false,
model: {},
depotList: [],
isReadOnly: false,
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules:{
depotId:{
rules: [
{ required: true, message: '请选择仓库!' }
]
}
},
}
},
created () {
},
methods: {
getDepotData() {
getAction('/depot/findDepotByCurrentUser').then((res)=>{
if(res.code === 200){
this.depotList = res.data;
}else{
this.$message.info(res.data);
}
})
},
add () {
this.edit({});
this.getDepotData()
},
edit (record) {
this.form.resetFields();
this.model = Object.assign({}, record);
this.visible = true;
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'depotId'))
});
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
const that = this;
//
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true;
let formData = Object.assign(this.model, values);
let depotId = formData.depotId
that.$emit('ok', depotId);
that.confirmLoading = false;
that.close();
}
})
},
handleCancel () {
this.close()
}
}
}
</script>
<style scoped>
</style>

1376
src/views/erp/bill/dialog/BillDetail.vue

File diff suppressed because it is too large Load Diff

71
src/views/erp/bill/dialog/BillPrintIframe.vue

@ -0,0 +1,71 @@
<template>
<a-modal
:title="title"
:width="width"
:visible="visible"
@cancel="handleCancel"
cancelText="关闭"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<template slot="footer">
<a-button key="back" @click="handleCancel">取消</a-button>
</template>
<a-form :form="form">
<template>
<iframe :src="billPrintUrl" width="100%" :height="height" frameborder="0" scrolling="no"></iframe>
</template>
<template>
<a-row>
<a-col>
<a-form-item>
<a-input v-decorator="['id']" hidden/>
</a-form-item>
</a-col>
</a-row>
</template>
</a-form>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
export default {
name: 'BillPrintIframe',
data () {
return {
title: "三联打印预览",
width: '1550px',
visible: false,
billPrintUrl: '',
height: "",
model: {},
form: this.$form.createForm(this),
loading: false
}
},
created () {
},
methods: {
show(record, billPrintUrl, billPrintHeight) {
this.height = billPrintHeight
this.billPrintUrl = billPrintUrl
this.visible = true;
this.model = Object.assign({}, record);
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'id'))
});
},
handleCancel() {
this.close()
},
close() {
this.$emit('close');
this.visible = false;
}
}
}
</script>
<style scoped>
</style>

159
src/views/erp/bill/dialog/LinkBillList.vue

@ -0,0 +1,159 @@
<template>
<a-modal
:title="title"
:width="1250"
:visible="visible"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<!-- table区域-begin -->
<a-table
bordered
ref="table"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange, type: getType}"
:customRow="rowAction"
@change="handleTableChange">
<template slot="customRenderStatus" slot-scope="status">
<a-tag v-if="status === '0'" color="red">未审核</a-tag>
<a-tag v-if="status === '1'" color="green">已审核</a-tag>
<a-tag v-if="status === '2' && queryParam.subType === '采购订单'" color="cyan">完成采购</a-tag>
<a-tag v-if="status === '2' && queryParam.subType === '销售订单'" color="cyan">完成销售</a-tag>
<a-tag v-if="status === '3' && queryParam.subType === '采购订单'" color="blue">部分采购</a-tag>
<a-tag v-if="status === '3' && queryParam.subType === '销售订单'" color="blue">部分销售</a-tag>
</template>
</a-table>
<!-- table区域-end -->
</a-modal>
</template>
<script>
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
export default {
name: 'LinkBillList',
mixins:[JeecgListMixin],
data () {
return {
title: "操作",
visible: false,
disableMixinCreated: true,
selectedRowKeys: [],
selectionRows: [],
selectBillRows: [],
selectBillIds: '',
queryParam: {
number: "",
searchMaterial: "",
type: "",
subType: "",
status: ""
},
//
columns: [
{
title: '#',
dataIndex: '',
key:'rowIndex',
width:40,
align:"center",
customRender:function (t,r,index) {
return parseInt(index)+1;
}
},
{ title: '', dataIndex: 'organName',width:120},
{ title: '单据编号', dataIndex: 'number',width:150},
{ title: '商品信息', dataIndex: 'materialsList',width:280, ellipsis:true,
customRender:function (text,record,index) {
if(text) {
return text.replace(",",",");
}
}
},
{ title: '单据日期', dataIndex: 'operTimeStr',width:145},
{ title: '操作员', dataIndex: 'userName',width:70},
{ title: '金额合计', dataIndex: 'totalPrice',width:70},
{ title: '状态', dataIndex: 'status', width: 70, align: "center",
scopedSlots: { customRender: 'customRenderStatus' }
}
],
url: {
list: "/depotHead/list"
}
}
},
computed: {
getType: function () {
return 'radio';
}
},
created() {
},
methods: {
show(type, subType, organType, status) {
this.queryParam.type = type
this.queryParam.subType = subType
this.queryParam.status = status
this.columns[1].title = organType
this.model = Object.assign({}, {});
this.visible = true;
this.loadData(1)
},
close () {
this.$emit('close');
this.visible = false;
},
handleCancel () {
this.close()
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
},
handleOk () {
this.getSelectBillRows();
this.$emit('ok', this.selectBillRows);
this.close();
},
getSelectBillRows() {
let dataSource = this.dataSource;
let billIds = "";
this.selectBillRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectBillRows.push(dataSource[i]);
billIds = billIds + "," + dataSource[i].id
}
}
this.selectBillIds = billIds.substring(1);
},
rowAction(record, index) {
return {
on: {
click: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
},
dblclick: () => {
let arr = []
arr.push(record.id)
this.selectedRowKeys = arr
this.handleOk()
}
}
}
}
}
}
</script>
<style scoped>
</style>

188
src/views/erp/bill/dialog/ManyAccountModal.vue

@ -0,0 +1,188 @@
<template>
<a-modal
:title="title"
:width="650"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭"
wrapClassName="ant-modal-cust-warp"
style="top:20%;height: 60%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算账户1">
<a-select style="width:185px;" placeholder="请选择结算账户" v-decorator="[ 'oneAccountId' ]" :dropdownMatchSelectWidth="false" allowClear>
<a-select-option v-for="(item,index) in accountList" :key="index" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算金额">
<a-input-number placeholder="请输入金额" v-decorator.trim="[ 'oneAccountPrice' ]" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算账户2">
<a-select style="width:185px;" placeholder="请选择结算账户" v-decorator="[ 'twoAccountId' ]" :dropdownMatchSelectWidth="false" allowClear>
<a-select-option v-for="(item,index) in accountList" :key="index" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算金额">
<a-input-number placeholder="请输入金额" v-decorator.trim="[ 'twoAccountPrice' ]" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算账户3">
<a-select style="width:185px;" placeholder="请选择结算账户" v-decorator="[ 'threeAccountId' ]" :dropdownMatchSelectWidth="false" allowClear>
<a-select-option v-for="(item,index) in accountList" :key="index" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算金额">
<a-input-number placeholder="请输入金额" v-decorator.trim="[ 'threeAccountPrice' ]" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
import {getAccount} from '@/api/api'
export default {
name: 'ManyAccountModal',
data () {
return {
title:"操作",
visible: false,
model: {},
accountList: [],
accountIdList: [],
accountMoneyList: [],
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
confirmLoading: false,
form: this.$form.createForm(this)
}
},
created () {
},
methods: {
edit (idStr, moneyStr) {
this.initAccount()
this.form.resetFields();
this.model = Object.assign({}, {});
let idList = [], moneyList = []
if(idStr && idStr.indexOf(',')>-1) {
idList = idStr.split(",")
moneyList = moneyStr.split(",")
} else {
idList = idStr
moneyList = moneyStr
}
if(idList[0]) {this.model.oneAccountId = idList[0]-0}
if(idList[1]) {this.model.twoAccountId = idList[1]-0}
if(idList[2]) {this.model.threeAccountId = idList[2]-0}
if(moneyList[0]) {this.model.oneAccountPrice = Math.abs(moneyList[0])}
if(moneyList[1]) {this.model.twoAccountPrice = Math.abs(moneyList[1])}
if(moneyList[2]) {this.model.threeAccountPrice = Math.abs(moneyList[2])}
this.visible = true;
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'oneAccountId','oneAccountPrice',
'twoAccountId','twoAccountPrice','threeAccountId','threeAccountPrice'))
});
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
const that = this;
//
this.form.validateFields((err, values) => {
if (!err) {
let allPrice = 0
that.confirmLoading = true;
that.accountIdList = []
that.accountMoneyList = []
let formData = Object.assign(this.model, values);
if(formData.oneAccountId!==undefined) {
that.accountIdList.push(formData.oneAccountId)
}
if(formData.twoAccountId!==undefined) {
that.accountIdList.push(formData.twoAccountId)
}
if(formData.threeAccountId!==undefined) {
that.accountIdList.push(formData.threeAccountId)
}
if(formData.oneAccountPrice!==undefined) {
that.accountMoneyList.push(formData.oneAccountPrice)
allPrice = allPrice + formData.oneAccountPrice
}
if(formData.twoAccountPrice!==undefined) {
that.accountMoneyList.push(formData.twoAccountPrice)
allPrice = allPrice + formData.twoAccountPrice
}
if(formData.threeAccountPrice!==undefined) {
that.accountMoneyList.push(formData.threeAccountPrice)
allPrice = allPrice + formData.threeAccountPrice
}
if(that.accountIdList.length<2 || that.accountMoneyList.length<2) {
this.$message.warning('抱歉,多账户结算必须选择两个以上账户和金额!');
that.confirmLoading = false;
return;
}
if((formData.oneAccountId && !formData.oneAccountPrice)||
(formData.twoAccountId && !formData.twoAccountPrice)||
(formData.threeAccountId && !formData.threeAccountPrice)) {
this.$message.warning('抱歉,请填写结算金额!');
that.confirmLoading = false;
return;
}
that.$emit('ok', that.accountIdList, that.accountMoneyList, allPrice);
that.confirmLoading = false;
that.close();
}
})
},
handleCancel () {
this.close()
},
initAccount(){
let that = this;
getAccount({}).then((res)=>{
if(res && res.code === 200) {
that.accountList = res.data.accountList
}
})
}
}
}
</script>
<style scoped>
</style>

133
src/views/erp/bill/mixins/BillListMixin.js

@ -0,0 +1,133 @@
import Vue from 'vue'
import {getAction } from '@/api/manage'
import {findBySelectSup, findBySelectCus, findBySelectRetail, getUserList } from '@/api/api'
export const BillListMixin = {
data () {
return {
supList: [],
cusList: [],
retailList: [],
userList: []
}
},
computed: {
importExcelUrl: function(){
return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
},
isBatchDelEnabled: function () {
for (let i = 0; i < this.selectedRowKeys.length; i++) {
if (!this.selectionRows[i].actionsEnabled.delete) {
return false;
}
}
return true;
}
},
created() {
this.removeStatusColumn()
},
methods: {
myHandleAdd() {
this.$refs.modalForm.action = "add";
this.handleAdd();
},
myHandleCopyAdd(record) {
this.$refs.modalForm.action = "copyAdd";
this.$refs.modalForm.edit(record);
this.$refs.modalForm.title = "复制新增";
this.$refs.modalForm.disableSubmit = false;
},
myHandleEdit(record) {
if(record.status === '0') {
this.$refs.modalForm.action = "edit";
this.handleEdit(record);
} else {
this.$message.warning("抱歉,只有未审核的单据才能编辑!")
}
},
myHandleDelete(record) {
if(record.status === '0') {
this.handleDelete(record.id)
} else {
this.$message.warning("抱歉,只有未审核的单据才能删除!")
}
},
myHandleDetail(record, type) {
this.handleDetail(record, type);
},
handleApprove(record) {
this.$refs.modalForm.action = "approve";
this.$refs.modalForm.edit(record);
this.$refs.modalForm.title = "审核";
},
searchReset() {
this.queryParam = {
type: this.queryParam.type,
subType: this.queryParam.subType
}
this.loadData(1);
},
onDateChange: function (value, dateString) {
this.queryParam.beginTime=dateString[0];
this.queryParam.endTime=dateString[1];
},
onDateOk(value) {
console.log(value);
},
removeStatusColumn() {
//没有审核反审核权限的时候直接移除状态列
if(this.btnEnableList.indexOf(2)===-1 && this.btnEnableList.indexOf(7)===-1) {
let statusIndex = 0
for(let i=0; i<this.columns.length; i++){
if(this.columns[i].dataIndex === 'status') {
statusIndex = i
}
}
//移除状态列
this.columns.splice(statusIndex,1)
}
},
initSupplier() {
let that = this;
findBySelectSup({}).then((res)=>{
if(res) {
that.supList = res;
}
});
},
initCustomer() {
let that = this;
findBySelectCus({}).then((res)=>{
if(res) {
that.cusList = res;
}
});
},
initRetail() {
let that = this;
findBySelectRetail({}).then((res)=>{
if(res) {
that.retailList = res;
}
});
},
getDepotData() {
getAction('/depot/findDepotByCurrentUser').then((res)=>{
if(res.code === 200){
this.depotList = res.data;
}else{
this.$message.info(res.data);
}
})
},
initUser() {
getUserList({}).then((res)=>{
if(res) {
this.userList = res;
}
});
}
}
}

748
src/views/erp/bill/mixins/BillModalMixin.js

@ -0,0 +1,748 @@
import { FormTypes, getListData } from '@/utils/JEditableTableUtil'
import {findBySelectSup,findBySelectCus,findBySelectRetail,getMaterialByBarCode,findStockByDepotAndBarCode,getAccount,
getPersonByNumType, getBatchNumberList} from '@/api/api'
import { getAction,putAction } from '@/api/manage'
import { getMpListShort, getNowFormatDateTime } from "@/utils/util"
import Vue from 'vue'
export const BillModalMixin = {
data() {
return {
action: '',
manyAccountBtnStatus: false,
supList: [],
cusList: [],
retailList: [],
personList: {
options: [],
value: ''
},
depotList: [],
accountList: [],
accountIdList: [],
accountMoneyList: [],
billUnitPirce: '',
scanBarCode: '',
scanStatus: true,
isTenant: false,
spans: {
labelCol1: {span: 2},
wrapperCol1: {span: 22},
//1_5: 分为1.5列(相当于占了2/3)
labelCol1_5: { span: 3 },
wrapperCol1_5: { span: 21 },
labelCol2: {span: 4},
wrapperCol2: {span: 20},
labelCol3: {span: 6},
wrapperCol3: {span: 18},
labelCol6: {span: 12},
wrapperCol6: {span: 12}
},
};
},
created () {
// this.isTenant = userInfo.id === userInfo.tenantId? true:false
this.isTenant = true;
var that = this
document.onkeydown = function (e) {
var key = window.event.keyCode
if (key === 13) {
that.handleOk() // 触发事件
}
}
},
computed: {
readOnly: function() {
return this.action !== "add" && this.action !== "edit";
}
},
methods: {
addInit(amountNum) {
getAction('/sequence/buildNumber').then((res) => {
if (res && res.code === 200) {
this.form.setFieldsValue({'number':amountNum + res.data.defaultNumber})
}
})
this.$nextTick(() => {
this.form.setFieldsValue({'operTime':getNowFormatDateTime(), 'discount': 0,
'discountMoney': 0, 'discountLastMoney': 0, 'otherMoney': 0, 'changeAmount': 0, 'debt': 0})
})
this.$nextTick(() => {
getAccount({}).then((res)=>{
if(res && res.code === 200) {
for (const item of res.data.accountList) {
if(item.isDefault){
this.form.setFieldsValue({'accountId': Number(item.id)})
}
}
}
})
})
this.accountIdList = []
this.accountMoneyList = []
this.manyAccountBtnStatus = false
},
copyAddInit(amountNum) {
getAction('/sequence/buildNumber').then((res) => {
if (res && res.code === 200) {
this.form.setFieldsValue({'number':amountNum + res.data.defaultNumber})
}
})
this.$nextTick(() => {
this.form.setFieldsValue({'operTime':getNowFormatDateTime()})
})
},
/** 查询某个tab的数据 */
requestSubTableData(url, params, tab, success) {
tab.loading = true
getAction(url, params).then(res => {
if(res && res.code === 200){
tab.dataSource = res.data.rows
for(let i=0; i<tab.dataSource.length; i++){
let info = tab.dataSource[i]
this.changeColumnShow(info)
}
typeof success === 'function' ? success(res) : ''
}
}).finally(() => {
tab.loading = false
})
},
//改变字段的状态,1-显示 0-隐藏
changeFormTypes(columns, key, type) {
for(let i=0; i<columns.length; i++){
if(columns[i].key === key) {
if(type){
if(key === 'snList' || key === 'batchNumber') {
if(this.prefixNo === 'LSCK' || this.prefixNo === 'CGTH' || this.prefixNo === 'XSCK' || this.prefixNo === 'QTCK') {
columns[i].type = FormTypes.popupJsh //显示
} else {
columns[i].type = FormTypes.input //显示
}
} else if(key === 'expirationDate') {
if(this.prefixNo === 'LSTH' || this.prefixNo === 'CGRK' || this.prefixNo === 'XSTH' || this.prefixNo === 'QTRK') {
columns[i].type = FormTypes.date //显示
} else {
columns[i].type = FormTypes.normal //显示
}
} else {
columns[i].type = FormTypes.normal //显示
}
} else {
columns[i].type = FormTypes.hidden //隐藏
}
}
}
},
initSupplier() {
let that = this;
findBySelectSup({}).then((res)=>{
if(res) {
that.supList = res;
// this.form.setFieldsValue({'organId': Number(that.supList[0].id)})
}
});
},
initCustomer() {
let that = this;
findBySelectCus({}).then((res)=>{
if(res) {
that.cusList = res;
}
});
},
initRetail() {
let that = this;
findBySelectRetail({}).then((res)=>{
if(res) {
that.retailList = res;
}
});
},
initSalesman() {
let that = this;
getPersonByNumType({type:1}).then((res)=>{
if(res) {
that.personList.options = res;
}
});
},
initDepot() {
let that = this;
getAction('/depot/findDepotByCurrentUser').then((res) => {
if(res.code === 200){
let arr = res.data
for(let item of that.materialTable.columns){
if(item.key == 'depotId' || item.key == 'anotherDepotId') {
item.options = []
for(let i=0; i<arr.length; i++) {
let depotInfo = {};
depotInfo.value = arr[i].id + '' //注意-此处value必须为字符串格式
depotInfo.text = arr[i].depotName
depotInfo.title = arr[i].depotName
item.options.push(depotInfo)
}
}
}
}
})
},
initAccount(){
let that = this;
getAccount({}).then((res)=>{
if(res && res.code === 200) {
let list = res.data.accountList
list.splice(0,0,{id: 0, name: '多账户'})
that.accountList = list
}
})
},
handleManyAccount(){
this.selectAccount(0)
},
selectAccount(value){
if(value === 0) { //多账户
this.$refs.manyAccountModalForm.edit(this.accountIdList, this.accountMoneyList)
this.$refs.manyAccountModalForm.title = "多账户结算"
this.manyAccountBtnStatus = true
} else {
this.accountIdList = []
this.accountMoneyList = []
this.manyAccountBtnStatus = false
}
},
manyAccountModalFormOk(idList, moneyList, allPrice) {
this.accountIdList = idList
this.accountMoneyList = moneyList
let discountLastMoney = this.form.getFieldValue('discountLastMoney')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
let debt = (discountLastMoney + otherMoney - allPrice).toFixed(2)
this.$nextTick(() => {
this.form.setFieldsValue({'changeAmount':allPrice, 'debt':debt})
});
},
addSupplier() {
this.$refs.vendorModalForm.add();
this.$refs.vendorModalForm.title = "新增供应商";
this.$refs.vendorModalForm.disableSubmit = false;
},
addCustomer() {
this.$refs.customerModalForm.add();
this.$refs.customerModalForm.title = "新增客户(提醒:如果找不到新添加的客户,请到用户管理检查是否分配了该客户权限)";
this.$refs.customerModalForm.disableSubmit = false;
},
addMember() {
this.$refs.memberModalForm.add();
this.$refs.memberModalForm.title = "新增会员";
this.$refs.memberModalForm.disableSubmit = false;
},
handleBatchSetDepot() {
this.$refs.batchSetDepotModalForm.add();
this.$refs.batchSetDepotModalForm.title = "批量设置仓库";
this.$refs.batchSetDepotModalForm.disableSubmit = false;
},
addDepot() {
this.$refs.depotModalForm.add();
this.$refs.depotModalForm.title = "新增仓库";
this.$refs.depotModalForm.disableSubmit = false;
},
addAccount() {
this.$refs.accountModalForm.add();
this.$refs.accountModalForm.title = "新增结算账户";
this.$refs.accountModalForm.disableSubmit = false;
},
vendorModalFormOk() {
this.initSupplier()
},
customerModalFormOk() {
this.initCustomer()
},
memberModalFormOk() {
this.initRetail()
},
batchSetDepotModalFormOk(depotId) {
this.getAllTable().then(tables => {
return getListData(this.form, tables)
}).then(allValues => {
//获取单据明细列表信息
let detailArr = allValues.tablesValue[0].values
let barCodes = ''
for(let detail of detailArr){
barCodes += detail.barCode + ','
}
if(barCodes) {
barCodes = barCodes.substring(0, barCodes.length-1)
}
let param = {
barCode: barCodes,
depotId: depotId,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')), //扩展属性
prefixNo: this.prefixNo
}
getMaterialByBarCode(param).then((res) => {
if (res && res.code === 200) {
let mList = res.data
//构造新的列表数组,用于存放单据明细信息
let newDetailArr = []
if(mList && mList.length) {
for (let i = 0; i < detailArr.length; i++) {
let item = detailArr[i]
item.depotId = depotId
item.stock = mList[i] ? (mList[i].stock ? mList[i].stock : 0) : 0
newDetailArr.push(item)
}
} else {
for (let i = 0; i < detailArr.length; i++) {
let item = detailArr[i]
item.depotId = depotId
newDetailArr.push(item)
}
}
this.materialTable.dataSource = newDetailArr
}
})
})
},
depotModalFormOk() {
this.initDepot()
},
accountModalFormOk() {
this.initAccount()
},
onAdded(event) {
const { row, target } = event
getAction('/depot/findDepotByCurrentUser').then((res) => {
if (res.code === 200) {
let arr = res.data
for (let i = 0; i < arr.length; i++) {
if(arr[i].isDefault){
target.setValues([{rowKey: row.id, values: {depotId: arr[i].id+''}}])
}
}
}
})
},
//单元值改变一个字符就触发一次
onValueChange(event) {
let that = this
const { type, row, column, value, target } = event
let param,snList,batchNumber,operNumber,unitPrice,allPrice,taxRate,taxMoney,taxLastMoney
switch(column.key) {
case "depotId":
if(row.barCode){
that.getStockByDepotBarCode(row, target)
}
break;
case "barCode":
param = {
barCode: value,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')), //扩展属性
prefixNo: this.prefixNo
}
getMaterialByBarCode(param).then((res) => {
if (res && res.code === 200) {
let mList = res.data
if (value.indexOf(',') > -1) {
//多个物料编码
this.$refs.materialDataTable.getValues((error, values) => {
values.pop() //移除最后一行数据
let mArr = values
for (let i = 0; i < mList.length; i++) {
let mInfo = mList[i]
this.changeColumnShow(mInfo)
let mObj = this.parseInfoToObj(mInfo)
mObj.depotId = mInfo.depotId
mObj.stock = mInfo.stock
mObj.barCode = mInfo.mBarCode
mArr.push(mObj)
}
let taxLastMoneyTotal = 0
for (let j = 0; j < mArr.length; j++) {
taxLastMoneyTotal += mArr[j].taxLastMoney-0
//组合和拆分单据给商品类型进行重新赋值
if(j===0) {
mArr[0].mType = '组合件'
} else {
mArr[j].mType = '普通子件'
}
}
this.materialTable.dataSource = mArr
target.statisticsColumns.taxLastMoney = taxLastMoneyTotal
that.autoChangePrice(target)
})
} else {
//单个物料编码
findStockByDepotAndBarCode({ depotId: row.depotId, barCode: row.barCode }).then((res) => {
if (res && res.code === 200) {
let mArr = []
let mInfo = mList[0]
this.changeColumnShow(mInfo)
let mInfoEx = this.parseInfoToObj(mInfo)
mInfoEx.stock = res.data.stock
mInfoEx.barCode = row.barCode
let mObj = {
rowKey: row.id,
values: mInfoEx
}
mArr.push(mObj)
target.setValues(mArr);
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
target.autoSelectBySpecialKey('operNumber')
}
})
}
}
});
break;
case "snList":
snList = value
if(snList) {
let snArr = snList.split(',')
operNumber = snArr.length
taxRate = row.taxRate-0 //税率
unitPrice = row.unitPrice-0 //单价
allPrice = (unitPrice*operNumber).toFixed(2)-0
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {operNumber: operNumber, allPrice: allPrice, taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
}
break;
case "batchNumber":
batchNumber = value
getBatchNumberList({name:'', depotId: row.depotId, barCode: row.barCode, batchNumber: batchNumber}).then((res) => {
if (res && res.code === 200) {
if(res.data && res.data.rows) {
let info = res.data.rows[0]
operNumber = info.totalNum
taxRate = row.taxRate-0 //税率
unitPrice = row.unitPrice-0 //单价
allPrice = (unitPrice*operNumber).toFixed(2)-0
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {expirationDate: info.expirationDateStr, operNumber: operNumber,
allPrice: allPrice, taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
}
}
})
break;
case "operNumber":
operNumber = value-0
taxRate = row.taxRate-0 //税率
unitPrice = row.unitPrice-0 //单价
allPrice = (unitPrice*operNumber).toFixed(2)-0
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {allPrice: allPrice, taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
break;
case "unitPrice":
operNumber = row.operNumber-0 //数量
unitPrice = value-0 //单价
taxRate = row.taxRate-0 //税率
allPrice = (unitPrice*operNumber).toFixed(2)-0
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {allPrice: allPrice, taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
break;
case "allPrice":
operNumber = row.operNumber-0 //数量
taxRate = row.taxRate-0 //税率
allPrice = value-0
unitPrice = (allPrice/operNumber).toFixed(6)-0 //单价
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {unitPrice: unitPrice, taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
break;
case "taxRate":
operNumber = row.operNumber-0 //数量
allPrice = row.allPrice-0
unitPrice = row.unitPrice-0
taxRate = value-0 //税率
taxMoney =((taxRate*0.01)*allPrice).toFixed(2)-0
taxLastMoney = (allPrice + taxMoney).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {taxMoney: taxMoney, taxLastMoney: taxLastMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
break;
case "taxLastMoney":
operNumber = row.operNumber-0 //数量
taxLastMoney = value-0
taxRate = row.taxRate-0 //税率
unitPrice = (taxLastMoney/operNumber/(1+taxRate*0.01)).toFixed(6)-0
allPrice = (unitPrice*operNumber).toFixed(2)-0
taxMoney =(taxLastMoney-allPrice).toFixed(2)-0
target.setValues([{rowKey: row.id, values: {unitPrice: unitPrice, allPrice: allPrice, taxMoney: taxMoney}}])
target.recalcAllStatisticsColumns()
that.autoChangePrice(target)
break;
case "currentStock" :
target.setValues([{rowKey: row.id, values: {operNumber: Number(value) - Number(row.stock)}}])
break
}
if(value.split(',').length > 1) {
this.clearKh()
}
},
//转为商品对象
parseInfoToObj(mInfo) {
return {
barCode: mInfo.mBarCode,
name: mInfo.name,
standard: mInfo.standard,
model: mInfo.model,
color: mInfo.color,
materialOther: mInfo.materialOther,
unit: mInfo.commodityUnit,
sku: mInfo.sku,
operNumber: 1,
unitPrice: mInfo.billPrice,
allPrice: mInfo.billPrice,
taxRate: 0,
taxMoney: 0,
taxLastMoney: mInfo.billPrice
}
},
//使得型号、颜色、扩展信息、sku等为隐藏
changeColumnHide() {
this.changeFormTypes(this.materialTable.columns, 'model', 0)
this.changeFormTypes(this.materialTable.columns, 'color', 0)
this.changeFormTypes(this.materialTable.columns, 'materialOther', 0)
this.changeFormTypes(this.materialTable.columns, 'sku', 0)
},
//使得sku、序列号、批号、到期日等为显示
changeColumnShow(info) {
if(info.model) {
this.changeFormTypes(this.materialTable.columns, 'model', 1)
}
if(info.color) {
this.changeFormTypes(this.materialTable.columns, 'color', 1)
}
if(info.materialOther) {
this.changeFormTypes(this.materialTable.columns, 'materialOther', 1)
}
if(info.sku) {
this.changeFormTypes(this.materialTable.columns, 'sku', 1)
}
if(info.enableSerialNumber === "1") {
this.changeFormTypes(this.materialTable.columns, 'snList', 1)
}
if(info.enableBatchNumber === "1") {
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 1)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 1)
}
},
//删除一行或多行的时候触发
onDeleted(ids, target) {
target.recalcAllStatisticsColumns()
this.autoChangePrice(target)
},
//根据仓库和物料编码查询库存
getStockByDepotBarCode(row, target){
findStockByDepotAndBarCode({ depotId: row.depotId, barCode: row.barCode }).then((res) => {
if (res && res.code === 200) {
console.log(res.data.stock,'res.data.stock')
target.setValues([{rowKey: row.id, values: {stock: res.data.stock}}])
target.recalcAllStatisticsColumns()
}
})
},
//改变优惠、本次付款、欠款的值
autoChangePrice(target) {
let allTaxLastMoney = target.statisticsColumns.taxLastMoney-0
let discount = this.form.getFieldValue('discount')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
let discountMoney = (discount*0.01*allTaxLastMoney).toFixed(2)-0
let discountLastMoney = (allTaxLastMoney-discountMoney).toFixed(2)-0
let changeAmountNew = (discountLastMoney + otherMoney).toFixed(2)-0
this.$nextTick(() => {
this.form.setFieldsValue({'discount':discount,'discountMoney':discountMoney,'discountLastMoney':discountLastMoney,
'changeAmount':changeAmountNew,'debt':0})
});
},
//改变优惠率
onKeyUpDiscount(e) {
const value = e.target.value-0
let discountMoney = this.form.getFieldValue('discountMoney')-0
let discountLastMoney = this.form.getFieldValue('discountLastMoney')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
let allTaxLastMoney = (discountMoney + discountLastMoney).toFixed(2)-0
let discountMoneyNew = (allTaxLastMoney*value*0.01).toFixed(2)-0
let discountLastMoneyNew = (allTaxLastMoney - discountMoneyNew).toFixed(2)-0
let changeAmountNew = (discountLastMoneyNew + otherMoney).toFixed(2)-0
this.$nextTick(() => {
this.form.setFieldsValue({'discountMoney':discountMoneyNew,'discountLastMoney':discountLastMoneyNew,
'changeAmount':changeAmountNew,'debt':0})
});
},
//改变付款优惠
onKeyUpDiscountMoney(e) {
const value = e.target.value-0
let discount = this.form.getFieldValue('discount')-0
let discountLastMoney = this.form.getFieldValue('discountLastMoney')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
if(discount !== 100) {
let allTaxLastMoney = (discountLastMoney/(1-discount/100)).toFixed(2)-0
let discountNew = (value/allTaxLastMoney*100).toFixed(2)-0
let discountLastMoneyNew = (allTaxLastMoney - value).toFixed(2)-0
let changeAmountNew = (discountLastMoneyNew + otherMoney).toFixed(2)-0
this.$nextTick(() => {
this.form.setFieldsValue({'discount':discountNew,'discountLastMoney':discountLastMoneyNew,
'changeAmount':changeAmountNew,'debt':0})
});
}
},
//其它费用
onKeyUpOtherMoney(e) {
const value = e.target.value-0
let discountLastMoney = this.form.getFieldValue('discountLastMoney')-0
let changeAmountNew = (discountLastMoney + value).toFixed(2)-0
this.$nextTick(() => {
this.form.setFieldsValue({'changeAmount':changeAmountNew, 'debt':0})
});
},
//改变本次付款
onKeyUpChangeAmount(e) {
const value = e.target.value-0
let discountLastMoney = this.form.getFieldValue('discountLastMoney')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
let debtNew = (discountLastMoney + otherMoney - value).toFixed(2)-0
this.$nextTick(() => {
this.form.setFieldsValue({'debt':debtNew})
});
},
scanEnter() {
this.scanStatus = false
this.$nextTick(() => {
this.$refs.scanBarCode.focus()
})
},
//扫码之后回车
scanPressEnter() {
if(this.scanBarCode) {
this.getAllTable().then(tables => {
return getListData(this.form, tables)
}).then(allValues => {
let param = {
barCode: this.scanBarCode,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')), //扩展属性
prefixNo: this.prefixNo
}
getMaterialByBarCode(param).then((res) => {
if (res && res.code === 200) {
let hasFinished = false
let allLastMoney = 0
let allTaxLastMoney = 0
//获取单据明细列表信息
let detailArr = allValues.tablesValue[0].values
//构造新的列表数组,用于存放单据明细信息
let newDetailArr = []
for(let detail of detailArr){
if(detail.barCode) {
//如果物料编码重复,就在给原来的数量加1
if(detail.barCode === this.scanBarCode) {
detail.operNumber = (detail.operNumber-0)+1
//由于改变了商品数量,需要同时更新相关金额和价税合计
let taxRate = detail.taxRate-0 //税率
let unitPrice = detail.unitPrice-0 //单价
detail.allPrice = (unitPrice*detail.operNumber).toFixed(2)-0
detail.taxMoney = ((taxRate*0.01)*detail.allPrice).toFixed(2)-0
detail.taxLastMoney = (detail.allPrice + detail.taxMoney).toFixed(2)-0
hasFinished = true
}
newDetailArr.push(detail)
}
}
if(!hasFinished) {
//将扫码的物料编码对应的商品加入列表
let item = {}
item.barCode = this.scanBarCode
let mList = res.data
if(mList && mList.length>0) {
let mInfo = mList[0]
this.changeColumnShow(mInfo)
item.depotId = mInfo.depotId
item.name = mInfo.name
item.standard = mInfo.standard
item.model = mInfo.model
item.color = mInfo.color
item.materialOther = mInfo.materialOther
item.stock = mInfo.stock
item.unit = mInfo.commodityUnit
item.sku = mInfo.sku
item.operNumber = 1
item.unitPrice = mInfo.billPrice
item.allPrice = mInfo.billPrice
item.taxRate = 0
item.taxMoney = 0
item.taxLastMoney = mInfo.billPrice
newDetailArr.push(item)
} else {
this.$message.warning('抱歉,此物料编码不存在商品信息!');
}
}
//组合和拆分单据给商品类型进行重新赋值
for(let i=0; i< newDetailArr.length; i++) {
if(i===0) {
newDetailArr[0].mType = '组合件'
} else {
newDetailArr[i].mType = '普通子件'
}
}
this.materialTable.dataSource = newDetailArr
//更新优惠后金额、本次付款等信息
for(let newDetail of newDetailArr){
allLastMoney = allLastMoney + (newDetail.allPrice-0)
allTaxLastMoney = allTaxLastMoney + (newDetail.taxLastMoney-0)
}
let discount = this.form.getFieldValue('discount')-0
let otherMoney = this.form.getFieldValue('otherMoney')-0
let discountMoney = (discount*0.01*allTaxLastMoney).toFixed(2)-0
let discountLastMoney = (allTaxLastMoney-discountMoney).toFixed(2)-0
let changeAmountNew = (discountLastMoney + otherMoney).toFixed(2)-0
if(this.prefixNo === 'LSCK' || this.prefixNo === 'LSTH') {
this.$nextTick(() => {
this.form.setFieldsValue({'changeAmount':allLastMoney,'getAmount':allLastMoney,'backAmount':0})
});
} else {
this.$nextTick(() => {
this.form.setFieldsValue({'discount':discount,'discountMoney':discountMoney,'discountLastMoney':discountLastMoney,
'changeAmount':changeAmountNew,'debt':0})
});
}
//置空扫码的内容
this.scanBarCode = ''
this.$refs.scanBarCode.focus()
}
})
})
}
},
stopScan() {
this.scanStatus = true
this.scanBarCode = ''
},
//清空空行
clearKh() {
this.getAllTable().then(tables => {
let inputValues = tables[0].inputValues
let ids = []
inputValues.forEach((item) => {
if(!item.barCode && !item.operNumber) {
ids.push(item.id)
}
})
tables[0].removeRows(ids)
})
}
}
}

239
src/views/erp/bill/modules/AllocationOutModal.vue

@ -0,0 +1,239 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品条码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品条码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "AllocationOutModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
DepotModal,
BatchSetDepot,
JUpload,
JDate
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'DBCK',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true },
{ title: '调入仓库', key: 'anotherDepotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [], allowSearch:true},
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initDepot()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '出库'
billMain.subType = '调拨'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
}
}
</script>
<style scoped>
</style>

258
src/views/erp/bill/modules/AssembleModal.vue

@ -0,0 +1,258 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品条码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品条码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getAction } from '@/api/manage'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "AssembleModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
DepotModal,
BatchSetDepot,
JUpload,
JDate
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'ZZD',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '商品类型',key: 'mType',width:'7%', type: FormTypes.normal },
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true},
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.model.debt = (this.model.discountLastMoney - this.model.changeAmount).toFixed(2)
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount','debt'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initDepot()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '其它'
billMain.subType = '组装单'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
onAdded(event) {
const { row, target } = event
getAction('/depot/findDepotByCurrentUser').then((res) => {
if (res.code === 200) {
let arr = res.data
for (let i = 0; i < arr.length; i++) {
if(arr[i].isDefault){
target.setValues([{rowKey: row.id, values: {depotId: arr[i].id+''}}])
}
}
}
})
if(target.rows.length>=2) {
target.setValues([{rowKey: row.id, values: {mType: '普通子件'}}])
} else {
target.setValues([{rowKey: row.id, values: {mType: '组合件'}}])
}
}
}
}
</script>
<style scoped>
</style>

257
src/views/erp/bill/modules/DisassembleModal.vue

@ -0,0 +1,257 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品条码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品条码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getAction } from '@/api/manage'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "DisassembleModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
DepotModal,
BatchSetDepot,
JUpload,
JDate
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'CXD',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '商品类型',key: 'mType',width:'7%', type: FormTypes.normal },
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true},
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initDepot()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '其它'
billMain.subType = '拆卸单'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
onAdded(event) {
const { row, target } = event
getAction('/depot/findDepotByCurrentUser').then((res) => {
if (res.code === 200) {
let arr = res.data
for (let i = 0; i < arr.length; i++) {
if(arr[i].isDefault){
target.setValues([{rowKey: row.id, values: {depotId: arr[i].id+''}}])
}
}
}
})
if(target.rows.length>=2) {
target.setValues([{rowKey: row.id, values: {mType: '普通子件'}}])
} else {
target.setValues([{rowKey: row.id, values: {mType: '组合件'}}])
}
}
}
}
</script>
<style scoped>
</style>

287
src/views/erp/bill/modules/InventoryReviewModal.vue

@ -0,0 +1,287 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="关联单据">
<a-input-search placeholder="请选择关联单据" v-decorator="[ 'linkNumber' ]" @search="onSearchLinkNumber" :readOnly="true"/>
</a-form-item>
</a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品物料编码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品物料编码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<link-bill-list ref="linkBillList" @ok="linkBillListOk"></link-bill-list>
<vendor-modal ref="vendorModalForm" @ok="vendorModalFormOk"></vendor-modal>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import LinkBillList from '../dialog/LinkBillList'
import VendorModal from '../../system/modules/VendorModal'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "OtherInModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
VendorModal,
DepotModal,
BatchSetDepot,
JUpload,
JDate,
LinkBillList,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'QTRK',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true },
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '实存', key: 'currentStock', width: '4%', type: FormTypes.inputNumber},
{ title: '序列号', key: 'snList', width: '12%', type: FormTypes.input, placeholder: '多个序列号请用逗号隔开',
validateRules: [{ pattern: /^\S{1,100}$/, message: '请小于100位字符' }]
},
{ title: '批号', key: 'batchNumber', width: '5%', type: FormTypes.input },
{ title: '有效期', key: 'expirationDate',width: '7%', type: FormTypes.date },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber,readonly:true, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
this.changeFormTypes(this.materialTable.columns, 'snList', 0)
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 0)
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'linkNumber', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initSupplier()
this.initDepot()
},
onSearchLinkNumber() {
this.$refs.linkBillList.show('复盘', '盘点')
this.$refs.linkBillList.title = "请选择盘点复盘"
},
linkBillListOk(selectBillRows) {
this.changeFormTypes(this.materialTable.columns, 'preNumber', 1)
this.changeFormTypes(this.materialTable.columns, 'finishNumber', 1)
if(selectBillRows && selectBillRows.length>0) {
let record = selectBillRows[0]
this.$nextTick(() => {
this.form.setFieldsValue({
'linkNumber': record.linkNumber,
'linkNumber': record.number,
'remark': record.remark,
'discountLastMoney': record.totalPrice,
'changeAmount': record.totalPrice
})
});
//
let params = {
headerId: record.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
this.requestSubTableDataEx(this.url.detailList, params, this.materialTable);
}
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '其它'
billMain.subType = '盘点复盘'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
}
}
</script>
<style scoped>
</style>

269
src/views/erp/bill/modules/OtherInModal.vue

@ -0,0 +1,269 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="供应商">
<a-select placeholder="选择供应商" v-decorator="[ 'organId' ]"
:dropdownMatchSelectWidth="false" showSearch optionFilterProp="children">
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0;" />
<div v-if="isTenant" style="padding: 4px 8px; cursor: pointer;"
@mousedown="e => e.preventDefault()" @click="addSupplier"><a-icon type="plus" /> 新增供应商</div>
</div>
<a-select-option v-for="(item,index) in supList" :key="index" :value="Number(item.id)">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品物料编码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品物料编码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<vendor-modal ref="vendorModalForm" @ok="vendorModalFormOk"></vendor-modal>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import VendorModal from '../../system/modules/VendorModal'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "OtherInModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
VendorModal,
DepotModal,
BatchSetDepot,
JUpload,
JDate,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'QTRK',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true},
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '序列号', key: 'snList', width: '12%', type: FormTypes.input, placeholder: '多个序列号请用逗号隔开',
validateRules: [{ pattern: /^\S{1,100}$/, message: '请小于100位字符' }]
},
{ title: '批号', key: 'batchNumber', width: '5%', type: FormTypes.input },
{ title: '有效期', key: 'expirationDate',width: '7%', type: FormTypes.date },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
this.changeFormTypes(this.materialTable.columns, 'snList', 0)
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 0)
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initSupplier()
this.initDepot()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '入库'
billMain.subType = '其它'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
}
}
</script>
<style scoped>
</style>

267
src/views/erp/bill/modules/OtherOutModal.vue

@ -0,0 +1,267 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="客户">
<a-select placeholder="选择客户" v-decorator="[ 'organId' ]"
:dropdownMatchSelectWidth="false" showSearch optionFilterProp="children">
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0;" />
<div v-if="isTenant" style="padding: 4px 8px; cursor: pointer;"
@mousedown="e => e.preventDefault()" @click="addCustomer"><a-icon type="plus" /> 新增客户</div>
</div>
<a-select-option v-for="(item,index) in cusList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"></a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品条码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品条码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<customer-modal ref="customerModalForm" @ok="customerModalFormOk"></customer-modal>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import CustomerModal from '../../system/modules/CustomerModal'
import DepotModal from '../../system/modules/DepotModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort } from "@/utils/util"
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "OtherOutModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
CustomerModal,
DepotModal,
BatchSetDepot,
JUpload,
JDate,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'QTCK',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true },
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '序列号', key: 'snList', width: '12%', type: FormTypes.popupJsh, kind: 'sn', multi: true },
{ title: '批号', key: 'batchNumber', width: '7%', type: FormTypes.popupJsh, kind: 'batch', multi: false },
{ title: '有效期', key: 'expirationDate',width: '6%', type: FormTypes.normal },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
type:{
rules: [
{ required: true, message: '请选择类型!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
this.changeFormTypes(this.materialTable.columns, 'snList', 0)
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 0)
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initCustomer()
this.initDepot()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '出库'
billMain.subType = '其它'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
}
}
</script>
<style scoped>
</style>

404
src/views/erp/bill/modules/PurchaseBackModal.vue

@ -0,0 +1,404 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
style="top:5%;height: 100%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="供应商">
<a-select placeholder="选择供应商" v-decorator="[ 'organId', validatorRules.organId ]"
:dropdownMatchSelectWidth="false" showSearch optionFilterProp="children">
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0;" />
<div v-if="isTenant" style="padding: 4px 8px; cursor: pointer;"
@mousedown="e => e.preventDefault()" @click="addSupplier"><a-icon type="plus" /> 新增供应商</div>
</div>
<a-select-option v-for="(item,index) in supList" :key="index" :value="Number(item.id)">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据编号">
<a-input placeholder="请输入单据编号" v-decorator.trim="[ 'number' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="关联单据">
<a-input-search placeholder="请选择关联单据" v-decorator="[ 'linkNumber' ]" @search="onSearchLinkNumber" :readOnly="true"/>
</a-form-item>
</a-col>
</a-row>
<j-editable-table id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted">
<template #buttonAfter>
<a-row :gutter="24" style="float:left;" data-step="4" data-title="扫码录入" data-intro="此功能支持扫码枪扫描商品条码进行录入">
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input placeholder="请扫码商品条码并回车" v-model="scanBarCode" @pressEnter="scanPressEnter" ref="scanBarCode"/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float:left;">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting"/>批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus"/>新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left:10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{xs: { span: 24 },sm: { span: 24 }}" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="[ 'remark' ]" style="margin-top:8px;"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="优惠率">
<a-input style="width:185px;" placeholder="请输入优惠率" v-decorator.trim="[ 'discount' ]" suffix="%" @keyup="onKeyUpDiscount"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="退款优惠">
<a-input placeholder="请输入付款优惠" v-decorator.trim="[ 'discountMoney' ]" @keyup="onKeyUpDiscountMoney"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="优惠后金额">
<a-input placeholder="请输入优惠后金额" v-decorator.trim="[ 'discountLastMoney' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="其它费用">
<a-input placeholder="请输入其它费用" v-decorator.trim="[ 'otherMoney' ]" @keyup="onKeyUpOtherMoney"/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="结算账户">
<a-select style="width:185px;" placeholder="选择结算账户" v-decorator="[ 'accountId']"
:dropdownMatchSelectWidth="false" allowClear @select="selectAccount">
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0;" />
<div v-if="isTenant" style="padding: 4px 8px; cursor: pointer;"
@mousedown="e => e.preventDefault()" @click="addAccount"><a-icon type="plus" /> 新增结算账户</div>
</div>
<a-select-option v-for="(item,index) in accountList" :key="index" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
<a-tooltip title="多账户明细">
<a-button type="default" icon="folder" style="margin-left: 8px;" size="small" v-show="manyAccountBtnStatus" @click="handleManyAccount"/>
</a-tooltip>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="本次退款">
<a-input placeholder="请输入本次退款" v-decorator.trim="[ 'changeAmount' ]" @keyup="onKeyUpChangeAmount" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="本次欠款">
<a-input placeholder="请输入本次欠款" v-decorator.trim="[ 'debt' ]" :readOnly="true"/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="附件">
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<many-account-modal ref="manyAccountModalForm" @ok="manyAccountModalFormOk"></many-account-modal>
<link-bill-list ref="linkBillList" @ok="linkBillListOk"></link-bill-list>
<vendor-modal ref="vendorModalForm" @ok="vendorModalFormOk"></vendor-modal>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<account-modal ref="accountModalForm" @ok="accountModalFormOk"></account-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import ManyAccountModal from '../dialog/ManyAccountModal'
import LinkBillList from '../dialog/LinkBillList'
import VendorModal from '../../system/modules/VendorModal'
import DepotModal from '../../system/modules/DepotModal'
import AccountModal from '../../system/modules/AccountModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort} from "@/utils/util"
import { getAction } from '@/api/manage'
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "PurchaseBackModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
ManyAccountModal,
LinkBillList,
VendorModal,
DepotModal,
AccountModal,
BatchSetDepot,
JUpload,
JDate,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'CGTH',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true },
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '序列号', key: 'snList', width: '12%', type: FormTypes.popupJsh, kind: 'sn', multi: true },
{ title: '批号', key: 'batchNumber', width: '7%', type: FormTypes.popupJsh, kind: 'batch', multi: false },
{ title: '有效期', key: 'expirationDate',width: '6%', type: FormTypes.normal },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '5%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '5%', type: FormTypes.inputNumber},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '税率', key: 'taxRate', width: '4%', type: FormTypes.inputNumber,placeholder: '%'},
{ title: '税额', key: 'taxMoney', width: '5%', type: FormTypes.inputNumber, readonly: true, statistics: true },
{ title: '价税合计', key: 'taxLastMoney', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
organId:{
rules: [
{ required: true, message: '请选择供应商!' }
]
},
accountId:{
rules: [
{ required: true, message: '请选择结算账户!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
this.changeFormTypes(this.materialTable.columns, 'snList', 0)
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 0)
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.model.debt = (this.model.discountLastMoney + this.model.otherMoney - this.model.changeAmount).toFixed(2)
if(this.model.accountId == null) {
this.model.accountId = 0
this.manyAccountBtnStatus = true
this.accountIdList = this.model.accountIdList
this.accountMoneyList = this.model.accountMoneyList
} else {
this.manyAccountBtnStatus = false
}
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'linkNumber', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount','debt'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initSupplier()
this.initDepot()
this.initAccount()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '出库'
billMain.subType = '采购退货'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = totalPrice
if(billMain.accountId === 0) {
billMain.accountId = ''
}
billMain.accountIdList = this.accountIdList.length>0 ? JSON.stringify(this.accountIdList) : ""
billMain.accountMoneyList = this.accountMoneyList.length>0 ? JSON.stringify(this.accountMoneyList) : ""
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
onSearchLinkNumber() {
this.$refs.linkBillList.show('入库', '采购', '供应商', "1")
this.$refs.linkBillList.title = "选择采购入库"
},
linkBillListOk(selectBillRows) {
if(selectBillRows && selectBillRows.length>0) {
let record = selectBillRows[0]
this.$nextTick(() => {
this.form.setFieldsValue({
'organId': record.organId,
'linkNumber': record.number,
'remark': record.remark,
'discountLastMoney': record.totalPrice,
'changeAmount': record.totalPrice
})
});
//
let params = {
headerId: record.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
this.requestSubTableDataEx(this.url.detailList, params, this.materialTable);
}
},
/** 查询某个tab的数据,给明细里面的价税合计赋值 */
requestSubTableDataEx(url, params, tab, success) {
tab.loading = true
getAction(url, params).then(res => {
if(res && res.code === 200){
let list = res.data.rows
let listEx = []
for(let j=0; j<list.length; j++){
let info = list[j];
info.taxMoney = 0
info.taxLastMoney = info.allPrice
listEx.push(info)
this.changeColumnShow(info)
}
tab.dataSource = listEx
typeof success === 'function' ? success(res) : ''
}
}).finally(() => {
tab.loading = false
})
}
}
}
</script>
<style scoped>
</style>

564
src/views/erp/bill/modules/PurchaseInModal.vue

@ -0,0 +1,564 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
:keyboard="false"
:forceRender="true"
v-bind:prefixNo="prefixNo"
switchHelp
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
wrapClassName="ant-modal-cust-warp"
:id="prefixNo"
style="top: 5%; height: 100%; overflow-y: hidden"
>
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="供应商"
data-step="1"
data-title="供应商"
data-intro="供应商必须选择,如果发现需要选择的供应商尚未录入,可以在下拉框中点击新增供应商进行录入"
>
<a-select
placeholder="选择供应商"
v-decorator="['organId', validatorRules.organId]"
:dropdownMatchSelectWidth="false"
showSearch
>
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0" />
<div
v-if="isTenant"
style="padding: 4px 8px; cursor: pointer"
@mousedown="(e) => e.preventDefault()"
@click="addSupplier"
>
<a-icon type="plus" /> 新增供应商
</div>
</div>
<a-select-option v-for="(item, index) in supList" :key="index" :value="item.id">
{{ item.supplier }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="单据日期">
<j-date v-decorator="['operTime', validatorRules.operTime]" :show-time="true" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="单据编号"
data-step="2"
data-title="单据编号"
data-intro="单据编号自动生成、自动累加、开头是单据类型的首字母缩写,累加的规则是每次打开页面会自动占用一个新的编号"
>
<a-input placeholder="请输入单据编号" v-decorator.trim="['number']" :readOnly="true" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="关联订单"
data-step="3"
data-title="关联订单"
data-intro="采购入库单据可以通过关联订单来选择已录入的订单选择之后会自动加载订单的内容然后继续录入仓库等信息完成单据的提交
提交之后原来的采购订单会对应的改变单据状态另外本系统支持订单多次入库只需选择订单之后修改对应的商品数量即可"
>
<a-input-search
placeholder="请选择关联订单"
v-decorator="['linkNumber']"
@search="onSearchLinkNumber"
:readOnly="true"
/>
</a-form-item>
</a-col>
</a-row>
<j-editable-table
id="billModal"
:ref="refKeys[0]"
:loading="materialTable.loading"
:columns="materialTable.columns"
:dataSource="materialTable.dataSource"
:maxHeight="300"
:rowNumber="false"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
@valueChange="onValueChange"
@added="onAdded"
@deleted="onDeleted"
>
<template #buttonAfter>
<a-row
:gutter="24"
style="float: left"
data-step="4"
data-title="扫码录入"
data-intro="此功能支持扫码枪扫描商品物料编码进行录入"
>
<a-col v-if="scanStatus" :md="6" :sm="24">
<a-button @click="scanEnter">扫码录入</a-button>
</a-col>
<a-col v-if="!scanStatus" :md="16" :sm="24" style="padding: 0 6px 0 12px">
<a-input
placeholder="请扫码商品物料编码并回车"
v-model="scanBarCode"
@pressEnter="scanPressEnter"
ref="scanBarCode"
/>
</a-col>
<a-col v-if="!scanStatus" :md="6" :sm="24" style="padding: 0px">
<a-button @click="stopScan">收起扫码</a-button>
</a-col>
</a-row>
<a-row :gutter="24" style="float: left">
<a-col :md="24" :sm="24">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1" @click="handleBatchSetDepot"><a-icon type="setting" />批量设置</a-menu-item>
<a-menu-item v-if="isTenant" key="2" @click="addDepot"><a-icon type="plus" />新增仓库</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px">仓库操作 <a-icon type="down" /></a-button>
</a-dropdown>
</a-col>
</a-row>
<a-button type="primary" @click="clearKh" style="margin-left: 10px">清空空行</a-button>
</template>
</j-editable-table>
<a-row class="form-row" :gutter="24">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="{ xs: { span: 24 }, sm: { span: 24 } }" label="">
<a-textarea :rows="1" placeholder="请输入备注" v-decorator="['remark']" style="margin-top: 8px" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="优惠率"
data-step="5"
data-title="优惠率"
data-intro="针对单据明细中商品总金额进行优惠的比例"
>
<a-input
style="width: 185px"
placeholder="请输入优惠率"
v-decorator.trim="['discount']"
suffix="%"
@keyup="onKeyUpDiscount"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="付款优惠"
data-step="6"
data-title="付款优惠"
data-intro="针对单据明细中商品总金额进行优惠的金额"
>
<a-input
placeholder="请输入付款优惠"
v-decorator.trim="['discountMoney']"
@keyup="onKeyUpDiscountMoney"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="优惠后金额"
data-step="7"
data-title="优惠后金额"
data-intro="针对单据明细中商品总金额进行优惠后的金额"
>
<a-input placeholder="请输入优惠后金额" v-decorator.trim="['discountLastMoney']" :readOnly="true" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="其它费用"
data-step="8"
data-title="其它费用"
data-intro="比如快递费、油费、过路费"
>
<a-input placeholder="请输入其它费用" v-decorator.trim="['otherMoney']" @keyup="onKeyUpOtherMoney" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="结算账户"
data-step="9"
data-title="结算账户"
data-intro="如果在下拉框中选择多账户,则可以通过多个结算账户进行结算"
>
<a-select
style="width: 185px"
placeholder="选择结算账户"
v-decorator="['accountId']"
:dropdownMatchSelectWidth="false"
allowClear
@select="selectAccount"
>
<div slot="dropdownRender" slot-scope="menu">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0" />
<div
v-if="isTenant"
style="padding: 4px 8px; cursor: pointer"
@mousedown="(e) => e.preventDefault()"
@click="addAccount"
>
<a-icon type="plus" /> 新增结算账户
</div>
</div>
<a-select-option v-for="(item, index) in accountList" :key="index" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
<a-tooltip title="多账户明细">
<a-button
type="default"
icon="folder"
style="margin-left: 8px"
size="small"
v-show="manyAccountBtnStatus"
@click="handleManyAccount"
/>
</a-tooltip>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="本次付款">
<a-input placeholder="请输入本次付款" v-decorator.trim="['changeAmount']" @keyup="onKeyUpChangeAmount" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="本次欠款"
data-step="10"
data-title="本次欠款"
data-intro="欠款产生的费用,后续可以在付款单进行支付"
>
<a-input placeholder="请输入本次欠款" v-decorator.trim="['debt']" :readOnly="true" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24"> </a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :lg="6" :md="12" :sm="24">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="附件"
data-step="11"
data-title="附件"
data-intro="可以上传与单据相关的图片、文档,支持多个文件"
>
<j-upload v-model="fileList" bizPath="bill"></j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
<many-account-modal ref="manyAccountModalForm" @ok="manyAccountModalFormOk"></many-account-modal>
<link-bill-list ref="linkBillList" @ok="linkBillListOk"></link-bill-list>
<vendor-modal ref="vendorModalForm" @ok="vendorModalFormOk"></vendor-modal>
<depot-modal ref="depotModalForm" @ok="depotModalFormOk"></depot-modal>
<account-modal ref="accountModalForm" @ok="accountModalFormOk"></account-modal>
<batch-set-depot ref="batchSetDepotModalForm" @ok="batchSetDepotModalFormOk"></batch-set-depot>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import ManyAccountModal from '../dialog/ManyAccountModal'
import LinkBillList from '../dialog/LinkBillList'
import VendorModal from '../../system/modules/VendorModal'
import DepotModal from '../../system/modules/DepotModal'
import AccountModal from '../../system/modules/AccountModal'
import BatchSetDepot from '../dialog/BatchSetDepot'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { JEditableTableMixin } from '@/mixins/JEditableTableMixin'
import { BillModalMixin } from '../mixins/BillModalMixin'
import { getMpListShort, changeListFmtMinus } from "@/utils/util"
import { getAction } from '@/api/manage'
import JUpload from '@/components/jeecg/JUpload'
import JDate from '@/components/jeecg/JDate'
import Vue from 'vue'
export default {
name: "PurchaseInModal",
mixins: [JEditableTableMixin, BillModalMixin],
components: {
ManyAccountModal,
LinkBillList,
VendorModal,
DepotModal,
AccountModal,
BatchSetDepot,
JUpload,
JDate,
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
},
data () {
return {
title:"操作",
width: '100%',
moreStatus: false,
//
addDefaultRowNum: 10,
visible: false,
operTimeStr: '',
prefixNo: 'CGRK',
fileList:[],
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
refKeys: ['materialDataTable', ],
activeKey: 'materialDataTable',
materialTable: {
loading: false,
dataSource: [],
columns: [
{ title: '仓库名称', key: 'depotId', width: '7%', type: FormTypes.select, placeholder: '请选择${title}', options: [],
allowSearch:true, validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '物料编码', key: 'barCode', width: '8%', type: FormTypes.popupJsh, kind: 'material', multi: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '名称', key: 'name', width: '20%', type: FormTypes.normal },
{ title: '规格', key: 'standard', width: '5%', type: FormTypes.normal },
{ title: '型号', key: 'model', width: '5%', type: FormTypes.normal },
{ title: '颜色', key: 'color', width: '5%', type: FormTypes.normal },
{ title: '扩展信息', key: 'materialOther', width: '5%', type: FormTypes.normal },
{ title: '库存', key: 'stock', width: '5%', type: FormTypes.inputNumber, readonly:true },
{ title: '单位', key: 'unit', width: '4%', type: FormTypes.normal },
{ title: '序列号', key: 'snList', width: '12%', type: FormTypes.input, placeholder: '多个序列号请用逗号隔开',
validateRules: [{ pattern: /^\S{1,100}$/, message: '请小于100位字符' }]
},
{ title: '批号', key: 'batchNumber', width: '5%', type: FormTypes.input },
{ title: '有效期', key: 'expirationDate',width: '7%', type: FormTypes.date },
{ title: '多属性', key: 'sku', width: '4%', type: FormTypes.normal },
{ title: '原数量', key: 'preNumber', width: '4%', type: FormTypes.normal },
{ title: '已入库', key: 'finishNumber', width: '4%', type: FormTypes.normal },
{ title: '数量', key: 'operNumber', width: '4%', type: FormTypes.inputNumber, statistics: true,
validateRules: [{ required: true, message: '${title}不能为空' }]
},
{ title: '单价', key: 'unitPrice', width: '4%', type: FormTypes.inputNumber, validateRules: [{ required: true, message: '${title}不能为空' }]},
{ title: '金额', key: 'allPrice', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '税率', key: 'taxRate', width: '3%', type: FormTypes.inputNumber,placeholder: '%'},
{ title: '税额', key: 'taxMoney', width: '5%', type: FormTypes.inputNumber, readonly: true, statistics: true },
{ title: '价税合计', key: 'taxLastMoney', width: '5%', type: FormTypes.inputNumber, statistics: true },
{ title: '备注', key: 'remark', width: '5%', type: FormTypes.input }
]
},
confirmLoading: false,
validatorRules:{
operTime:{
rules: [
{ required: true, message: '请输入单据日期!' }
]
},
organId:{
rules: [
{ required: true, message: '请选择供应商!' }
]
},
accountId:{
rules: [
{ required: true, message: '请选择结算账户!' }
]
}
},
url: {
add: '/depotHead/addDepotHeadAndDetail',
edit: '/depotHead/updateDepotHeadAndDetail',
detailList: '/depotItem/getDetailList'
}
}
},
created () {
},
methods: {
//edit()
editAfter() {
this.changeColumnHide()
this.changeFormTypes(this.materialTable.columns, 'snList', 0)
this.changeFormTypes(this.materialTable.columns, 'batchNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'expirationDate', 0)
this.changeFormTypes(this.materialTable.columns, 'preNumber', 0)
this.changeFormTypes(this.materialTable.columns, 'finishNumber', 0)
if (this.action === 'add') {
this.addInit(this.prefixNo)
this.fileList = []
} else {
this.model.operTime = this.model.operTimeStr
this.model.debt = (this.model.discountLastMoney + this.model.otherMoney - this.model.changeAmount).toFixed(2)
if(this.model.accountId == null) {
this.model.accountId = 0
this.manyAccountBtnStatus = true
this.accountIdList = this.model.accountIdList
this.accountMoneyList = this.model.accountMoneyList
} else {
this.manyAccountBtnStatus = false
}
this.fileList = this.model.fileName
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'organId', 'operTime', 'number', 'linkNumber', 'remark',
'discount','discountMoney','discountLastMoney','otherMoney','accountId','changeAmount','debt'))
});
//
let params = {
headerId: this.model.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
let url = this.readOnly ? this.url.detailList : this.url.detailList;
this.requestSubTableData(url, params, this.materialTable);
}
//-
if(this.action === 'copyAdd') {
this.model.id = ''
this.model.tenantId = ''
this.copyAddInit(this.prefixNo)
}
this.initSupplier()
this.initDepot()
this.initAccount()
},
//formData
classifyIntoFormData(allValues) {
let totalPrice = 0
let billMain = Object.assign(this.model, allValues.formValue)
let detailArr = allValues.tablesValue[0].values
billMain.type = '入库'
billMain.subType = '采购'
billMain.defaultNumber = billMain.number
for(let item of detailArr){
totalPrice += item.allPrice-0
}
billMain.totalPrice = 0-totalPrice
billMain.changeAmount = 0-billMain.changeAmount
if(billMain.accountId === 0) {
billMain.accountId = ''
}
this.accountMoneyList = changeListFmtMinus(this.accountMoneyList)
billMain.accountIdList = this.accountIdList.length>0 ? JSON.stringify(this.accountIdList) : ""
billMain.accountMoneyList = this.accountMoneyList.length>0 ? JSON.stringify(this.accountMoneyList) : ""
if(this.fileList && this.fileList.length > 0) {
billMain.fileName = this.fileList
}
if(this.model.id){
billMain.id = this.model.id
}
return {
info: billMain,
rows: detailArr,
}
},
onSearchLinkNumber() {
this.$refs.linkBillList.show('其它', '采购订单', '供应商', "1,3")
this.$refs.linkBillList.title = "选择采购订单"
},
linkBillListOk(selectBillRows) {
console.log(selectBillRows,'selectBillRows')
this.changeFormTypes(this.materialTable.columns, 'preNumber', 1)
this.changeFormTypes(this.materialTable.columns, 'finishNumber', 1)
if(selectBillRows && selectBillRows.length>0) {
let record = selectBillRows[0]
this.$nextTick(() => {
this.form.setFieldsValue({
'organId': record.organId,
'linkNumber': record.number,
'remark': record.remark,
'discountLastMoney': record.totalPrice,
'changeAmount': record.totalPrice,
'operTime': record.operTimeStr
})
});
//
let params = {
headerId: record.id,
mpList: getMpListShort(Vue.ls.get('materialPropertyList')) //
}
this.requestSubTableDataEx(this.url.detailList, params, this.materialTable);
}
},
/** 查询某个tab的数据,给明细里面的价税合计赋值 */
requestSubTableDataEx(url, params, tab, success) {
tab.loading = true
getAction(url, params).then(res => {
if(res && res.code === 200){
let list = res.data.rows
let listEx = []
let discountLastMoney = 0
for(let j=0; j<list.length; j++){
let info = list[j];
if(info.preNumber) {
info.operNumber = info.preNumber - info.finishNumber
// info.allPrice = info.allPrice;
// info.allPrice = info.operNumber * info.unitPrice-0;
discountLastMoney += info.allPrice
}
info.taxMoney = 0
info.taxLastMoney = info.allPrice
listEx.push(info)
this.changeColumnShow(info)
}
tab.dataSource = listEx
//
if(discountLastMoney) {
this.$nextTick(() => {
this.form.setFieldsValue({
'discountLastMoney': discountLastMoney,
'changeAmount': discountLastMoney
})
});
}
typeof success === 'function' ? success(res) : ''
}
}).finally(() => {
tab.loading = false
})
}
}
}
</script>
<style scoped>
</style>

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

Loading…
Cancel
Save