Browse Source

1.1补充

master
zkthink 5 years ago
parent
commit
a3299910d5
  1. 2
      package.json
  2. 6
      postcss.config.js
  3. 22
      src/api/AfterSale.js
  4. 78
      src/api/Application.js
  5. 74
      src/api/Area.js
  6. 68
      src/api/Attachment.js
  7. 47
      src/api/Dictionary.js
  8. 19
      src/api/Finance.js
  9. 58
      src/api/LoginLog.js
  10. 39
      src/api/Menu.js
  11. 85
      src/api/Msgs.js
  12. 68
      src/api/OptLog.js
  13. 78
      src/api/Org.js
  14. 79
      src/api/Parameter.js
  15. 47
      src/api/Resource.js
  16. 129
      src/api/Role.js
  17. 17
      src/api/SmsSendStatus.js
  18. 84
      src/api/SmsTask.js
  19. 89
      src/api/SmsTemplate.js
  20. 78
      src/api/Station.js
  21. 84
      src/api/SystemApi.js
  22. 57
      src/api/Tenant copy.js
  23. 135
      src/api/User.js
  24. BIN
      src/assets/404_images/404_image.jpeg
  25. 0
      src/assets/footer/fenlei2.png
  26. BIN
      src/assets/menu-left.png
  27. BIN
      src/assets/menu-right.png
  28. 44
      src/components/Management/Container.vue
  29. 4
      src/components/Management/checkDialog.vue
  30. 117
      src/components/ceres/CommonTree.vue
  31. 189
      src/components/ceres/Import.vue
  32. 368
      src/components/ceres/fileUpload.vue
  33. 343
      src/components/ceres/imgUpload.vue
  34. 12
      src/main.js
  35. 302
      src/router/index.js
  36. 290
      src/store/modules/account.js
  37. 99
      src/utils/utils.js
  38. 77
      src/views/after-sale-service/details/component/after-sales.vue
  39. 48
      src/views/after-sale-service/details/component/buyer.vue
  40. 87
      src/views/after-sale-service/details/component/logistics.vue
  41. 206
      src/views/after-sale-service/details/component/order-info.vue
  42. 46
      src/views/after-sale-service/details/component/store-info.vue
  43. 462
      src/views/after-sale-service/details/index.vue
  44. 233
      src/views/after-sale-service/index.vue
  45. 203
      src/views/ceres/auth/menu/Edit.vue
  46. 477
      src/views/ceres/auth/menu/Icons.vue
  47. 628
      src/views/ceres/auth/menu/Index.vue
  48. 274
      src/views/ceres/auth/role/Edit.vue
  49. 530
      src/views/ceres/auth/role/Index.vue
  50. 398
      src/views/ceres/auth/role/RoleAuthority.vue
  51. 179
      src/views/ceres/auth/role/UserRole.vue
  52. 393
      src/views/ceres/base/area/Index.vue
  53. 278
      src/views/ceres/base/dict/Dictionary.vue
  54. 394
      src/views/ceres/base/dict/DictionaryItem.vue
  55. 191
      src/views/ceres/base/dict/DictionaryItemEdit.vue
  56. 181
      src/views/ceres/base/dict/Edit.vue
  57. 62
      src/views/ceres/base/dict/Index.vue
  58. 184
      src/views/ceres/base/parameter/Edit.vue
  59. 418
      src/views/ceres/base/parameter/Index.vue
  60. 218
      src/views/ceres/developer/application/Edit.vue
  61. 394
      src/views/ceres/developer/application/Index.vue
  62. 71
      src/views/ceres/developer/db/Index.vue
  63. 433
      src/views/ceres/developer/loginLog/Index.vue
  64. 513
      src/views/ceres/developer/optLog/Index.vue
  65. 230
      src/views/ceres/developer/systemApi/Edit.vue
  66. 512
      src/views/ceres/developer/systemApi/Index.vue
  67. 127
      src/views/ceres/developer/systemApi/Scan.vue
  68. 188
      src/views/ceres/file/attachment/Edit.vue
  69. 435
      src/views/ceres/file/attachment/Index.vue
  70. 524
      src/views/ceres/msgs/myMsgs/Index.vue
  71. 412
      src/views/ceres/msgs/sendMsgs/Index.vue
  72. 507
      src/views/ceres/sms/manage/Edit.vue
  73. 431
      src/views/ceres/sms/manage/Index.vue
  74. 249
      src/views/ceres/sms/manage/SendStatusIndex.vue
  75. 238
      src/views/ceres/sms/template/Edit.vue
  76. 401
      src/views/ceres/sms/template/Index.vue
  77. 343
      src/views/ceres/user/org/Index.vue
  78. 232
      src/views/ceres/user/station/Edit.vue
  79. 453
      src/views/ceres/user/station/Index.vue
  80. 427
      src/views/ceres/user/user/Edit.vue
  81. 705
      src/views/ceres/user/user/Index.vue
  82. 222
      src/views/ceres/user/user/View.vue
  83. 235
      src/views/finance/overview/index.vue
  84. 88
      src/views/management/apply/detail.vue
  85. 2
      src/views/management/apply/dialog.vue
  86. 2
      src/views/management/apply/index.vue
  87. 11
      src/views/management/merchantList/index.vue
  88. 320
      src/views/menu/empower/index.vue

2
package.json

@ -65,10 +65,12 @@
"showdown": "1.9.0",
"sortablejs": "1.8.4",
"tui-editor": "1.3.3",
"v-viewer": "^1.5.1",
"vue": "2.6.10",
"vue-count-to": "^1.0.13",
"vue-i18n": "7.3.2",
"vue-loader": "^15.9.2",
"vue-photo-preview": "^1.1.3",
"vue-quill-editor": "^3.0.6",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.4",

6
postcss.config.js

@ -1,5 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
module.exports = {
plugins: {
autoprefixer: {}
}
}

22
src/api/AfterSale.js

@ -0,0 +1,22 @@
import axiosApi from './AxiosApi.js'
const apiList = {
getList: `/authority//order/findReturnInterventionList`,
getInfo: `/authority/order/getReturnInterventionDetail`
}
export default {
// 售后平台介入列表
getList(data) {
return axiosApi({
method: 'POST',
url: apiList.getList,
data
})
},
getInfo(id) {
return axiosApi({
method: 'GET',
url: `${apiList.getInfo}/${id}`
})
}
}

78
src/api/Application.js

@ -0,0 +1,78 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/application/page`
},
update: {
method: 'PUT',
url: `/authority/application`
},
save: {
method: 'POST',
url: `/authority/application`
},
delete: {
method: 'DELETE',
url: `/authority/application`
},
preview: {
method: 'POST',
url: `/authority/application/preview`
},
export: {
method: 'POST',
url: `/authority/application/export`
},
import: {
method: 'POST',
url: `/authority/application/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

74
src/api/Area.js

@ -0,0 +1,74 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/area/page`
},
update: {
method: 'PUT',
url: `/authority/area`
},
save: {
method: 'POST',
url: `/authority/area`
},
delete: {
method: 'DELETE',
url: `/authority/area`
},
query: {
method: 'POST',
url: `/authority/area/query`
},
tree: {
method: 'GET',
url: `/authority/area/tree`
}
}
export default {
page(data) {
return axiosApi({
...apiList.page,
data
})
},
tree(data) {
return axiosApi({
...apiList.tree,
data
})
},
query(data) {
return axiosApi({
...apiList.query,
data
})
},
save(data) {
return axiosApi({
...apiList.save,
data
})
},
update(data) {
return axiosApi({
...apiList.update,
data
})
},
delete(data) {
return axiosApi({
...apiList.delete,
data
})
},
check(code, id) {
return axiosApi({
method: 'GET',
url: `/authority/area/check/` + code,
data: { id: id }
})
}
}

68
src/api/Attachment.js

@ -0,0 +1,68 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/file/attachment/page`
},
upload: {
method: 'POST',
url: `/file/attachment/upload`
},
download: {
method: 'GET',
url: `/file/attachment/download`
},
downloadBiz: {
method: 'GET',
url: `/file/attachment/download/biz`
},
downloadUrl: {
method: 'GET',
url: `/file/attachment/download/url`
},
delete: {
method: 'DELETE',
url: `/file/attachment`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
upload (data) {
return axiosApi({
...apiList.upload,
data
})
},
download (data) {
return axiosApi({
...apiList.download,
responseType: "blob",
data
})
},
downloadBiz (data) {
return axiosApi({
...apiList.downloadBiz,
data
})
},
downloadUrl (data) {
return axiosApi({
...apiList.downloadUrl,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
}
}

47
src/api/Dictionary.js

@ -0,0 +1,47 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/dictionary/page`
},
update: {
method: 'PUT',
url: `/authority/dictionary`
},
save: {
method: 'POST',
url: `/authority/dictionary`
},
delete: {
method: 'DELETE',
url: `/authority/dictionary`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
}
}

19
src/api/Finance.js

@ -5,7 +5,9 @@ const apiList = {
getDetail: '/authority/cashOutDetail/audit/',
audiCash: '/authority/cashOutDetail/audit',
getDepositList: `/authority/depositJournal/page`, // 保证金列表
getDepositSum: `/authority/depositJournal/summary` // 保证金总金额
getDepositSum: `/authority/depositJournal/summary`, // 保证金总金额
getDayList: `/authority/journalDetail/platform-finance-page`,
getSummary: `/authority/journalDetail/platform-finance-summary`
}
export default {
// 提现审核列表
@ -45,5 +47,20 @@ export default {
method: 'POST',
url: apiList.getDepositSum
})
},
// 平台流水列表
getDayList(data) {
return axiosApi({
method: 'POST',
url: apiList.getDayList,
data
})
},
// 平台财务统计
getSummary() {
return axiosApi({
method: 'POST',
url: apiList.getSummary
})
}
}

58
src/api/LoginLog.js

@ -0,0 +1,58 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/loginLog/page`
},
delete: {
method: 'DELETE',
url: `/authority/loginLog`
},
clear: {
method: 'DELETE',
url: `/authority/loginLog/clear`
},
preview: {
method: 'POST',
url: `/authority/loginLog/preview`
},
export: {
method: 'POST',
url: `/authority/loginLog/export`
}
}
export default {
page(data) {
return axiosApi({
...apiList.page,
data
})
},
delete(data) {
return axiosApi({
...apiList.delete,
data
})
},
clear(data) {
return axiosApi({
...apiList.clear,
data
})
},
preview(data) {
return axiosApi({
...apiList.preview,
data
})
},
export(data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
}
}

39
src/api/Menu.js

@ -0,0 +1,39 @@
import axiosApi from './AxiosApi.js'
const apiList = {
getPlatformMenu: `/authority/menu/tree`,
getAllMerchanMenu: `/authority/menu/getAllTenantMenu`,
getSpecialMerchantMenu: `/authority/menu/getSpecifiedTenantMenu`,
saveMenu: `/authority/menu/syncMenu`
}
export default {
// 平台所有菜单
getPlatformMenu() {
return axiosApi({
method: 'GET',
url: apiList.getPlatformMenu
})
},
// 查询所有商家菜单
getAllMerchanMenu() {
return axiosApi({
method: 'GET',
url: apiList.getAllMerchanMenu
})
},
// 查询指定商家菜单
getSpecialMerchantMenu(id) {
return axiosApi({
method: 'GET',
url: `${apiList.getSpecialMerchantMenu}/${id}`
})
},
// 同步菜单
saveMenu(data) {
return axiosApi({
method: 'POST',
url: apiList.saveMenu,
data
})
}
}

85
src/api/Msgs.js

@ -0,0 +1,85 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/msgs/msgsCenterInfo/page`,
method: 'POST'
},
mark: {
url: `/msgs/msgsCenterInfo/mark`,
method: 'GET'
},
save: {
url: `/msgs/msgsCenterInfo`,
method: 'POST'
},
delete: {
url: `/msgs/msgsCenterInfo`,
method: 'DELETE'
},
preview: {
method: 'POST',
url: `/msgs/msgsCenterInfo/preview`
},
export: {
method: 'POST',
url: `/msgs/msgsCenterInfo/export`
},
import: {
method: 'POST',
url: `/msgs/msgsCenterInfo/import`
}
}
export default {
page (data, custom = {}) {
return axiosApi({
...apiList.page,
data,
custom
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
mark (data) {
return axiosApi({
...apiList.mark,
data
})
},
get (id) {
return axiosApi({
url: `/msgs/msgsCenterInfo/${id}`,
method: 'GET'
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

68
src/api/OptLog.js

@ -0,0 +1,68 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/optLog/page`
},
delete: {
method: 'DELETE',
url: `/authority/optLog`
},
clear: {
method: 'DELETE',
url: `/authority/optLog/clear`
},
preview: {
method: 'POST',
url: `/authority/optLog/preview`
},
export: {
method: 'POST',
url: `/authority/optLog/export`
},
import: {
method: 'POST',
url: `/authority/optLog/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
clear (data) {
return axiosApi({
...apiList.clear,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

78
src/api/Org.js

@ -0,0 +1,78 @@
import axiosApi from './AxiosApi.js'
const apiList = {
allTree: {
method: 'GET',
url: `/authority/org/tree`
},
save: {
method: 'POST',
url: `/authority/org`
},
update: {
method: 'PUT',
url: `/authority/org`
},
delete: {
method: 'DELETE',
url: `/authority/org`
},
preview: {
method: 'POST',
url: `/authority/org/preview`
},
export: {
method: 'POST',
url: `/authority/org/export`
},
import: {
method: 'POST',
url: `/authority/org/import`
}
}
export default {
allTree (data) {
return axiosApi({
...apiList.allTree,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

79
src/api/Parameter.js

@ -0,0 +1,79 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/parameter/page`
},
update: {
method: 'PUT',
url: `/authority/parameter`
},
save: {
method: 'POST',
url: `/authority/parameter`
},
delete: {
method: 'DELETE',
url: `/authority/parameter`
},
preview: {
method: 'POST',
url: `/authority/parameter/preview`
},
export: {
method: 'POST',
url: `/authority/parameter/export`
},
import: {
method: 'POST',
url: `/authority/parameter/import`
}
}
export default {
page (data, custom = {}) {
return axiosApi({
...apiList.page,
data,
custom
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

47
src/api/Resource.js

@ -0,0 +1,47 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/resource/page`
},
save: {
method: 'POST',
url: `/authority/resource`
},
update: {
method: 'PUT',
url: `/authority/resource`
},
delete: {
method: 'DELETE',
url: `/authority/resource`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
}
}

129
src/api/Role.js

@ -0,0 +1,129 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/authority/role/page`,
method: 'POST'
},
save: {
url: `/authority/role`,
method: 'POST'
},
update: {
url: `/authority/role`,
method: 'PUT'
},
delete: {
url: `/authority/role`,
method: 'DELETE'
},
saveUserRole: {
url: `/authority/role/user`,
method: 'POST'
},
saveRoleAuthority: {
url: `/authority/role/authority`,
method: 'POST'
},
preview: {
method: 'POST',
url: `/authority/user/preview`
},
export: {
method: 'POST',
url: `/authority/user/export`
},
import: {
method: 'POST',
url: `/authority/role/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
// formData: true,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
get (id) {
return axiosApi({
url: `/authority/role/${id}`,
method: 'GET'
})
},
getDetails (id) {
return axiosApi({
url: `/authority/role/details/${id}`,
method: 'GET'
})
},
check (code) {
return axiosApi({
url: `/authority/role/check/${code}`,
method: 'GET'
})
},
saveUserRole (data) {
return axiosApi({
...apiList.saveUserRole,
data
})
},
findUserIdByRoleId (roleId) {
return axiosApi({
url: `/authority/role/user/${roleId}`,
method: 'GET'
})
},
findAuthorityIdByRoleId (roleId) {
return axiosApi({
url: `/authority/role/authority/${roleId}`,
method: 'GET'
})
},
saveRoleAuthority (data) {
return axiosApi({
...apiList.saveRoleAuthority,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

17
src/api/SmsSendStatus.js

@ -0,0 +1,17 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/msgs/smsSendStatus/page`,
method: 'POST'
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
}
}

84
src/api/SmsTask.js

@ -0,0 +1,84 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/msgs/smsTask/page`,
method: 'POST'
},
save: {
url: `/msgs/smsTask`,
method: 'POST'
},
update: {
url: `/msgs/smsTask`,
method: 'PUT'
},
delete: {
url: `/msgs/smsTask`,
method: 'DELETE'
},
preview: {
method: 'POST',
url: `/msgs/smsTask/preview`
},
export: {
method: 'POST',
url: `/msgs/smsTask/export`
},
import: {
method: 'POST',
url: `/msgs/smsTask/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
get (id) {
return axiosApi({
url: `/msgs/smsTask/${id}`,
method: 'GET'
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

89
src/api/SmsTemplate.js

@ -0,0 +1,89 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/msgs/smsTemplate/page`,
method: 'POST'
},
save: {
url: `/msgs/smsTemplate`,
method: 'POST'
},
update: {
url: `/msgs/smsTemplate`,
method: 'PUT'
},
delete: {
url: `/msgs/smsTemplate`,
method: 'DELETE'
},
check: {
url: `/msgs/smsTemplate/check`,
method: 'GET'
},
preview: {
method: 'POST',
url: `/msgs/smsTemplate/preview`
},
export: {
method: 'POST',
url: `/msgs/smsTemplate/export`
},
import: {
method: 'POST',
url: `/msgs/smsTemplate/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
check (customCode) {
const data = { customCode: customCode }
return axiosApi({
...apiList.check,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

78
src/api/Station.js

@ -0,0 +1,78 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
url: `/authority/station/page`,
method: 'POST'
},
save: {
url: `/authority/station`,
method: 'POST'
},
update: {
url: `/authority/station`,
method: 'PUT'
},
delete: {
url: `/authority/station`,
method: 'DELETE'
},
preview: {
method: 'POST',
url: `/authority/station/preview`
},
export: {
method: 'POST',
url: `/authority/station/export`
},
import: {
method: 'POST',
url: `/authority/station/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

84
src/api/SystemApi.js

@ -0,0 +1,84 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/systemApi/page`
},
update: {
method: 'PUT',
url: `/authority/systemApi`
},
save: {
method: 'POST',
url: `/authority/systemApi`
},
delete: {
method: 'DELETE',
url: `/authority/systemApi`
},
preview: {
method: 'POST',
url: `/authority/systemApi/preview`
},
export: {
method: 'POST',
url: `/authority/systemApi/export`
},
import: {
method: 'POST',
url: `/authority/systemApi/import`
}
}
export default {
page(data) {
return axiosApi({
...apiList.page,
data
})
},
scan(serviceId) {
return axiosApi({
method: 'GET',
url: `/${serviceId}/systemApiScan`
})
},
save(data) {
return axiosApi({
...apiList.save,
data
})
},
update(data) {
return axiosApi({
...apiList.update,
data
})
},
delete(data) {
return axiosApi({
...apiList.delete,
data
})
},
preview(data) {
return axiosApi({
...apiList.preview,
data
})
},
export(data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import(data) {
return axiosApi({
...apiList.import,
data
})
}
}

57
src/api/Tenant copy.js

@ -0,0 +1,57 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/tenant/page`
},
update: {
method: 'PUT',
url: `/authority/tenant`
},
save: {
method: 'POST',
url: `/authority/tenant`
},
saveInit: {
method: 'POST',
url: `/authority/tenant/init`
},
remove: {
method: 'DELETE',
url: `/authority/tenant/remove`
}
}
export default {
page(data) {
return axiosApi({
...apiList.page,
data
})
},
save(data) {
return axiosApi({
...apiList.save,
data
})
},
saveInit(data) {
return axiosApi({
...apiList.saveInit,
data
})
},
update(data) {
return axiosApi({
...apiList.update,
data
})
},
remove(data) {
return axiosApi({
...apiList.remove,
data
})
}
}

135
src/api/User.js

@ -0,0 +1,135 @@
import axiosApi from './AxiosApi.js'
const apiList = {
page: {
method: 'POST',
url: `/authority/user/page`
},
save: {
method: 'POST',
url: `/authority/user`
},
update: {
method: 'PUT',
url: `/authority/user`
},
updateBaseInfo: {
method: 'PUT',
url: `/authority/user/base`
},
avatar: {
method: 'PUT',
url: `/authority/user/avatar`
},
delete: {
method: 'DELETE',
url: `/authority/user`
},
reset: {
method: 'GET',
url: `/authority/user/reset`
},
updatePassword: {
method: 'PUT',
url: `/authority/user/password`
},
reload: {
method: 'POST',
url: `/authority/user/reload`
},
preview: {
method: 'POST',
url: `/authority/user/preview`
},
export: {
method: 'POST',
url: `/authority/user/export`
},
import: {
method: 'POST',
url: `/authority/user/import`
}
}
export default {
page (data) {
return axiosApi({
...apiList.page,
data
})
},
save (data) {
return axiosApi({
...apiList.save,
data
})
},
update (data) {
return axiosApi({
...apiList.update,
data
})
},
updateBaseInfo (data) {
return axiosApi({
...apiList.updateBaseInfo,
data
})
},
updatePassword (data) {
return axiosApi({
...apiList.updatePassword,
data
})
},
delete (data) {
return axiosApi({
...apiList.delete,
data
})
},
get (id) {
return axiosApi({
method: 'GET',
url: `/authority/user/${id}`
})
},
reset (data) {
return axiosApi({
...apiList.reset,
data
})
},
avatar (data) {
return axiosApi({
...apiList.avatar,
data
})
},
reload (userId) {
return axiosApi({
...apiList.reload,
formData: true,
data: { userId: userId }
})
},
preview (data) {
return axiosApi({
...apiList.preview,
data
})
},
export (data) {
return axiosApi({
...apiList.export,
responseType: "blob",
data
})
},
import (data) {
return axiosApi({
...apiList.import,
data
})
}
}

BIN
src/assets/404_images/404_image.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
src/assets/footer/fenlei2.png

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/menu-left.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

BIN
src/assets/menu-right.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

44
src/components/Management/Container.vue

@ -1,6 +1,6 @@
<template>
<div class="management-container-component">
<Table :data="dataList" style="width: 100%">
<Table :data="dataList.records" style="width: 100%" border>
<table-column prop="name" label="店铺名称" />
<table-column prop="code" label="店铺编码" />
<table-column prop="duty" label="负责人" width="180" />
@ -37,6 +37,13 @@
</template>
</table-column>
</Table>
<pagination
v-show="dataList.total > 0"
:limit.sync="form.size"
:page.sync="form.current"
:total="Number(dataList.total)"
@pagination="fetch"
/>
<Dialog
:center="true"
title="查看商家"
@ -52,9 +59,10 @@
<script>
import Management from '@/api/Management.js'
import { Table, TableColumn, Dialog } from 'element-ui'
import Pagination from '@/components/Pagination'
import checkDialog from '@/components/Management/checkDialog'
export default {
components: { Table, TableColumn, Dialog, checkDialog },
components: { Table, TableColumn, Dialog, checkDialog, Pagination },
filters: {
state(code) {
const stateMap = {
@ -73,9 +81,9 @@ export default {
},
props: {
dataList: {
type: Array,
type: Object,
default() {
return []
return {}
}
}
},
@ -84,10 +92,17 @@ export default {
checkVisible: false,
checkData: {
status: {}
},
form: {
current: 1,
size: 10
}
}
},
methods: {
fetch() {
this.$emit('fetch', this.form)
},
checkRow(item) {
this.checkVisible = true
this.checkData = item
@ -148,4 +163,25 @@ export default {
.management-container-component {
padding: 0 20px;
}
/deep/ .el-table {
th {
background: #EEF3FF;
color:#333333;
font-size:16px;
font-weight: 400;
border-color: #E0E5EB;
text-align: center;
}
td {
font-size: 14px;
text-align: center;
color: #666666;
}
.cell {
text-align: center;
white-space: pre-line;/*保留换行符*/
}
}
</style>

4
src/components/Management/checkDialog.vue

@ -52,7 +52,7 @@
value-format="yyyy-MM-dd HH:mm:ss"
/>
</form-item>
<form-item label="生效时限" prop="expireLimitYear">
<form-item label="生效时限(年)" prop="expireLimitYear">
<Input
v-model="dataMap.expireLimitYear"
readonly
@ -70,7 +70,7 @@
</form-item>
<form-item label="账号" prop="mobile">
<Input
v-model="dataMap.mobile"
v-model="dataMap.account"
readonly
size="medium"
maxlength="11"

117
src/components/ceres/CommonTree.vue

@ -0,0 +1,117 @@
<template>
<div class="common-tree">
<el-tree
:ref="treeRef"
:data="treeData"
:check-strictly="checkStrictly"
show-checkbox
:accordion="false"
node-key="id"
default-expand-all
:highlight-current="true"
:expand-on-click-node="false"
:filter-node-method="filterNodeMethod"
@check-change="checkChange"
@node-click="nodeClick"
@current-change="currentChange"
>
<span
slot-scope="{ data, node }"
class="custom-tree-node"
>
<span style="margin-right: 15px;">{{ data.label }}</span>
<slot
:data="data"
:node="node"
></slot>
<!-- <template v-if='data.id !== "0"'> -->
<!-- <span class='ope-btn' title='添加' @click='modify("onAdd", data, node)'>
<i class='el-icon-plus' />
</span>
<span class='ope-btn' title='修改' @click='modify("onEdit", data, node)'>
<i class='el-icon-edit' />
</span>
<span
class='ope-btn ope-btn-remove'
title='删除'
@click='modify("onDelete", data, node)'
v-if='!data.children'
>
<i class='el-icon-delete' />
</span>-->
<!-- </template> -->
</span>
</el-tree>
</div>
</template>
<script>
export default {
props: {
treeRef: {
type: String,
default: "treeRef"
},
treeData: {
type: Array,
required: true,
default () {
return []
}
},
checkStrictly: {
type: Boolean,
default () {
return true
}
},
opeBtns: {
type: Array,
default () {
return ['add', 'edit', 'remove']
}
}
},
methods: {
modify (type, data, node) {
window.event.stopPropagation()
this.$emit(type, data, node)
},
checkChange (data, checked, childrenChecked) {
this.$emit('checkChange', data, checked, childrenChecked)
},
nodeClick (data, node, tree) {
this.$emit('nodeClick', data, node, tree)
},
currentChange (data, node) {
this.$emit('currentChange', data, node)
},
filterNodeMethod (value, data) {
// reutrn this.$emit('filterNodeMethod', value, data, node)
if (!value) return true
return data.label.indexOf(value) !== -1
}
}
}
</script>
<style lang="scss">
.common-tree {
.el-tree-node__content {
height: 28px;
.custom-tree-node {
font-size: 14px;
height: 28px;
line-height: 28px;
width: 100%;
.status-item {
top: 4px;
}
.tree-icon {
vertical-align: middle;
float: right;
margin-right: 10px;
box-sizing: border-box;
}
}
}
}
</style>

189
src/components/ceres/Import.vue

@ -0,0 +1,189 @@
<template>
<el-dialog
v-el-drag-dialog :close-on-click-modal="false" :title="title"
:type="type" :visible.sync="isVisible"
:width="width" top="50px" @dragDialog="handleDrag"
>
<el-form
ref="form" :model="model" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item label="文件" prop="fileLength">
<fileUpload
ref="fileRef" :accept-size="acceptSize" :auto-upload="false"
:limit="1" :accept="accept"
:action="action" @fileLengthVaild="fileLengthVaild" @setId="setIdAndSubmit"
>
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<div slot="tip" class="el-upload__tip">文件不超过100MB</div>
</fileUpload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button :disabled="disabled" plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import elDragDialog from '@/directive/el-drag-dialog'
import fileUpload from "@/components/ceres/fileUpload"
export default {
name: 'CommonImport',
directives: { elDragDialog, fileUpload },
components: { fileUpload },
props: {
action: {
type: String,
required: true
},
//
acceptSize: {
type: Number,
default: 50 * 1024 * 1024
},
accept: {
type: String,
default: '.xls,.xlsx'
},
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data() {
return {
model: this.initImport(),
screenWidth: 0,
width: this.initWidth(),
fileLength: 0,
disabled: false,
rules: {
fileLength: {
required: true, trigger: "change",
validator: (rule, value, callback) => {
const vm = this
if (vm.fileLength === 0) {
callback(new Error("请上传文件"))
} else if (vm.fileLength > 1) {
callback(new Error("一次性只能上传1个文件"))
} else {
callback()
}
}
}
}
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.$t('common.upload')
}
},
watch: {},
mounted() {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initImport() {
return {
id: '',
bizId: '',
bizType: '',
file: null,
isSingle: false
}
},
handleDrag() {
},
//
fileLengthVaild(data) {
const vm = this
vm.fileLength = data || 0
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
setModel(val) {
const vm = this
if (val) {
vm.model = { ...val }
}
},
close() {
this.$emit('close')
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.disabled = false
this.model = this.initImport()
this.$refs.fileRef.init({
id: ""
})
},
submitForm() {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
vm.disabled = true
vm.$refs.fileRef.submitFile(this.model.id, this.model.bizId, this.model.bizType)
},
setIdAndSubmit(isUploadCompleted, response) {
const vm = this
if (isUploadCompleted) {
vm.disabled = false
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
} else if (response && response['code'] === -9 && response['data']) {
//
// commonApi.export(this.exportErrorUrl, {failList: response['data']})
// .then(response => {
// downloadFile(response);
// });
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

368
src/components/ceres/fileUpload.vue

@ -0,0 +1,368 @@
<template>
<div>
<el-upload
:ref="uploadRef"
:accept="accept"
:action="action"
:auto-upload="autoUpload"
:before-remove="beforeRemove"
:class="isUpload === false ? 'hidebtn' : ''"
:data="fileOtherData"
:file-list="fileList"
:headers="headers"
:limit="limit"
:multiple="multiple"
:on-change="handleChange"
:on-error="handleError"
:on-exceed="handleExceed"
:on-preview="handlePreview"
:on-remove="handleRemove"
class="upload-demo"
>
<el-button
v-if="isUpload" size="small" type="primary"
>点击上传</el-button>
</el-upload>
</div>
</template>
<script>
import db from "@/utils/localstorage"
import commonApi from "@/api/Common.js"
import { copy } from '@/utils/utils'
import { Base64 } from 'js-base64'
export default {
name: "FileUpload",
props: {
uploadRef: {
type: String,
default: "file1"
},
//
multiple: {
type: Boolean,
default: true
},
//
autoUpload: {
type: Boolean,
default: true
},
//
isUpload: {
type: Boolean,
default: true
},
//
limit: {
type: Number,
default: null
},
//
accept: {
type: String,
default: ""
},
action: {
type: String,
default: `${process.env.VUE_APP_BASE_API}/file/attachment/upload`
},
//
acceptSize: {
type: Number,
default: null
},
//
fileOtherData: {
type: Object,
default: function() {
return {
id: null,
bizId: "",
bizType: "",
isSingle: false
}
}
}
},
data() {
return {
//
fileList: [],
//
removeFileAry: [],
//
addFileAry: [],
//
successNum: 0,
//
errorNum: 0,
//
uploadTotalNum: 0,
//
isUploadError: false
// action: `${process.env.VUE_APP_BASE_API}/file/attachment/upload`
}
},
computed: {
headers() {
return {
token: 'Bearer ' + db.get("TOKEN", ""),
tenant: db.get("TENANT", "") || "",
Authorization: `Basic ${Base64.encode(`${process.env.VUE_APP_CLIENT_ID}:${process.env.VUE_APP_CLIENT_SECRET}`)}`
}
}
},
methods: {
//
init({ id, bizId, bizType, isSingle, isDetail }) {
const vm = this
vm.fileOtherData.bizId = bizId
vm.fileOtherData.id = id || ""
vm.fileOtherData.bizType = bizType
vm.fileOtherData.isSingle = isSingle || false
vm.fileList.length = 0
vm.removeFileAry = []
vm.addFileAry = []
vm.$emit("fileLengthVaild", 0)
if (isDetail) {
vm.getAttachment()
}
vm.successNum = 0
vm.errorNum = 0
vm.uploadTotalNum = 0
vm.$refs[vm.uploadRef].clearFiles()
},
handleChange(file, fileList) {
const vm = this
if (file.response) {
vm.uploadTotalNum += 1
if (file.response.isSuccess) {
vm.fileOtherData.bizId = file.response.data.bizId
vm.successNum += 1
} else {
setTimeout(() => {
vm.$message({
message: file.name + "上传失败,原因:<br/>" + file.response.msg,
type: "error",
dangerouslyUseHTMLString: true,
showClose: true,
duration: 10000,
onClose: (msgs) => {
copy(msgs['message'])
vm.$message({
message: "复制错误消息成功",
type: "success"
})
}
})
}, 0)
vm.isUploadError = false
vm.errorNum += 1
}
vm.$emit("setId", vm.uploadTotalNum === fileList.length && vm.errorNum <= 0, file.response)
} else {
if (vm.acceptSize) {
const isLtAcceptSize = file.size > vm.acceptSize
if (isLtAcceptSize) {
setTimeout(() => {
vm.$message.error(
"只能上传" +
vm.renderSize(vm.acceptSize) +
"的文件!已为您过滤文件:" +
file.name
)
}, 10)
fileList.forEach((item, index) => {
if (item.uid === file.uid) {
fileList.splice(index, 1)
}
})
} else {
if (!vm.isUploadError) {
vm.addFileAry.push(file.name)
}
vm.isUploadError = false
}
} else {
if (!vm.isUploadError) {
vm.addFileAry.push(file.name)
}
vm.isUploadError = false
}
vm.$emit("fileLengthVaild", vm.fileList.length + vm.addFileAry.length)
}
vm.$store.state.hasLoading = false
},
//
handleError() {
const vm = this
vm.$message.error("附件上传失败,请重试")
vm.isUploadError = true
vm.$store.state.hasLoading = false
},
renderSize(value) {
if (value == null || value == "") {
return "0 B"
}
const unitArr = new Array(
"B",
"KB",
"MB",
"GB",
"TB",
"PB",
"EB",
"ZB",
"YB"
)
let index = 0
const srcsize = parseFloat(value)
index = Math.floor(Math.log(srcsize) / Math.log(1024))
let size = srcsize / Math.pow(1024, index)
size = size.toFixed(2)
if (unitArr[index]) {
return size + unitArr[index]
}
return "文件太大"
},
handlePreview(file) {
if (file.bizId) {
this.downLoadFile(file)
}
},
beforeRemove(file) {
return this.$confirm("确定移除" + file.name, "删除确认")
},
//
handleExceed() {
const vm = this
vm.$message("当前最多允许上传" + vm.limit + "个文件")
},
//
handleRemove(file) {
const vm = this
if (file.bizId) {
vm.removeFileAry.push(file.id)
vm.fileList.map((item, index) => {
if (item.name === file.name) {
vm.fileList.splice(index, 1)
return false
}
})
} else {
vm.addFileAry.map((item, index) => {
if (item === file.name) {
vm.addFileAry.splice(index, 1)
return false
}
})
}
vm.$emit("fileLengthVaild", vm.fileList.length + vm.addFileAry.length)
},
//
async deleteAttachment() {
const vm = this
const res = await commonApi.deleteAttachment({
ids: vm.removeFileAry
})
if (res.status === 200) {
if (res.data.code !== 0) {
vm.$message(res.data.msg)
} else {
vm.removeFileAry = []
}
}
},
//
async getAttachment() {
const vm = this
const res = await commonApi.getAttachment({
bizIds: vm.fileOtherData.bizId,
bizTypes: vm.fileOtherData.bizType
})
if (res.status === 200) {
if (res.data.code === 0) {
if (res.data.data.length > 0) {
const data = res.data.data[0].list
data.map(item => {
item.name = item.submittedFileName
})
vm.fileList = data
vm.$emit("fileLengthVaild", vm.fileList.length)
}
}
}
},
//
async getAttachmentByArr(bizIds, bizTypes) {
const vm = this
const res = await commonApi.getAttachment({
bizIds: bizIds,
bizTypes: bizTypes
})
if (res.status === 200) {
if (res.data.code === 0) {
if (res.data.data.length > 0) {
const data = res.data.data[0].list
data.map(item => {
item.name = item.submittedFileName
})
vm.fileList = data
}
}
}
},
//
handleBack() {
const vm = this
return {
addLength: vm.addFileAry.length,
removeLength: vm.removeFileAry.length
}
},
//
submitFile(id, bizId, bizType, tagId) {
const vm = this
vm.fileOtherData.id = id
if (bizId) {
vm.fileOtherData.bizId = bizId
vm.isUpload = true
}
if (tagId) {
vm.fileOtherData.tagId = tagId
}
vm.fileOtherData.bizType = bizType
if (vm.limitType(vm.$refs[vm.uploadRef].uploadFiles).length) {
return vm.$message.error('只能上传jpg/png/mp4格式的文件')
}
vm.$refs[vm.uploadRef].submit()
vm.addFileAry = []
},
//
async downLoadFile(data) {
const link = document.createElement("a")
link.href = data.url
link.download = data.name
link.click()
window.URL.revokeObjectURL(link.href)
},
//
limitType(arr) {
return arr.filter(item => {
const t = item.raw.type
return t.indexOf('image') === -1 && t.indexOf('video') === -1
})
}
}
}
</script>
<style>
.hidebtn .el-upload {
display: none;
}
</style>

343
src/components/ceres/imgUpload.vue

@ -0,0 +1,343 @@
<template>
<div>
<el-upload
:ref="uploadRef"
:accept="accept"
:action="action"
:auto-upload="autoUpload"
:before-upload="beforeUpload"
:class="{ limit: showUploadBtn }"
:data="fileOtherData"
:file-list="imgFileList"
:headers="headers"
:limit="limit"
:multiple="multiple"
:on-change="handleChange"
:on-error="handleError"
:on-exceed="handleExceed"
:on-remove="handleRemove"
:show-file-list="showFileList"
class="avatar-uploader"
list-type="picture-card"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img :src="dialogImageUrl" alt width="100%" />
</el-dialog>
</div>
</template>
<script>
import db from "@/utils/localstorage"
import commonApi from "@/api/Common.js"
import { Base64 } from 'js-base64'
export default {
props: {
uploadRef: {
type: String,
default: "file1"
},
//
multiple: {
type: Boolean,
default: false
},
//
autoUpload: {
type: Boolean,
default: false
},
//
showFileList: {
type: Boolean,
default: true
},
//
limit: {
type: Number,
default: null
},
//
accept: {
type: String,
default: "image/jpeg, image/gif, image/png, image/bmp"
},
//
acceptSize: {
type: Number,
default: null
},
//
fileOtherData: {
type: Object,
default: function() {
return {
bizId: "",
bizType: "",
isSingle: false
}
}
}
},
data() {
return {
imageUrl: "",
dialogImageUrl: "",
dialogVisible: false,
disabled: true,
//
imgFileList: [],
//
removeFileAry: [],
//
addFileAry: [],
//
isUploadError: false,
fileLength: 0,
action: `${process.env.VUE_APP_BASE_API}/file/attachment/upload`
}
},
computed: {
showUploadBtn() {
return (
this.showFileList &&
this.addFileAry.length + this.imgFileList.length === this.limit
)
},
headers() {
return {
token: 'Bearer ' + db.get("TOKEN", ""),
tenant: db.get("TENANT", "") || "",
Authorization: `Basic ${Base64.encode(`${process.env.VUE_APP_CLIENT_ID}:${process.env.VUE_APP_CLIENT_SECRET}`)}`
}
}
},
methods: {
//
init({ bizId, bizType, imageUrl, isSingle, isDetail }) {
const vm = this
vm.fileOtherData.bizId = bizId
vm.fileOtherData.bizType = bizType
vm.fileOtherData.isSingle = isSingle || false
// vm.imgFileList = []
vm.imgFileList.length = 0
vm.removeFileAry = []
vm.addFileAry = []
vm.imageUrl = imageUrl
vm.isUploadError = false
if (isDetail) {
vm.getAttachment()
}
},
//
beforeUpload() {
const vm = this
vm.$store.state.hasLoading = true
},
//
handleChange(file, fileList) {
const vm = this
if (file.response) {
if (file.response.isSuccess) {
const remoteFile = file.response.data
vm.fileOtherData.bizId = remoteFile.bizId
vm.imageUrl = remoteFile.url
vm.$emit("setId", remoteFile.bizId, remoteFile.url)
} else {
vm.$message.error(file.response.msg)
vm.isUploadError = false
}
} else {
if (vm.acceptSize) {
const isLtAcceptSize = file.size > vm.acceptSize
if (isLtAcceptSize) {
setTimeout(() => {
vm.$message.error(
"只能上传" +
vm.renderSize(vm.acceptSize) +
"的文件!已为您过滤文件:" +
file.name
)
}, 10)
fileList.forEach((item, index) => {
if (item.uid === file.uid) {
fileList.splice(index, 1)
}
})
} else {
if (!vm.isUploadError) {
vm.addFileAry.push(file.name)
}
vm.isUploadError = false
}
}
}
vm.$store.state.hasLoading = false
},
renderSize(value) {
if (value == null || value == "") {
return "0 B"
}
const unitArr = new Array(
"B",
"KB",
"MB",
"GB",
"TB",
"PB",
"EB",
"ZB",
"YB"
)
let index = 0
const srcsize = parseFloat(value)
index = Math.floor(Math.log(srcsize) / Math.log(1024))
let size = srcsize / Math.pow(1024, index)
size = size.toFixed(2)
if (unitArr[index]) {
return size + unitArr[index]
}
return "文件太大"
},
//
handleError() {
const vm = this
vm.$message.error("附件上传失败,请重试")
vm.isUploadError = true
vm.$store.state.hasLoading = false
if (!vm.showFileList) {
vm.imageUrl = ""
}
},
//
async getAttachment() {
const vm = this
const res = await commonApi.getAttachment({
bizIds: vm.fileOtherData.bizId,
bizTypes: vm.fileOtherData.bizType
})
if (res.status === 200) {
if (res.data.code === 0) {
if (res.data.data.length > 0) {
const data = res.data.data[0].list
data.map(item => {
item.name = item.submittedFileName
if (!vm.showFileList) {
vm.imageUrl = item.url
}
})
vm.imgFileList = data
vm.$emit("fileLengthVaild", vm.imgFileList.length)
}
}
}
},
//
handleRemove(file) {
const vm = this
if (file.bizId) {
vm.removeFileAry.push(file.id)
vm.imgFileList.map((item, index) => {
if (item.bizId === file.bizId) {
vm.imgFileList.splice(index, 1)
return false
}
})
} else {
vm.addFileAry.map((item, index) => {
if (item === file.name) {
vm.addFileAry.splice(index, 1)
return false
}
})
}
},
//
handleExceed() {
const vm = this
vm.$message("当前最多允许上传" + vm.limit + "张图片")
},
//
handleBack() {
const vm = this
return {
addLength: vm.addFileAry.length,
removeLength: vm.removeFileAry.length
}
},
//
async deleteAttachment() {
const vm = this
const res = await commonApi.deleteAttachment({
ids: vm.removeFileAry
})
if (res.status === 200) {
if (res.data.code !== 0) {
vm.$message(res.data.msg)
}
}
},
//
submitFile(bizId, bizType, isSingle) {
const vm = this
vm.fileOtherData.bizId = bizId
vm.fileOtherData.bizType = bizType
vm.fileOtherData.isSingle = isSingle
if (!vm.showFileList) {
const length = vm.$refs[vm.uploadRef].uploadFiles.length - 1
vm.$refs[vm.uploadRef].uploadFiles = [
vm.$refs[vm.uploadRef].uploadFiles[length]
]
vm.imgFileList.map(item => {
vm.removeFileAry.push(item.id)
})
if (vm.imgFileList.length > 0) {
vm.deleteAttachment()
}
}
vm.$refs[vm.uploadRef].submit()
vm.$store.state.hasLoading = false
vm.addFileAry = []
},
//
async deleteAttachmentByIds(ids) {
const vm = this
const res = await commonApi.deleteAttachment({
ids: ids
})
if (res.status === 200) {
if (res.data.code !== 0) {
vm.$message(res.data.msg)
} else {
vm.removeFileAry = []
}
}
}
}
}
</script>
<style lang="scss" scoped>
.avatar {
width: 100%;
height: 100%;
}
/deep/.el-form-item__content {
line-height: 0;
}
/deep/.el-upload-list--picture-card .el-upload-list__item {
margin: 0 8px 0 0;
}
/deep/.el-upload--picture-card,
/deep/.el-upload-list--picture-card .el-upload-list__item {
width: 128px;
height: 128px;
}
.limit {
/deep/.el-upload--picture-card {
display: none;
}
}
</style>

12
src/main.js

@ -3,6 +3,10 @@ import 'dragula/dist/dragula.min.css'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import './styles/element-variables.scss'
import Viewer from 'v-viewer'
import 'viewerjs/dist/viewer.css'
import preview from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css'
// vue-quill-editor css
import 'quill/dist/quill.core.css'
@ -33,6 +37,14 @@ Plugins.map((plugin) => {
Vue.use(plugin)
})
Vue.use(preview)
Vue.use(Viewer)
Viewer.setDefaults({
Options: { "inline": true, "button": true, "navbar": true, "title": true, "toolbar": true,
"tooltip": true, "movable": true, "zoomable": true, "rotatable": true, "scalable": true, "transition": true,
"fullscreen": true, "keyboard": true, "url": "data-source" }
})
Vue.use(Element, {
i18n: (key, value) => i18n.t(key, value)
})

302
src/router/index.js

@ -149,10 +149,56 @@ const constRouter = [
title: '保证金',
affix: true
}
},
{
path: '/finance/overview',
component: () => import('@/views/finance/overview/index'),
meta: {
title: '财务概况',
affix: true
}
}
]
},
{
path: '/after-sale-service',
component: Layout,
name: '售后处理',
children: [
{
path: '/after-sale-service',
component: () => import('@/views/after-sale-service/index'),
meta: {
title: '售后处理',
affix: true
}
},
{
path: '/after-sale-service/details',
component: () => import('@/views/after-sale-service/details/index'),
meta: {
title: '售后详情',
affix: true
}
}
]
},
{
path: '/menu',
component: Layout,
name: '菜单',
children: [
{
path: '/menu/empower',
component: () => import('@/views/menu/empower/index'),
meta: {
title: '菜单赋权',
affix: true
}
}
]
},
{
path: '/profile',
component: Layout,
@ -184,6 +230,262 @@ const constRouter = [
meta: { title: 'page404', noCache: true }
}
]
}, {
"sortValue": 1,
children: [{
"sortValue": 0,
path: "/user/org",
name: "组织管理",
component: resolve => require(['@/views/ceres/user/org/Index'], resolve),
meta: {
"title": "组织管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 1,
path: "/user/station",
name: "岗位管理",
component: resolve => require(['@/views/ceres/user/station/Index'], resolve),
meta: {
"title": "岗位管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
path: "/user/user",
name: "用户管理",
component: resolve => require(['@/views/ceres/user/user/Index'], resolve),
meta: {
"title": "用户管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
path: "/user",
name: "用户中心",
component: Layout,
meta: {
"title": "用户中心",
"icon": "el-icon-user-solid",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 10,
children: [{
"sortValue": 1,
path: "/auth/role",
name: "角色管理",
component: resolve => require(['@/views/ceres/auth/role/Index'], resolve),
meta: {
"title": "角色管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
path: "/auth/user",
name: "菜单配置",
component: resolve => require(['@/views/ceres/auth/menu/Index'], resolve),
meta: {
"title": "菜单配置",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
path: "/auth",
name: "权限管理",
component: Layout,
meta: {
"title": "权限管理",
"icon": "el-icon-lock",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 12,
children: [{
"sortValue": 1,
path: "/developer/application",
name: "应用管理",
component: resolve => require(['@/views/ceres/developer/application/Index'], resolve),
meta: {
"title": "应用管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
path: "/developer/systemApi",
name: "接口查询",
component: resolve => require(['@/views/ceres/developer/systemApi/Index'], resolve),
meta: {
"title": "接口查询",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 3,
path: "/developer/optLog",
name: "操作日志",
component: resolve => require(['@/views/ceres/developer/optLog/Index'], resolve),
meta: {
"title": "操作日志",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 4,
path: "/developer/loginLog",
name: "登录日志",
component: resolve => require(['@/views/ceres/developer/loginLog/Index'], resolve),
meta: {
"title": "登录日志",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 5,
path: "/developer/db",
name: "数据库监控",
component: resolve => require(['@/views/ceres/developer/db/Index'], resolve),
meta: {
"title": "数据库监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 6,
path: "http://127.0.0.1:8760/api/gate/doc.html",
name: "接口文档",
component: Layout,
meta: {
"title": "接口文档",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 7,
path: "http://127.0.0.1:8848/nacos",
name: "注册&配置中心",
component: Layout,
meta: {
"title": "注册&配置中心",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 8,
path: "http://www.baidu.com",
name: "缓存监控",
component: Layout,
meta: {
"title": "缓存监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 10,
path: "http://127.0.0.1:8767/ceres-jobs-server",
name: "定时调度中心",
component: Layout,
meta: {
"title": "定时调度中心",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 11,
path: "http://127.0.0.1:8772/zipkin",
name: "链路调用监控",
component: Layout,
meta: {
"title": "链路调用监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
path: "/developer",
name: "开发者管理",
component: Layout,
meta: {
"title": "开发者管理",
"icon": "el-icon-user-solid",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 13,
children: [{
"sortValue": 1,
path: "/msgs/sendMsgs",
name: "消息推送",
component: resolve => require(['@/views/ceres/msgs/sendMsgs/Index'], resolve),
meta: {
"title": "消息推送",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
path: "/msgs/myMsgs",
name: "我的消息",
component: resolve => require(['@/views/ceres/msgs/myMsgs/Index'], resolve),
meta: {
"title": "我的消息",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
path: "/msgs",
name: "消息中心",
component: Layout,
meta: {
"title": "消息中心",
"icon": "el-icon-chat-line-square",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}
]

290
src/store/modules/account.js

@ -93,8 +93,298 @@ export default {
title: '保证金',
icon: 'el-icon-money'
}
},
{
path: '/finance/overview',
name: '财务概况',
meta: {
title: '财务概况',
icon: 'el-icon-pie-chart'
}
}
]
},
{
path: '/after-sale-service',
meta: {
title: '售后处理',
icon: 'el-icon-service'
},
children: []
},
{
path: '/menu',
meta: {
title: '菜单',
icon: 'el-icon-menu'
},
children: [
{
path: '/menu/empower',
name: '菜单赋权',
meta: {
title: '菜单赋权',
icon: ''
}
}
]
},
{
"sortValue": 1,
"children": [{
"sortValue": 0,
"path": "/user/org",
"name": "组织管理",
"component": "ceres/user/org/Index",
"meta": {
"title": "组织管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 1,
"path": "/user/station",
"name": "岗位管理",
"component": "ceres/user/station/Index",
"meta": {
"title": "岗位管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
"path": "/user/user",
"name": "用户管理",
"component": "ceres/user/user/Index",
"meta": {
"title": "用户管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
"path": "/user",
"name": "用户中心",
"component": "Layout",
"meta": {
"title": "用户中心",
"icon": "el-icon-user-solid",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 10,
"children": [{
"sortValue": 1,
"path": "/auth/role",
"name": "角色管理",
"component": "ceres/auth/role/Index",
"meta": {
"title": "角色管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
"path": "/auth/user",
"name": "菜单配置",
"component": "ceres/auth/menu/Index",
"meta": {
"title": "菜单配置",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
"path": "/auth",
"name": "权限管理",
"component": "Layout",
"meta": {
"title": "权限管理",
"icon": "el-icon-lock",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 12,
"children": [{
"sortValue": 1,
"path": "/developer/application",
"name": "应用管理",
"component": "ceres/developer/application/Index",
"meta": {
"title": "应用管理",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
"path": "/developer/systemApi",
"name": "接口查询",
"component": "ceres/developer/systemApi/Index",
"meta": {
"title": "接口查询",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 3,
"path": "/developer/optLog",
"name": "操作日志",
"component": "ceres/developer/optLog/Index",
"meta": {
"title": "操作日志",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 4,
"path": "/developer/loginLog",
"name": "登录日志",
"component": "ceres/developer/loginLog/Index",
"meta": {
"title": "登录日志",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 5,
"path": "/developer/db",
"name": "数据库监控",
"component": "ceres/developer/db/Index",
"meta": {
"title": "数据库监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 6,
"path": "http://127.0.0.1:8760/api/gate/doc.html",
"name": "接口文档",
"component": "Layout",
"meta": {
"title": "接口文档",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 7,
"path": "http://127.0.0.1:8848/nacos",
"name": "注册&配置中心",
"component": "Layout",
"meta": {
"title": "注册&配置中心",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 8,
"path": "http://www.baidu.com",
"name": "缓存监控",
"component": "Layout",
"meta": {
"title": "缓存监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 10,
"path": "http://127.0.0.1:8767/ceres-jobs-server",
"name": "定时调度中心",
"component": "Layout",
"meta": {
"title": "定时调度中心",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 11,
"path": "http://127.0.0.1:8772/zipkin",
"name": "链路调用监控",
"component": "Layout",
"meta": {
"title": "链路调用监控",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
"path": "/developer",
"name": "开发者管理",
"component": "Layout",
"meta": {
"title": "开发者管理",
"icon": "el-icon-user-solid",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}, {
"sortValue": 13,
"children": [{
"sortValue": 1,
"path": "/msgs/sendMsgs",
"name": "消息推送",
"component": "ceres/msgs/sendMsgs/Index",
"meta": {
"title": "消息推送",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}, {
"sortValue": 2,
"path": "/msgs/myMsgs",
"name": "我的消息",
"component": "ceres/msgs/myMsgs/Index",
"meta": {
"title": "我的消息",
"icon": "",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": false
}],
"path": "/msgs",
"name": "消息中心",
"component": "Layout",
"meta": {
"title": "消息中心",
"icon": "el-icon-chat-line-square",
"breadcrumb": true
},
"hidden": false,
"alwaysShow": true
}
]
},

99
src/utils/utils.js

@ -0,0 +1,99 @@
/**
* 判断终端以及浏览器
* userAgent string User-Agent信息
*/
export const readUserAgent = ua => {
const data = {
terminal: '',
browser: '',
terminalType: {}
}
data.terminalType = {
trident: ua.indexOf('Trident') > -1, // IE内核
presto: ua.indexOf('Presto') > -1, // opera内核
webKit: ua.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核
gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, // 火狐内核
mobile: !!ua.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
ios: !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
android: ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1, // android终端
iPhone: ua.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器
iPad: ua.indexOf('iPad') > -1, // 是否iPad
webApp: ua.indexOf('Safari') === -1, // 是否web应该程序,没有头部与底部
weixin: ua.indexOf('MicroMessenger') > -1, // 是否微信 (2015-01-22新增)
qq: ua.match(/\sQQ/i) === ' qq' // 是否QQ
}
if (
data.terminalType.ios ||
data.terminalType.iPhone ||
data.terminalType.iPad
) {
data.terminal = '苹果'
} else if (data.terminalType.android) {
data.terminal = '安卓'
} else {
data.terminal = 'PC'
}
if (/msie/i.test(ua) && !/opera/.test(ua)) {
data.browser = 'IE'
} else if (/firefox/i.test(ua)) {
data.browser = 'Firefox'
} else if (/chrome/i.test(ua) && /webkit/i.test(ua) && /mozilla/i.test(ua)) {
data.browser = 'Chrome'
} else if (/opera/i.test(ua)) {
data.browser = 'Opera'
} else if (/iPad/i.test(ua)) {
data.browser = 'iPad'
} else if (
/webkit/i.test(ua) &&
!(/chrome/i.test(ua) && /webkit/i.test(ua) && /mozilla/i.test(ua))
) {
data.browser = 'Safari'
} else {
data.browser = '未知'
}
return data
}
// 格式化文件大小 单位:B、KB、MB、GB
const renderSize = value => {
if (value == null || value == '') {
return "0 B"
}
var unitArr = new Array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
var index = 0
var srcsize = parseFloat(value)
index = Math.floor(Math.log(srcsize) / Math.log(1024))
var size = srcsize / Math.pow(1024, index)
size = size.toFixed(2)
if (unitArr[index]) {
return size + unitArr[index]
}
return '文件太大'
}
const convertEnum = obj => {
const list = []
if (obj) {
for (const key in obj) {
list.push({
text: obj[key],
value: key
})
}
}
return list
}
const copy = msg => {
if (msg) {
const oInput = document.createElement('input') // 创建一个隐藏input(重要!)
oInput.value = msg // 赋值
document.body.appendChild(oInput)
oInput.select() // 选择对象
document.execCommand("Copy") // 执行浏览器复制命令
oInput.className = 'oInput'
oInput.style.display = 'none'
}
}
export { renderSize, convertEnum, copy }

77
src/views/after-sale-service/details/component/after-sales.vue

@ -0,0 +1,77 @@
<template>
<div>
<div v-if="dialog.allow" class="allow">
<h1>建议联系商家由商家操作同意退款</h1>
<p>
若你同意售后客户申请的类型为仅退款时将自动从商家账户退款给买家 若客户申请的类型为退款退货时本售后将转为待买家退回物品的状态有可能被商家拒收
</p>
</div>
<div v-else class="refuse">
<p>拒绝售后本售后将自动关闭但买家有权再次发起</p>
</div>
<el-form label-width="120px" :model="form">
<el-form-item label="备注内容">
<el-input v-model="form.remark" type="textarea" placeholder="请填写相关内容" />
</el-form-item>
</el-form>
<div class="btn_list">
<el-button type="primary">{{ dialog.btnName }}</el-button>
<el-button @click="cancel">取消</el-button>
</div>
</div>
</template>
<script>
export default {
props: {
dialog: {
type: Object,
default: () => {}
}
},
data() {
return {
form: {
remark: ''
}
}
},
methods: {
cancel() {
this.$emit('close')
}
}
}
</script>
<style lang='less' scoped>
h1 {
font-size:24px;
font-weight:500;
color:rgba(51,51,51,1);
text-align: center;
width: 50%;
margin: 30px auto;
}
p {
font-size:16px;
color:rgba(51,51,51,1);
text-align: center;
width: 50%;
margin: 30px auto;
}
.btn_list {
width: 200px;
margin: 20px auto;
}
/deep/ .el-textarea {
width: 100%;
textarea {
min-height: 150px !important;
}
}
</style>

48
src/views/after-sale-service/details/component/buyer.vue

@ -0,0 +1,48 @@
<template>
<div class="store_info_page">
<p v-for="(item,index) in storeInfo" :key="index">
<span>{{ item.name }}:</span>
<span>{{ item.value }}</span>
</p>
</div>
</template>
<script>
export default {
data() {
return {
storeInfo: [
{ name: '下单账户', value: '和理智上', field: '' },
{ name: '注册时间', value: '2020-06-30', field: '' },
{ name: '手机号', value: '13966781258', field: '' },
{ name: '订单总数', value: '12个', field: '' },
{ name: '售后单数', value: '5个', field: '' },
{ name: '售后成功率', value: '68%', field: '' }
]
}
}
}
</script>
<style lang='less' scoped>
.store_info_page {
padding: 20px 10%;
overflow: hidden;
p {
width: 50%;
float: left;
span {
font-size: 16px;
color: #333333;
display: inline-block;
&:nth-of-type(1) {
width:130px;
}
&:nth-of-type(2) {
color:#666666;
}
}
}
}
</style>

87
src/views/after-sale-service/details/component/logistics.vue

@ -0,0 +1,87 @@
<template>
<div class="logistics_info">
<div class="logistics_com">
<p v-for="(item,index) in list" :key="index">
<span>{{ item.name }}:</span>
<span>{{ item.value }}</span>
</p>
</div>
<div class="logistics_status">
<div>物流信息:</div>
<div>
<p v-for="(item,index) in logisticsList" :key="index">
<span>{{ item.time }}</span>
<span>{{ item.status }}</span>
</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{ name: '物流公司', value: '中通', field: '' },
{ name: '运单号', value: '123123', field: '' }
],
logisticsList: [
{ time: '2020/12/20 15:20:23', status: '正在派件', field: '' },
{ time: '2020/12/20 15:20:23', status: '已从廊坊发货', field: '' },
{ time: ' 2020/12/20 15:20:23', status: '已到达目的地', field: '' }
]
}
}
}
</script>
<style lang='less' scoped>
.logistics_info {
padding: 20px 10%;
.logistics_com {
overflow: hidden;
margin-bottom: 30px;
p {
width: 50%;
float: left;
span {
font-size: 16px;
color: #333333;
display: inline-block;
&:nth-of-type(1) {
width:80px;
}
&:nth-of-type(2) {
color:#666666;
}
}
}
}
.logistics_status {
overflow: hidden;
div {
font-size: 16px;
color: #333333;
float: left;
&:nth-of-type(1) {
width:80px;
}
&:nth-of-type(2) {
width: calc(80% - 80px);
color:#666666;
p {
margin: 0;
margin-bottom: 20px;
span {
display: inline-block;
&:nth-of-type(2) {
margin-left: 50px;
}
}
}
}
}
}
}
</style>

206
src/views/after-sale-service/details/component/order-info.vue

@ -0,0 +1,206 @@
<template>
<div class="order_info_page">
<div class="order_info">
<p v-for="(item,index) in orderInfo" :key="index">
<span>{{ item.name }}:</span>
<span>{{ item.value }}</span>
</p>
</div>
<div class="detail_title">
商品信息
</div>
<div
v-for="(item,index) in shopList"
:key="index"
class="goods_list"
>
<div class="good_details">
<ul>
<li>
<img :src="item.skuImg" />
</li>
<li>
<p>{{ item.productName }}</p>
<p>{{ item.skuNameStr }}</p>
<p>SKU: {{ item.skuId }}</p>
</li>
<li>
<p>¥{{ item.sellPrice*item.buyNum }}</p>
<p>¥{{ `${item.sellPrice}*${item.buyNum}` }}</p>
</li>
<li> ¥{{ item.salePrice * item.buyNum }}</li>
</ul>
</div>
</div>
<div class="total">
<ul>
<li></li>
<li></li>
<li> </li>
<li>
<p><span>订单总金额</span> <span>¥240</span> </p>
<p><span>物流费用</span> <span>¥40</span></p>
<p><span>平台优惠(618活动)</span> <span>-¥100</span></p>
<p><span>实付</span> <span>¥180</span></p>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
orderInfo: [
{ name: '订单ID', value: '542543543', field: '' },
{ name: '支付单号', value: '12353563', field: '' },
{ name: '支付状态', value: '待发货', field: '' },
{ name: '售后状态', value: '售后中', field: '' }
],
shopList: [
{ skuImg: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg',
productName: 'DAD 气垫霜', skuNameStr: '粉色, 中瓶', skuId: '2525', sellPrice: '60', buyNum: '2', salePrice: '50' },
{ skuImg: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg',
productName: 'DAD 气垫霜', skuNameStr: '粉色, 中瓶', skuId: '2525', sellPrice: '60', buyNum: '2', salePrice: '50' }
],
recordList: [
{ title: '申请平台介入', name: '刘十', time: '2020-05-08 15:30:56', type: '问题描述', value: '不退款', img: '举证图片',
imgSrc: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg' },
{ title: '拒绝退款', name: '王辉', time: '2020-04-08 17:30:20', type: '卖家留言', value: '不退款', img: '', imgSrc: '' },
{ title: '发起了售后', name: '张强', time: '2020-03-25 13:25:45', type: '买家留言', value: '不要了, 买到的宝贝不符合预期', img: '', imgSrc: '' }
],
total: 200
}
}
}
</script>
<style lang='less' scoped>
.order_info_page {
.order_info {
overflow: hidden;
p {
width: 50%;
float: left;
span {
font-size: 16px;
color: #333333;
display: inline-block;
&:nth-of-type(1) {
width:130px;
}
&:nth-of-type(2) {
color:#666666;
}
}
}
}
.detail_title {
font-size: 24px;
color: #333333;
position: relative;
margin:50px 20px 20px;
&:before {
content: '';
display: block;
position: absolute;
top: 5px;
left: -20px;
width: 4px;
height: 24px;
background-color: #3A68F2;
}
}
.goods_list {
.good_details {
border-bottom: 1px #E0E5EB solid;
ul {
overflow: hidden;
display: flex;
margin: 0;
padding: 30px 0;
list-style: none;
li {
flex:3;
display: flex;
// justify-content: center;
// align-items: center;
text-align: left;
img {
width: 90px;
height: 90px;
border-radius:4px;
font-size: 16px;
color: #333333;
}
&:nth-child(2),&:nth-child(3) {
display: block;
}
&:nth-child(3) {
p {
// text-align: center;
&:nth-child(1) {
font-weight: 600;
}
&:nth-child(2) {
font-size: 12px;
}
}
}
&:nth-child(4) {
font-weight: 600;
}
p {
margin: 0;
padding: 0;
height: 30px;
line-height: 30px;
}
}
}
}
}
.total {
padding-left: 20px;
ul {
margin: 0;
padding: 30px 0;
display: flex;
list-style: none;
li {
// height: 40px;
flex:3;
font-size: 16px;
color: #333333;
// justify-content: center;
// align-items: center;
&:nth-child(4) {
// font-weight: 600;
}
p {
margin: 0;
padding: 0;
height: 30px;
line-height: 30px;
width: 100%;
clear: both;
span {
display: inline-block;
float: left;
&:nth-of-type(1) {
width: 40%;
margin-right: 10%;
}
&:nth-of-type(2) {
width: 50%;
font-weight: 600;
}
}
}
}
}
}
}
</style>

46
src/views/after-sale-service/details/component/store-info.vue

@ -0,0 +1,46 @@
<template>
<div class="store_info_page">
<p v-for="(item,index) in storeInfo" :key="index">
<span>{{ item.name }}:</span>
<span>{{ item.value }}</span>
</p>
</div>
</template>
<script>
export default {
data() {
return {
storeInfo: [
{ name: '店铺名称', value: '江山的店铺', field: '' },
{ name: '合同有效期', value: '2020-06-30──2022-07-23', field: '' },
{ name: '负责人', value: '江山', field: '' },
{ name: '联系电话', value: '0755-56894556', field: '' },
{ name: '联系地址', value: '广东省深圳市南山区科技园', field: '' }
]
}
}
}
</script>
<style lang='less' scoped>
.store_info_page {
padding: 20px 10%;
overflow: hidden;
p {
width: 50%;
float: left;
span {
font-size: 16px;
color: #333333;
display: inline-block;
&:nth-of-type(1) {
width:130px;
}
&:nth-of-type(2) {
color:#666666;
}
}
}
}
</style>

462
src/views/after-sale-service/details/index.vue

@ -0,0 +1,462 @@
<template>
<div class="detail_page">
<div class="content">
<div class="head_box">
售后详情
<div class="btn_list">
<el-button @click="refuse">拒绝售后</el-button>
<el-button type="primary" @click="handle">同意售后</el-button>
</div>
</div>
<div class="detail">
<div class="order_info">
<p class="detail_title">订单信息</p>
<ul class="order_list">
<li v-for="(item,index) in orderInfo" :key="index">
<p>{{ item.name }}:</p>
<p :class="[{active: item.type ===1 || item.type ===2 || item.type ===4}]">
<span @click="getInfo(item)">{{ item.value }}</span>
<span v-if="item.type ===3" class="active" @click="getInfo(item)">
查看物流
</span>
</p>
</li>
</ul>
</div>
<div class="after_sale_shop">
<p class="detail_title">售后商品</p>
<div
v-for="(item,index) in shopList"
:key="index"
class="goods_list"
>
<div class="good_details">
<ul>
<li>
<img :src="item.skuImg" />
</li>
<li>
<p>{{ item.productName }}</p>
<p>{{ item.skuNameStr }}</p>
<p>SKU: {{ item.skuId }}</p>
</li>
<li>
<p>¥{{ item.sellPrice*item.buyNum }}</p>
<p>¥{{ `${item.sellPrice}*${item.buyNum}` }}</p>
</li>
<li> ¥{{ item.applyPrice * item.buyNum }}</li>
</ul>
</div>
</div>
<div class="total">
<ul>
<li></li>
<li></li>
<li>退款总金额 </li>
<li>¥{{ total }}</li>
</ul>
</div>
</div>
</div>
<div class="after_sales_record">
<p class="detail_title">协商历史</p>
<div v-for="(item,index) in recordList" :key="index" class="record_list">
<div class="record_list_title">
<p>{{ item.title }}</p>
<p>{{ item.name }}</p>
<p>{{ item.createTime }}</p>
</div>
<div class="record_list_content">
<p>
<span>{{ item.type }}:</span>
<span>{{ item.actionNote }}</span>
</p>
<p v-if="item.img">
<span>举证图片:</span>
<img :src="item.imgSrc" alt="" />
</p>
</div>
</div>
</div>
</div>
<Dialog
:title="dialog.title"
:visible.sync="dialog.visible"
:fullscreen="!true"
:before-close="close"
width="55%"
>
<orderInfo v-if="dialog.type===1" />
<storeInfo v-if="dialog.type===2" />
<logistics v-if="dialog.type===3" />
<buyer v-if="dialog.type===4" />
<afterSale v-if="dialog.type===5" :dialog="dialog" @close="close" />
<!-- <goods v-if="dialog.type===1" :info="info" />
<audit-log v-if="dialog.type===2" :info="info" /> -->
</Dialog>
</div>
</template>
<script>
import { Dialog } from 'element-ui'
import afterSale from './component/after-sales'
import buyer from './component/buyer'
import logistics from './component/logistics'
import orderInfo from './component/order-info'
import storeInfo from './component/store-info'
import AfterSale from '@/api/AfterSale'
export default {
components: {
Dialog,
afterSale,
buyer,
logistics,
orderInfo,
storeInfo
},
data() {
return {
orderInfo: [
{ name: '订单ID', value: '', type: 1, field: 'orderCode' },
{ name: '售后订单ID', value: '', type: '', field: 'orderCode' },
{ name: '支付时间', value: '', type: '', field: 'createTime' },
{ name: '支付方式', value: '', type: '', field: 'payChannel' },
{ name: '订单总金额', value: '', type: '', field: '' },
{ name: '店铺名称', value: '', type: 2, field: 'memberAccount' },
{ name: '物流单号', value: '', type: 3, field: 'logisticsNum' },
{ name: '售后类型', value: '', type: '', field: 'returnType' },
{ name: '买家账户', value: '', type: 4, field: 'recName' }
],
shopList: [
{ skuImg: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg',
productName: 'DAD 气垫霜', skuNameStr: '粉色, 中瓶', skuId: '2525', sellPrice: '60', buyNum: '2', salePrice: '50' },
{ skuImg: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg',
productName: 'DAD 气垫霜', skuNameStr: '粉色, 中瓶', skuId: '2525', sellPrice: '60', buyNum: '2', salePrice: '50' }
],
recordList: [
{ title: '申请平台介入', name: '刘十', time: '2020-05-08 15:30:56', type: '问题描述', value: '不退款', img: '举证图片',
imgSrc: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=137628589,3436980029&fm=26&gp=0.jpg' },
{ title: '拒绝退款', name: '王辉', time: '2020-04-08 17:30:20', type: '卖家留言', value: '不退款', img: '', imgSrc: '' },
{ title: '发起了售后', name: '张强', time: '2020-03-25 13:25:45', type: '买家留言', value: '不要了, 买到的宝贝不符合预期', img: '', imgSrc: '' }
],
total: 0,
dialog: {}
}
},
computed: {
query() {
return this.$route.query
}
},
created() {
this.getDetails()
},
methods: {
async getDetails() {
const res = await AfterSale.getInfo(this.query.id)
const resData = res.data
if (resData.code === 0) {
const o = resData.data
this.getOrderInfo(o)
this.shopList = o.orderProductDTOList
this.getTotal(o.orderProductDTOList)
this.recordList = o.moneyReturnActionHistoryList
console.log(o, 'rr')
}
},
getTotal(data) {
if (data && data.length) {
data.forEach(item => {
this.total += item.applyPrice * item.buyNum
})
}
},
getOrderInfo(o) {
const payChannel = {
1: '微信支付',
2: '支付宝支付',
3: '余额支付'
}
const returnType = {
1: '无售后',
2: '退款',
3: '退货'
}
this.orderInfo.map(item => {
item.value = o[item.field] || ''
if (item.field === 'payChannel') {
item.value = payChannel[item.value]
}
if (item.field === 'returnType') {
item.value = returnType[item.value]
}
})
},
handle() {
this.dialog = {
title: '同意售后',
visible: true,
type: 5,
btnName: '同意售后',
allow: true
}
},
refuse() {
this.dialog = {
title: '拒绝售后',
visible: true,
type: 5,
btnName: '拒绝售后'
}
},
getInfo(item) {
if (item.type === 1) {
this.dialog = {
title: '订单信息',
visible: true,
type: item.type
}
}
if (item.type === 2) {
this.dialog = {
title: '店铺信息',
visible: true,
type: item.type
}
}
if (item.type === 3) {
this.dialog = {
title: '物流信息',
visible: true,
type: item.type
}
}
if (item.type === 4) {
this.dialog = {
title: '买家信息',
visible: true,
type: item.type
}
}
},
close() {
this.dialog = {}
}
}
}
</script>
<style lang='less' scoped>
.detail_page {
margin-top: 10px;
background-color: #fff;
height: calc(100% - 10px);
.content {
padding: 20px;
.head_box {
overflow: hidden;
height: 80px;
line-height: 80px;
font-size: 24px;
border-bottom: 1px solid #E0E5EB;
.btn_list {
display: inline-block;
float: right;
}
}
.detail , .after_sales_record{
min-height: 500px;
background:rgba(255,255,255,1);
box-shadow:0px 0px 10px 0px rgba(51,51,51,0.15);
border-radius:4px;
padding: 1px 10% 20px;
margin-top: 15px;
.detail_title {
font-size: 24px;
color: #333333;
position: relative;
margin:50px 20px 20px;
&:before {
content: '';
display: block;
position: absolute;
top: 5px;
left: -20px;
width: 4px;
height: 24px;
background-color: #3A68F2;
}
}
}
.order_info {
.order_list {
padding-left: 20px;
overflow: hidden;
list-style: none;
li {
float: left;
width: calc(100% / 3);
margin-bottom: 30px;
p {
margin: 0;
padding: 0;
display: inline-block;
font-size: 16px;
color: #333333;
&:nth-child(1) {
width: 20%;
}
&:nth-child(2) {
color: #666666;
}
}
}
.active {
color: #3A68F2 !important;
&:hover {
cursor: pointer;
}
}
}
}
.after_sale_shop {
.goods_list {
padding-left: 20px;
.good_details {
border-bottom: 1px #E0E5EB solid;
ul {
overflow: hidden;
display: flex;
margin: 0;
padding: 30px 0;
li {
flex:3;
display: flex;
// justify-content: center;
// align-items: center;
text-align: left;
img {
width: 90px;
height: 90px;
border-radius:4px;
font-size: 16px;
color: #333333;
}
&:nth-child(2),&:nth-child(3) {
display: block;
}
&:nth-child(3) {
p {
// text-align: center;
&:nth-child(1) {
font-weight: 600;
}
&:nth-child(2) {
font-size: 12px;
}
}
}
&:nth-child(4) {
font-weight: 600;
}
p {
margin: 0;
padding: 0;
height: 30px;
line-height: 30px;
}
}
}
}
}
.total {
padding-left: 20px;
ul {
margin: 0;
padding: 30px 0;
display: flex;
li {
// height: 40px;
flex:3;
font-size: 16px;
color: #333333;
display: flex;
// justify-content: center;
// align-items: center;
&:nth-child(4) {
font-weight: 600;
}
}
}
}
}
.after_sales_record {
p {
margin: 0;
padding:0 ;
font-size: 16px;
}
.record_list {
padding-left: 20px;
.record_list_title {
background: #F7F7F7;
overflow: hidden;
margin-bottom: 30px;
p {
float: left;
text-indent: 10px;
width: calc(100% / 3);
height: 36px;
line-height: 36px;
}
}
.record_list_content {
p {
overflow: hidden;
margin-bottom: 30px;
span {
display: block;
float: left;
width: 100px;
&:nth-of-type(2) {
text-indent: 10px;
}
&:nth-of-type(2) {
color: #666666;
width: calc(100% - 100px);
}
}
img {
width: 90px;
height: 90px;
border-radius: 4px;
}
}
}
}
}
}
}
/deep/ .el-dialog__wrapper {
.el-dialog__header {
height: 70px;
background-color: #3A68F2;
.el-dialog__title {
font-size:24px;
color: #fff;
}
}
}
</style>

233
src/views/after-sale-service/index.vue

@ -0,0 +1,233 @@
<template>
<div class="after_sale_service_page">
<div class="content">
<el-tabs
v-model="formParams.queryType"
@tab-click="selectItem"
>
<el-tab-pane
v-for="(item, index) in tabList"
:key="index"
:label="item.name"
:name="item.id"
/>
</el-tabs>
<div class="toolbar">
<el-form
:inline="true"
:model="formParams"
>
<el-form-item label="店铺名称/编号">
<el-input v-model="formParams.keyword" size="mini" />
</el-form-item>
<el-form-item label="订单id">
<el-input v-model="formParams.orderCode" size="mini" />
</el-form-item>
<el-form-item label="下单时间">
<el-date-picker
v-model="date"
size="mini"
type="daterange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="yyyy-MM-dd"
@change="handleChange"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="mini"
@click="query"
>
查询
</el-button>
<el-button
plain
size="mini"
@click="reset"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<div class="content_table">
<div class="table">
<el-table
:data="tableData.returnInterventionDTOList"
border
style="width: 100%"
>
<el-table-column
prop="id"
label="订单id"
/>
<el-table-column
prop="storeName"
label="店铺名称"
/>
<el-table-column
prop="tenantCode"
label="店铺编码"
/>
<el-table-column
prop="returnProductCount"
label="售后商品数量"
/>
<el-table-column
v-if="formParams.queryType === '1'"
prop="applySum"
:formatter="getPrice"
label="退款金额"
/>
<el-table-column
v-else
prop="value5"
:formatter="getPrice"
label="订单金额"
/>
<el-table-column label="操作">
<template
slot-scope="scope"
>
<el-button
v-if="formParams.queryType ==='1'"
type="text"
size="small"
@click.native.prevent="handle(scope.row.id)"
>
处理
</el-button>
<el-button
type="text"
size="small"
@click.native.prevent="details(scope.row.id)"
>
查看
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<pagination
v-show="tableData.total > 0"
:limit.sync="formParams.pageSize"
:page.sync="formParams.pageIndex"
:total="Number(tableData.total)"
@pagination="fetch"
/>
</div>
</div>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import AfterSale from '@/api/AfterSale'
export default {
components: {
Pagination
},
data() {
return {
formParams: {
keyword: '',
orderCode: '',
startTime: '',
endTime: '',
pageSize: 10,
pageIndex: 1,
queryType: '1'
},
date: [],
tableData: {},
tabList: [
{ name: '待处理', id: '1' },
{ name: '已处理', id: '2' }
]
}
},
created() {
this.getList()
},
methods: {
async getList() {
const res = await AfterSale.getList(this.formParams)
const resData = res.data
if (resData.code === 0) {
this.tableData = resData.data
}
},
getPrice(item, row, value) {
return value / 100
},
query() {
this.getList()
},
details(id) {
this.$router.push({ path: '/after-sale-service/details', query: { id }})
},
handle(id) {
this.$router.push({ path: '/after-sale-service/details', query: { id }})
},
reset() {
this.formParams = {
keyword: '',
orderCode: '',
startTime: '',
endTime: '',
pageSize: 10,
pageIndex: 1,
queryType: '1'
}
},
fetch() {
this.getList()
},
selectItem() {
this.getList()
},
handleChange(value) {
this.formParams.startTime = (value && value[0]) || ''
this.formParams.endTime = (value && value[1]) || ''
}
}
}
</script>
<style lang='less' scoped>
.after_sale_service_page {
padding: 10px 20px;
box-sizing: border-box;
.content {
background-color: #fff;
.toolbar {
padding: 10px;
}
}
}
/deep/ .el-table {
th {
background: #EEF3FF;
color:#333333;
font-size:16px;
font-weight: 400;
border-color: #E0E5EB;
text-align: center;
}
td {
font-size: 14px;
text-align: center;
color: #666666;
}
}
</style>

203
src/views/ceres/auth/menu/Edit.vue

@ -0,0 +1,203 @@
<template>
<el-dialog
:close-on-click-modal="false" :title="title" :type="type"
:visible.sync="isVisible"
:width="width" top="50px"
>
<el-form
ref="form" :model="resource" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.resource.code')" prop="code">
<el-input v-model="resource.code" :disabled="type==='edit'" @keyup.enter.native="submitForm" />
<p class="note">建议使用:作为分隔符并以viewaddupdatedeleteexportimportdownloadupload等关键词结尾</p>
<p class="note">menu:add resource:view file:upload</p>
</el-form-item>
<el-form-item :label="$t('table.resource.name')" prop="name" @keyup.enter.native="submitForm">
<el-input v-model="resource.name" />
</el-form-item>
<el-form-item :label="$t('table.resource.describe')" prop="describe" @keyup.enter.native="submitForm">
<el-input v-model="resource.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import resourceApi from '@/api/Resource.js'
export default {
name: 'ResourceEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
resource: this.initResource(),
screenWidth: 0,
width: this.initWidth(),
rules: {
code: [
{
required: true,
message: this.$t('rules.require'),
trigger: 'blur'
},
{
min: 1,
max: 255,
message: this.$t('rules.range4to10'),
trigger: 'blur'
},
{
validator: (rule, value, callback) => {
if (!this.resource.id) {
// this.$get(`system/user/check/${value}`).then((r) => {
// if (!r.data) {
// callback(this.$t('rules.usernameExist'))
// } else {
// callback()
// }
// })
} else {
// callback()
}
callback()
},
trigger: 'blur'
}
],
name: {
required: true,
message: this.$t('rules.require'),
trigger: 'blur'
}
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.type === 'add'
? this.$t('common.add')
: this.$t('common.edit')
}
},
watch: {},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initResource () {
return {
id: '',
name: '',
code: '',
describe: ''
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
setResource (val) {
const that = this
if (val) {
that.resource = { ...val }
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.resource = this.initResource()
},
submitForm () {
const that = this
this.$refs.form.validate(valid => {
if (valid) {
that.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const that = this
if (that.type === 'add') {
that.save()
} else {
that.update()
}
},
save () {
const that = this
resourceApi.save(this.resource).then(response => {
const res = response.data
if (res.isSuccess) {
that.isVisible = false
that.$message({
message: that.$t('tips.createSuccess'),
type: 'success'
})
that.$emit('success')
}
})
},
update () {
resourceApi.update(this.resource).then(response => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
p.note {
font-size: 12px;
margin: 0;
padding: 0;
line-height: 1.4rem;
}
</style>

477
src/views/ceres/auth/menu/Icons.vue

@ -0,0 +1,477 @@
<template>
<el-dialog
:close-on-click-modal="false" :title="$t('table.menu.icon')" :visible.sync="isVisible"
:width="width"
top="50px"
>
<el-input
v-model="name" class="filter-item search-item" clearable
placeholder="icon"
@keyup.native="searchIcon"
/>
<el-tabs v-model="activeName" style="margin-top: -1rem;">
<el-tab-pane :label="$t('common.tab.common')" name="first">
<ul>
<li v-for="icon in icons.commonIcons" :key="icon" class="icons-item">
<span :class="{'active':activeIndex === icon}">
<el-icon :class="icon" :title="icon" @click.native="chooseIcon(icon)" @dblclick.native="confirm" />
</span>
<p>{{ icon }}</p>
</li>
</ul>
</el-tab-pane>
<el-tab-pane :label="$t('common.tab.directivity')" name="second">
<ul>
<li v-for="icon in icons.directivityIcons" :key="icon">
<span :class="{'active':activeIndex === icon}">
<el-icon :class="icon" :title="icon" @click.native="chooseIcon(icon)" @dblclick.native="confirm" />
</span>
<p>{{ icon }}</p>
</li>
</ul>
</el-tab-pane>
<el-tab-pane :label="$t('common.tab.solid')" name="third">
<ul>
<li v-for="icon in icons.solidIcons" :key="icon">
<span :class="{'active':activeIndex === icon}">
<el-icon :class="icon" :title="icon" @click.native="chooseIcon(icon)" @dblclick.native="confirm" />
</span>
<p>{{ icon }}</p>
</li>
</ul>
</el-tab-pane>
<el-tab-pane :label="$t('common.tab.food')" name="fourth">
<ul>
<li v-for="icon in icons.foodIcons" :key="icon">
<span :class="{'active':activeIndex === icon}">
<el-icon :class="icon" :title="icon" @click.native="chooseIcon(icon)" @dblclick.native="confirm" />
</span>
<p>{{ icon }}</p>
</li>
</ul>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="confirm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
const commonIcons = [
'el-icon-eleme', 'el-icon-delete', 'el-icon-setting', 'el-icon-user', 'el-icon-phone-outline',
'el-icon-more-outline', 'el-icon-star-off', 'el-icon-goods', 'el-icon-warning-outline', 'el-icon-zoom-in',
'el-icon-zoom-out', 'el-icon-remove-outline', 'el-icon-circle-plus-outline', 'el-icon-circle-check', 'el-icon-circle-close',
'el-icon-help', 'el-icon-minus', 'el-icon-plus', 'el-icon-check', 'el-icon-close', 'el-icon-picture-outline',
'el-icon-picture-outline-round', 'el-icon-upload2', 'el-icon-download', 'el-icon-camera', 'el-icon-video-camera',
'el-icon-bell', 'el-icon-video-pause', 'el-icon-video-play', 'el-icon-refresh', 'el-icon-refresh-right', 'el-icon-refresh-left',
'el-icon-finished', 'el-icon-loading', 'el-icon-view', 'el-icon-c-scale-to-original', 'el-icon-date', 'el-icon-edit',
'el-icon-edit-outline', 'el-icon-folder', 'el-icon-folder-opened', 'el-icon-folder-add', 'el-icon-folder-remove', 'el-icon-folder-delete',
'el-icon-folder-checked', 'el-icon-tickets', 'el-icon-document-remove', 'el-icon-document-delete', 'el-icon-document-copy',
'el-icon-document-checked', 'el-icon-document', 'el-icon-document-add', 'el-icon-printer', 'el-icon-paperclip',
'el-icon-takeaway-box', 'el-icon-search', 'el-icon-monitor', 'el-icon-attract',
'el-icon-mobile',
'el-icon-scissors',
'el-icon-umbrella',
'el-icon-headset',
'el-icon-brush',
'el-icon-mouse',
'el-icon-coordinate',
'el-icon-magic-stick',
'el-icon-reading',
'el-icon-data-line',
'el-icon-data-board',
'el-icon-pie-chart',
'el-icon-data-analysis',
'el-icon-collection-tag',
'el-icon-film',
'el-icon-suitcase',
'el-icon-suitcase-1',
'el-icon-receiving',
'el-icon-collection',
'el-icon-files',
'el-icon-notebook-1',
'el-icon-notebook-2',
'el-icon-toilet-paper',
'el-icon-office-building',
'el-icon-school',
'el-icon-table-lamp',
'el-icon-house',
'el-icon-no-smoking',
'el-icon-smoking',
'el-icon-shopping-cart-full',
'el-icon-shopping-cart-1',
'el-icon-shopping-cart-2',
'el-icon-shopping-bag-1',
'el-icon-shopping-bag-2',
'el-icon-sold-out',
'el-icon-sell',
'el-icon-present',
'el-icon-box',
'el-icon-bank-card',
'el-icon-money',
'el-icon-coin',
'el-icon-wallet',
'el-icon-discount',
'el-icon-price-tag',
'el-icon-news',
'el-icon-guide',
'el-icon-male',
'el-icon-female',
'el-icon-thumb',
'el-icon-cpu',
'el-icon-link',
'el-icon-connection',
'el-icon-open',
'el-icon-turn-off',
'el-icon-set-up',
'el-icon-chat-round',
'el-icon-chat-line-round',
'el-icon-chat-square',
'el-icon-chat-dot-round',
'el-icon-chat-dot-square',
'el-icon-chat-line-square',
'el-icon-message',
'el-icon-postcard',
'el-icon-position',
'el-icon-turn-off-microphone',
'el-icon-microphone',
'el-icon-close-notification',
'el-icon-bangzhu',
'el-icon-time',
'el-icon-odometer',
'el-icon-crop',
'el-icon-aim',
'el-icon-switch-button',
'el-icon-full-screen',
'el-icon-copy-document',
'el-icon-mic',
'el-icon-stopwatch',
'el-icon-medal-1',
'el-icon-medal',
'el-icon-trophy',
'el-icon-trophy-1',
'el-icon-first-aid-kit',
'el-icon-discover',
'el-icon-place',
'el-icon-location-outline',
'el-icon-location-information',
'el-icon-add-location',
'el-icon-delete-location',
'el-icon-map-location',
'el-icon-alarm-clock',
'el-icon-timer',
'el-icon-watch-1',
'el-icon-watch',
'el-icon-lock',
'el-icon-unlock',
'el-icon-key',
'el-icon-service',
'el-icon-mobile-phone',
'el-icon-bicycle',
'el-icon-truck',
'el-icon-ship',
'el-icon-basketball',
'el-icon-football',
'el-icon-soccer',
'el-icon-baseball'
]
const directivityIcons = [
'el-icon-d-caret',
'el-icon-caret-left',
'el-icon-caret-right',
'el-icon-caret-bottom',
'el-icon-caret-top',
'el-icon-bottom-left',
'el-icon-bottom-right',
'el-icon-back',
'el-icon-right',
'el-icon-bottom',
'el-icon-top',
'el-icon-top-left',
'el-icon-top-right',
'el-icon-arrow-left',
'el-icon-arrow-right',
'el-icon-arrow-down',
'el-icon-arrow-up',
'el-icon-d-arrow-left',
'el-icon-d-arrow-right',
'el-icon-sort',
'el-icon-sort-up',
'el-icon-sort-down',
'el-icon-rank'
]
const solidIcons = [
'el-icon-question',
'el-icon-info',
'el-icon-remove',
'el-icon-circle-plus',
'el-icon-success',
'el-icon-error',
'el-icon-platform-eleme',
'el-icon-delete-solid',
'el-icon-s-tools',
'el-icon-user-solid',
'el-icon-phone',
'el-icon-star-on',
'el-icon-s-goods',
'el-icon-warning',
'el-icon-s-help',
'el-icon-picture',
'el-icon-upload',
'el-icon-camera-solid',
'el-icon-video-camera-solid',
'el-icon-message-solid',
'el-icon-s-cooperation',
'el-icon-s-order',
'el-icon-s-platform',
'el-icon-s-fold',
'el-icon-s-unfold',
'el-icon-s-operation',
'el-icon-s-promotion',
'el-icon-s-home',
'el-icon-s-release',
'el-icon-s-ticket',
'el-icon-s-management',
'el-icon-s-open',
'el-icon-s-shop',
'el-icon-s-marketing',
'el-icon-s-flag',
'el-icon-s-comment',
'el-icon-s-finance',
'el-icon-s-claim',
'el-icon-s-custom',
'el-icon-s-opportunity',
'el-icon-s-data',
'el-icon-s-check',
'el-icon-s-grid',
'el-icon-menu',
'el-icon-share',
'el-icon-d-caret',
'el-icon-caret-left',
'el-icon-caret-right',
'el-icon-caret-bottom',
'el-icon-caret-top',
'el-icon-location'
]
const foodIcons = [
'el-icon-dish',
'el-icon-dish-1',
'el-icon-food',
'el-icon-chicken',
'el-icon-fork-spoon',
'el-icon-knife-fork',
'el-icon-burger',
'el-icon-tableware',
'el-icon-sugar',
'el-icon-dessert',
'el-icon-ice-cream',
'el-icon-hot-water',
'el-icon-water-cup',
'el-icon-coffee-cup',
'el-icon-cold-drink',
'el-icon-goblet',
'el-icon-goblet-full',
'el-icon-goblet-square',
'el-icon-goblet-square-full',
'el-icon-refrigerator',
'el-icon-grape',
'el-icon-watermelon',
'el-icon-cherry',
'el-icon-apple',
'el-icon-pear',
'el-icon-orange',
'el-icon-coffee',
'el-icon-ice-tea',
'el-icon-ice-drink',
'el-icon-milk-tea',
'el-icon-potato-strips',
'el-icon-lollipop',
'el-icon-ice-cream-square',
'el-icon-ice-cream-round'
]
export default {
name: 'Icons',
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data () {
return {
name: '',
icons: {
commonIcons,
directivityIcons,
solidIcons,
foodIcons
},
activeIndex: '',
choosedIcon: '',
activeName: 'first',
screenWidth: 0,
width: this.initWidth()
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
}
}
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '60%'
} else {
return '800px'
}
},
close () {
this.$emit('close')
this.activeName = 'first'
this.choosedIcon = this.activeIndex = ''
},
chooseIcon (icon) {
this.activeIndex = icon
this.choosedIcon = icon
},
confirm () {
if (!this.choosedIcon) {
this.$message({
message: this.$t('tips.chooseNothing'),
type: 'warning'
})
return
}
this.$emit('choose', this.choosedIcon)
this.activeName = 'first'
this.choosedIcon = this.activeIndex = ''
},
searchIcon () {
if (this.name.trim() === '') {
this.icons.commonIcons = commonIcons
this.icons.directivityIcons = directivityIcons
this.icons.solidIcons = solidIcons
this.icons.foodIcons = foodIcons
this.activeName = 'first'
}
const commonList = commonIcons.filter(item => item.indexOf(this.name) !== -1)
const directivityList = directivityIcons.filter(item => item.indexOf(this.name) !== -1)
const solidList = solidIcons.filter(item => item.indexOf(this.name) !== -1)
const foodIconsList = foodIcons.filter(item => item.indexOf(this.name) !== -1)
this.icons.commonIcons = commonList
this.icons.directivityIcons = directivityList
this.icons.solidIcons = solidList
this.icons.foodIcons = foodIconsList
if (commonList.length > 0) {
this.activeName = 'first'
return
}
if (directivityList.length > 0) {
this.activeName = 'second'
return
}
if (solidList.length > 0) {
this.activeName = 'third'
return
}
if (foodIconsList.length > 0) {
this.activeName = 'fourth'
return
}
this.activeName = 'first'
}
}
}
</script>
<style lang="scss" scoped>
.search-item {
margin: 0px 0 10px;
}
ul {
// overflow-y: auto;
// padding-left: 0rem;
// margin-top: 0rem;
padding: 0;
margin: 0;
overflow: auto;
zoom: 1;
height: 500px;
li {
// list-style: none;
// float: left;
// width: 60px;
// text-align: center;
// cursor: pointer;
// color: #555;
// transition: color 0.3s ease-in-out, background-color 0.3s ease-in-out;
// position: relative;
// // margin: 3px 0;
// border-radius: 4px;
// background-color: #fff;
// overflow: hidden;
padding: 0;
float: left;
margin: 2px;
width: 100px;
text-align: center;
list-style: none;
cursor: pointer;
color: #5c6b77;
transition: all 0.2s ease;
position: relative;
}
span.active {
i {
border-radius: 2px;
border-color: #4a4a48;
background-color: #4a4a48;
color: #fff;
transition: all 0.3s;
}
}
i {
font-size: 1.7rem;
border: 1px solid #f1f1f1;
padding: 0.2rem;
margin: 0.3rem;
cursor: pointer;
&:hover {
border-radius: 2px;
border-color: #4a4a48;
background-color: #4a4a48;
color: #fff;
transition: all 0.3s;
}
}
li p {
word-break: break-all;
overflow: hidden;
margin: 2px 0;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>

628
src/views/ceres/auth/menu/Index.vue

@ -0,0 +1,628 @@
<template>
<div class="menu">
<el-row :gutter="10">
<el-col :sm="6" :xs="24">
<div class="app-container">
<div class="filter-container">
<el-input
v-model="label" :placeholder="$t('table.menu.label')" class="filter-item search-item"
clearable @keyup.enter.native="search"
/>
<el-tooltip class="item" content="新增/删除时,请先勾选菜单" effect="dark" placement="right">
<el-dropdown
v-has-any-permission="['menu:add','menu:delete','menu:export']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['menu:add']" @click.native="add">
{{ $t('table.add') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['menu:delete']" @click.native="deleteMenu">
{{ $t('table.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-tooltip>
</div>
<commonTree ref="menuTree" :tree-data="menuTree" @nodeClick="nodeClick">
<template scope="treeNode">
<span class="tree-icon">
<i :class="treeNode.data.icon ? treeNode.data.icon : 'el-icon-document'"></i>
</span>
<span class="tree-icon">
<el-badge :type="treeNode.data.isEnable ? 'success' :'danger'" class="status-item" is-dot />
</span>
</template>
</commonTree>
</div>
</el-col>
<el-col :sm="8" :xs="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>{{ menu.id === '' ? this.$t('common.add') : this.$t('common.edit') }}</span>
</div>
<div>
<el-form
ref="form" :model="menu" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.menu.parentId')" prop="parentId">
<el-tooltip :content="$t('tips.topId')" class="item" effect="dark" placement="right">
<el-input v-model="menu.parentId" readonly />
</el-tooltip>
</el-form-item>
<el-form-item :label="$t('table.menu.label')" prop="label">
<el-input v-model="menu.label" />
</el-form-item>
<!-- <el-form-item :label='$t("table.menu.type")' prop='type'>
<el-radio-group v-model='menu.type'>
<el-radio label='DIR'>目录</el-radio>
<el-radio label='MENU'>菜单</el-radio>
</el-radio-group>
</el-form-item>-->
<el-form-item :label="$t('table.menu.path')" prop="path">
<el-input v-model="menu.path" @keyup.native="menuPath" />
</el-form-item>
<el-form-item :label="$t('table.menu.component')" prop="component">
<el-input v-model="menu.component" />
<span>{{ menuComponent }}</span>
</el-form-item>
<el-form-item :label="$t('table.menu.icon')" prop="icon">
<el-input v-model="menu.icon">
<el-button slot="append" icon="el-icon-brush" style="padding-left: 0;" @click="chooseIcons" />
</el-input>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('table.status')" prop="isEnable">
<el-switch
v-model="menu.isEnable" :active-text="$t('common.status.valid')"
:inactive-text="$t('common.status.invalid')"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('table.menu.isPublic')" prop="isPublic">
<el-switch
v-model="menu.isPublic" :active-text="$t('common.yes')"
:inactive-text="$t('common.no')"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('table.menu.sortValue')" prop="sortValue">
<el-input-number v-model="menu.sortValue" :max="100" :min="0" @change="handleNumChange" />
</el-form-item>
<el-form-item :label="$t('table.menu.group')" prop="group">
<el-tooltip class="item" content="用于区分多组菜单" effect="dark" placement="right">
<el-input v-model="menu.group" />
</el-tooltip>
</el-form-item>
<el-form-item :label="$t('table.menu.describe')" prop="describe">
<el-input v-model="menu.describe" />
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="box-card" style="margin-top: -2rem;">
<el-row>
<el-col :span="24" style="text-align: right">
<el-button plain type="primary" @click="submit">{{ menu.id === '' ? this.$t('common.add') :
this.$t('common.edit') }}
</el-button>
</el-col>
</el-row>
</el-card>
</el-col>
<el-col :sm="10" :xs="24">
<el-card class="box-card">
<div class="app-container">
<div class="filter-container">
<el-input
v-model="resourceQueryParams.model.code" :placeholder="$t('table.resource.code')" class="filter-item search-item"
clearable
/>
<el-input
v-model="resourceQueryParams.model.name" :placeholder="$t('table.resource.name')" class="filter-item search-item"
clearable
/>
<el-button class="filter-item" plain type="primary" @click="resourceSearch">{{ $t('table.search') }}
</el-button>
<el-dropdown
v-has-any-permission="['resource:add','resource:delete']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-has-permission="['resource:add']" :disabled="!menu.id"
@click.native="resourceAdd"
>{{ $t('table.add') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['resource:delete']" @click.native="resourceBatchDelete">{{
$t('table.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="resourceTableKey"
ref="resourceTable"
v-loading="resourceLoading"
:data="resourceTableData.records"
border
fit
style="width: 100%;"
@selection-change="onResourceSelectChange"
@sort-change="resourceSortChange"
@filter-change="resourceFilterChange"
>
<el-table-column align="center" type="selection" width="40px" />
<el-table-column
:label="$t('table.resource.code')" :show-overflow-tooltip="true" align="center"
prop="code"
>
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.resource.name')" :show-overflow-tooltip="true" align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" align="center" class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{row}">
<i
v-hasPermission="['resource:update']" class="el-icon-edit table-operation" style="color: #2db7f5;"
@click="resourceEdit(row)"
></i>
<i
v-hasPermission="['resource:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="resourceSingleDelete(row)"
></i>
<el-link v-has-no-permission="['resource:update','resource:delete']" class="no-perm">{{
$t('tips.noPermission') }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="resourceTableData.total>0"
:limit.sync="resourceQueryParams.size"
:page.sync="resourceQueryParams.current"
:total="Number(resourceTableData.total)"
@pagination="resourceFetch"
/>
</div>
</el-card>
</el-col>
</el-row>
<Icons :dialog-visible="iconVisible" @choose="chooseIcon" @close="iconVisible = false" />
<resource-edit
ref="resourceEdit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="resourceEditClose" @success="resourceEditSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import commonTree from '@/components/ceres/CommonTree.vue'
import Icons from './Icons'
import ResourceEdit from './Edit'
import Pagination from '@/components/Pagination'
import elDragDialog from '@/directive/el-drag-dialog'
import menuApi from '@/api/Menu.js'
import resourceApi from '@/api/Resource.js'
import { initQueryParams } from '@/utils/commons'
export default {
name: 'MenuManage',
directives: { elDragDialog },
components: { Icons, commonTree, Pagination, ResourceEdit },
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
iconVisible: false,
menuTree: [],
label: '',
menu: this.initMenu(),
resourceQueryParams: initQueryParams({
model: {
menuId: null
}
}),
resourceTableKey: 0,
resourceLoading: false,
resourceSelection: [],
resourceTableData: {
total: 0
},
rules: {
label: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range2to10'), trigger: 'blur' }
],
path: [{ max: 255, message: this.$t('rules.noMoreThan100'), trigger: 'blur' },
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{
validator: (rule, value, callback) => {
const isUrl = this.isUrl(this.menu.path)
if (value === '/' || (!isUrl && value.endsWith('/'))) {
callback('请填写有效的路由地址')
} else {
callback()
}
}, trigger: 'blur'
}]
}
}
},
computed: {
menuComponent() {
let comp = ''
if (this.menu.path && this.menu.path !== '/') {
const isUrl = this.isUrl(this.menu.path)
if (isUrl) {
comp = `跳转地址:${this.menu.path}`
} else {
// comp = `src/views/ceres${this.menu.path}/Index.vue`
comp = `组件路径:src/views/${this.menu.component}.vue`
}
} else {
comp = `组件路径:src/views/ceres/Index.vue`
}
return comp
}
},
watch: {
'menu.path': function () {
this.computedComponent()
}
},
mounted() {
this.initMenuTree()
},
methods: {
isUrl(path) {
const urls = ['http://', '/http://', 'https://', '/https://', 'www.', '/www.']
const urlIndex = urls.findIndex(item => {
return path.startsWith(item)
})
return urlIndex >= 0
},
menuPath() {
const isUrl = this.isUrl(this.menu.path)
if (!isUrl && !this.menu.path.startsWith('/')) {
this.menu.path = '/' + this.menu.path
} else if (isUrl) {
if (this.menu.path.startsWith('/')) {
this.menu.path = this.menu.path.substr(1)
}
}
},
computedComponent() {
const isUrl = this.isUrl(this.menu.path)
if (isUrl) {
this.menu.component = 'Layout'
} else {
if (this.menu.path) {
this.menu.component = `ceres${this.menu.path}/Index`
} else {
this.menu.component = `ceres/Index`
}
}
},
initMenuTree() {
menuApi.allTree().then((response) => {
const res = response.data
this.menuTree = res.data
})
},
initMenu() {
return {
id: '',
label: '',
describe: '',
code: '',
isPublic: false,
path: '',
component: '',
isEnable: true,
sortValue: '',
parentId: 0,
icon: '',
group: ''
}
},
nodeClick(data) {
this.menu = { ...data }
this.$refs.form.clearValidate()
this.resourceQueryParams.model.menuId = data.id
this.resourceSearch()
},
handleNumChange(val) {
this.menu.sortValue = val
},
chooseIcons() {
this.iconVisible = true
},
chooseIcon(icon) {
this.menu.icon = icon
this.iconVisible = false
},
submit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.menu.createTime = this.menu.updateTime = null
if (this.menu.id) {
this.update()
} else {
this.save()
}
} else {
return false
}
})
},
save() {
console.log(this.menu.component)
menuApi.save(this.menu)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.createSuccess'),
type: 'success'
})
}
this.reset()
})
},
update() {
console.log(this.menu)
menuApi.update(this.menu)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
}
this.reset()
})
},
reset() {
this.initMenuTree()
this.label = ''
this.resetForm()
},
search() {
this.$refs.menuTree.$refs.treeRef.filter(this.label)
},
add() {
this.resetForm()
const checked = this.$refs.menuTree.$refs.treeRef.getCheckedKeys()
if (checked.length > 1) {
this.$message({
message: this.$t('tips.onlyChooseOne'),
type: 'warning'
})
} else if (checked.length > 0) {
this.menu.parentId = checked[0]
} else {
this.menu.parentId = 0
}
this.resourceQueryParams.model.menuId = null
this.resourceReset()
},
deleteMenu() {
const checked = this.$refs.menuTree.$refs.treeRef.getCheckedKeys()
if (checked.length === 0) {
this.$message({
message: this.$t('tips.noNodeSelected'),
type: 'warning'
})
} else {
this.$confirm(this.$t('tips.confirmDeleteNode'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
menuApi.delete({ ids: checked })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.reset()
this.resourceQueryParams.model.menuId = null
this.resourceReset()
})
}).catch(() => {
this.$refs.menuTree.$refs.treeRef.setCheckedKeys([])
})
}
},
resetForm() {
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.menu = this.initMenu()
},
resourceAdd() {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.resourceEdit.setResource({
menuId: this.menu.id
})
},
resourceEdit(row) {
this.dialog.type = 'edit'
this.dialog.isVisible = true
row.menuId = this.menu.id
this.$refs.resourceEdit.setResource(row)
},
resourceSingleDelete(row) {
this.$refs.resourceTable.clearSelection()
this.$refs.resourceTable.toggleRowSelection(row, true)
this.resourceBatchDelete()
},
resourceBatchDelete() {
if (!this.resourceSelection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDeleteNode'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const ids = this.resourceSelection.map((item) => item.id)
resourceApi.delete({ ids: ids }).then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.resourceReset()
})
})
},
resourceReset() {
this.resourceQueryParams = initQueryParams({
model: {
menuId: this.resourceQueryParams.menuId
}
})
this.$refs.resourceTable.clearSort()
this.$refs.resourceTable.clearFilter()
this.resourceSearch()
},
resourceSearch() {
this.resourceFetch({
...this.resourceQueryParams
})
},
resourceFetch(params = {}) {
if (this.resourceQueryParams.timeRange) {
this.resourceQueryParams.map.createTime_st = this.queryParams.timeRange[0]
this.resourceQueryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.resourceQueryParams.current = params.current ? params.current : this.resourceQueryParams.current
this.resourceQueryParams.size = params.size ? params.size : this.resourceQueryParams.size
if (this.resourceQueryParams.model.menuId) {
this.resourceLoading = true
resourceApi.page(this.resourceQueryParams)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.resourceTableData = res.data
}
})
.finally(() => this.resourceLoading = false)
} else {
this.resourceTableData = {}
}
},
resourceSortChange(val) {
this.resourceQueryParams.sort = val.prop
this.resourceQueryParams.order = val.order
if (this.resourceQueryParams.sort) {
this.resourceSearch()
}
},
resourceFilterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.resourceQueryParams.model[key.split('.')[0]] = val
} else {
this.resourceQueryParams.model[key] = filters[key][0]
}
}
this.resourceSearch()
},
onResourceSelectChange(selection) {
this.resourceSelection = selection
},
resourceEditClose() {
this.dialog.isVisible = false
},
resourceEditSuccess() {
this.resourceSearch()
}
}
}
</script>
<style lang="scss" scoped>
.menu {
margin: 10px;
.app-container {
margin: 0 0 10px 0 !important;
}
}
.el-card.is-always-shadow {
box-shadow: none;
}
.el-card {
border-radius: 0;
border: none;
.el-card__header {
padding: 10px 20px !important;
border-bottom: 1px solid #f1f1f1 !important;
}
}
</style>

274
src/views/ceres/auth/role/Edit.vue

@ -0,0 +1,274 @@
<template>
<el-dialog
:close-on-click-modal="false" :title="title" :type="type"
:visible.sync="isVisible" :width="width"
top="50px"
>
<el-form
ref="form" :model="role" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.role.code')" prop="code">
<el-input v-model="role.code" :disabled="type==='edit'" />
</el-form-item>
<el-form-item :label="$t('table.role.name')" prop="name">
<el-input v-model="role.name" />
</el-form-item>
<el-form-item :label="$t('table.role.status')" prop="status">
<el-radio-group v-model="role.status">
<el-radio-button :label="true">{{ $t('common.status.valid') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.status.invalid') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.role.describe')" prop="describe">
<el-input v-model="role.describe" />
</el-form-item>
<el-form-item :label="$t('table.role.dsType')" prop="dsType">
<el-radio-group v-model="role.dsType.code" @change="dsTypeChange">
<el-radio-button v-for="(item, key, index) in enums.DataScopeType" :key="index" :label="key" :value="key">
{{ item }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :hidden="orgHidden" :label="$t('table.role.orgList')" prop="orgList">
<el-tree
ref="orgTree"
:check-strictly="true"
:data="orgList"
:default-checked-keys="role.orgList"
:default-expanded-keys="role.orgList"
:expand-on-click-node="false"
highlight-current
node-key="id"
show-checkbox
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import roleApi from '@/api/Role.js'
import orgApi from '@/api/Org.js'
export default {
name: 'RoleEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data() {
return {
role: this.initRole(),
screenWidth: 0,
width: this.initWidth(),
orgList: [],
orgHidden: true,
enums: {
DataScopeType: {}
},
rules: {
name: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!this.type === 'add' && value.trim().length > 0) {
roleApi.check(value)
.then((response) => {
const res = response.data
if (res.data) {
callback('编码重复')
} else {
callback()
}
})
} else {
callback()
}
callback()
}, trigger: 'blur'
}
],
status: { required: true, message: this.$t('rules.require'), trigger: 'blur' },
orgList: {
validator: (rule, value, callback) => {
if (this.role.dsType.code === 'CUSTOMIZE') {
if (this.$refs.orgTree.getCheckedKeys().length > 0) {
callback()
} else {
callback('请至少选择一个单位或部门')
}
} else {
callback()
}
}
}
}
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {},
mounted() {
this.initOrg()
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initRole() {
return {
id: '',
code: '',
name: '',
orgList: [],
status: true,
describe: '',
dsType: {
code: 'SELF',
desc: ''
}
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
initOrg() {
orgApi.allTree({ status: true })
.then((response) => {
const res = response.data
this.orgList = res.data
})
},
loadListOptions({ callback }) {
callback()
},
setRole(val = {}) {
const vm = this
if (val['enums']) {
vm.enums = val['enums']
}
if (val['row']) {
vm.role = { ...val['row'] }
this.orgHidden = vm.role.dsType.code !== 'CUSTOMIZE'
if (!this.orgHidden) {
roleApi.getDetails(vm.role.id)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.role.orgList = res.data.orgList
this.$refs.orgTree.setCheckedKeys(res.data.orgList)
}
})
}
}
},
close() {
this.$emit('close')
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.role = this.initRole()
this.orgHidden = true
this.$refs.orgTree.setCheckedKeys([])
},
submitForm() {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
if (this.orgHidden && this.role.orgList) {
this.role.orgList.length = 0
} else {
this.role.orgList = this.$refs.orgTree.getCheckedKeys()
}
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save() {
const vm = this
roleApi.save(this.role)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update() {
roleApi.update(this.role)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
},
dsTypeChange(value) {
this.orgHidden = value !== 'CUSTOMIZE'
}
}
}
</script>
<style lang="scss" scoped>
</style>

530
src/views/ceres/auth/role/Index.vue

@ -0,0 +1,530 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="queryParams.model.code" :placeholder="$t('table.role.code')" class="filter-item search-item" />
<el-input v-model="queryParams.model.name" :placeholder="$t('table.role.name')" class="filter-item search-item" />
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-button
v-has-permission="['user:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="[ 'role:delete', 'role:export', 'role:import']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['role:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['role:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['role:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['role:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.role.code')"
align="center"
prop="code"
width="200px"
>
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.role.name')"
:show-overflow-tooltip="true"
align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.role.describe')"
:show-overflow-tooltip="true"
align="center"
prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="dsTypeList"
column-key="dsType.code"
:label="$t('table.role.dsType')"
align="center"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.dsType ? scope.row.dsType.desc : '' }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
column-key="readonly"
:filters="[
{ text: $t('common.yes'), value: true },
{ text: $t('common.no'), value: false }
]"
:label="$t('table.role.readonly')"
align="center"
width="80px"
>
<template slot-scope="scope">
<span>{{ scope.row.readonly ? $t('common.yes') : $t('common.no') }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
column-key="status"
:filters="[
{ text: $t('common.status.valid'), value: true },
{ text: $t('common.status.invalid'), value: false }
]"
:label="$t('table.role.status')"
class-name="status-col"
width="70px"
>
<template slot-scope="{ row }">
<el-tag :type="row.status | statusFilter">{{
row.status ? $t("common.status.valid") : $t("common.status.invalid")
}}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
align="center"
column-key="operation"
class-name="small-padding fixed-width"
width="140px"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['role:update']"
class="el-icon-edit table-operation"
style="color: #2db7f5;"
title="修改"
@click="edit(row)"
></i>
<i
v-hasPermission="['role:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
title="删除"
@click="singleDelete(row)"
></i>
<i
v-hasPermission="['role:auth']"
class="el-icon-user table-operation"
style="color: #87d068;"
title="授权用户"
@click="authUser(row)"
></i>
<i
v-hasPermission="['role:config']"
class="el-icon-setting table-operation"
style="color: #E6A23C"
title="分配权限"
@click="authResource(row)"
></i>
<!-- dropdown 有时候会有bug不知道这么解决 -->
<!-- <el-dropdown v-has-any-permission="['role:delete','role:auth','role:config']">
<span class="el-dropdown-link">
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="singleDelete(row)" icon="el-icon-delete" v-hasPermission="['role:delete']">删除</el-dropdown-item>
<el-dropdown-item @click.native="authUser(row)" icon="el-icon-user" v-hasPermission="['role:auth']">授权</el-dropdown-item>
<el-dropdown-item @click.native="authResource(row)" icon="el-icon-setting" v-hasPermission="['role:config']">配置</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
<el-link
v-has-no-permission="[
'role:update',
'role:delete',
'role:auth',
'role:config'
]"
class="no-perm"
>{{ $t("tips.noPermission") }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<role-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<user-role
ref="userRole"
:dialog-visible="userRoleDialog.isVisible"
@close="userRoleClose"
@success="userRoleSuccess"
/>
<role-authority
ref="roleAuthority"
:dialog-visible="roleAuthorityDialog.isVisible"
@close="roleAuthorityClose"
@success="roleAuthoritySuccess"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import elDragDialog from '@/directive/el-drag-dialog'
import RoleEdit from "./Edit"
import UserRole from "./UserRole"
import FileImport from "@/components/ceres/Import"
import RoleAuthority from "./RoleAuthority"
import roleApi from "@/api/Role.js"
import { convertEnum } from '@/utils/utils'
import { downloadFile, initEnums, initQueryParams } from '@/utils/commons'
export default {
name: "RoleManage",
directives: { elDragDialog },
components: { Pagination, RoleEdit, UserRole, RoleAuthority, FileImport },
filters: {
statusFilter(status) {
const map = {
false: "danger",
true: "success"
}
return map[status] || "success"
}
},
data() {
return {
dialog: {
isVisible: false,
type: "add"
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/role/import`
},
userRoleDialog: {
isVisible: false
},
roleAuthorityDialog: {
isVisible: false
},
tableKey: 0,
queryParams: initQueryParams({
model: {
dsType: {
code: null
}
}
}),
selection: [],
loading: false,
tableData: {
total: 0
},
enums: {
DataScopeType: {}
},
dicts: {}
}
},
computed: {
dsTypeList() {
return convertEnum(this.enums.DataScopeType)
}
},
mounted() {
initEnums('DataScopeType', this.enums)
this.fetch()
},
methods: {
editClose() {
this.dialog.isVisible = false
},
userRoleClose() {
this.userRoleDialog.isVisible = false
},
roleAuthorityClose() {
this.roleAuthorityDialog.isVisible = false
},
editSuccess() {
this.search()
},
userRoleSuccess() {
this.search()
},
roleAuthoritySuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {
dsType: {
code: null
}
}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出角色数据'
roleApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出角色数据'
roleApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.delete(ids)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
roleApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
add() {
this.dialog.type = "add"
this.dialog.isVisible = true
this.$refs.edit.setRole({ enums: this.enums })
},
edit(row) {
this.$refs.edit.setRole({ row, enums: this.enums })
this.dialog.type = "edit"
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
roleApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
},
authResource(row) {
this.roleAuthorityDialog.isVisible = true
this.$refs.roleAuthority.setRoleAuthority(row)
},
authUser(row) {
this.userRoleDialog.isVisible = true
this.$refs.userRole.setUserRole(row)
}
}
}
</script>
<style lang="scss" scoped></style>

398
src/views/ceres/auth/role/RoleAuthority.vue

@ -0,0 +1,398 @@
<template>
<el-dialog
:close-on-click-modal="false"
:title="title"
:visible.sync="isVisible"
:width="width"
top="50px"
>
<el-form
ref="form"
:model="roleAuthority"
:rules="rules"
label-position="top"
label-width="100px"
>
<el-scrollbar style="height:800px">
<el-row :gutter="12">
<el-col :span="8">
<el-card class="box-card">
<el-form-item label="菜单" prop="menuIdList">
<div align="left" style="margin-left:24px;">
<el-checkbox
v-model="checkedMenu"
:indeterminate="isIndeterminate"
@change="checkedAll"
/>
全选/反选
</div>
<el-tree
ref="menuTree"
:check-strictly="true"
:data="menuTree"
:default-checked-keys="roleAuthority.menuIdList"
:default-expanded-keys="roleAuthority.menuIdList"
:disabled="disabled"
:expand-on-click-node="false"
default-expand-all
highlight-current
node-key="id"
show-checkbox
@check="checkMenu"
@node-click="nodeClick"
/>
</el-form-item>
</el-card>
</el-col>
<el-col :span="16">
<el-card class="box-card">
<el-form-item label="资源" prop="resourceIdList">
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id"
style="width: 100%;"
@select="onSelect"
@select-all="onAllSelect"
>
<el-table-column
:reserve-selection="true"
align="center"
type="selection"
width="40px"
/>
<el-table-column
:label="$t('table.resource.code')"
:show-overflow-tooltip="true"
align="center"
prop="code"
>
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.resource.name')"
:show-overflow-tooltip="true"
align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-card>
</el-col>
</el-row>
</el-scrollbar>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{
$t("common.cancel")
}}
</el-button>
<el-button
:disabled="disabled"
plain
type="primary"
@click="submitForm"
>{{ $t("common.confirm") }}
</el-button>
</div>
</el-dialog>
</template>
<script>
import roleApi from "@/api/Role.js"
import menuApi from "@/api/Menu.js"
import resourceApi from "@/api/Resource.js"
export default {
name: "RoleAuthorityEdit",
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data() {
return {
roleAuthority: this.initRoleAuthority(),
screenWidth: 0,
width: this.initWidth(),
menuTree: [],
resourceList: [],
//
echoResourceIdList: [],
rules: {},
tableKey: 0,
loading: false,
tableData: {
total: 0
},
selection: [],
disabled: false,
isIndeterminate: false,
checkedMenu: false
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return "配置菜单资源"
}
},
watch: {},
mounted() {
this.initMenuTree()
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
allMenuIdList() {
const menuIdList = []
this.getMenuIdList(this.menuTree, menuIdList)
return menuIdList
},
getMenuIdList(menuList, menuIdList) {
if (menuList) {
menuList.forEach(item => {
menuIdList.push(item.id)
if (item.children && item.children.length > 0) {
this.getMenuIdList(item.children, menuIdList)
}
})
}
},
checkedAll() {
if (this.checkedMenu) {
//
this.$refs.menuTree.setCheckedKeys(this.allMenuIdList())
this.isIndeterminate = false
} else {
//
this.$refs.menuTree.setCheckedKeys([])
this.isIndeterminate = false
}
},
nodeClick(data) {
const vm = this
vm.loading = true
resourceApi
.page({
current: 1, size: 10000,
model: { menuId: data.id }
})
.then(response => {
const res = response.data
vm.tableData = res.data
vm.loading = false
vm.displayTable()
})
},
displayTable() {
const vm = this
vm.tableData.records.forEach(item => {
vm.roleAuthority.resourceIdList.forEach(resourceId => {
if (item.id === resourceId) {
vm.$refs.table.toggleRowSelection(item, true)
}
})
})
},
onAllSelect(selection) {
this.onSelect(selection)
},
onSelect(selection, row) {
this.mergeResourceIdList(selection, row)
// this.roleAuthority.resourceIdList = selection.map(item => item.id);
this.selection = selection
//
const old = this.$refs.menuTree.getCheckedKeys()
const must = selection.map(item => item.menuId)
const newSelected = Array.from(new Set([...old, ...must]))
this.$refs.menuTree.setCheckedKeys(newSelected)
newSelected.forEach(item => {
this.selectedParent(item)
})
},
mergeResourceIdList(selection, row) {
// true0false
let selected = true
if (row) {
selected = selection.length && selection.indexOf(row) !== -1
} else {
selected = selection.length > 0
}
//
const curResourceIdList = selection.map(item => item.id)
const ridList = this.echoResourceIdList
if (!selected && row) {
var index = ridList.findIndex(item => {
if (item == row.id) {
return true
}
})
ridList.splice(index, 1)
}
// +
this.roleAuthority.resourceIdList = [
...new Set([...curResourceIdList, ...ridList])
]
},
initMenuTree() {
menuApi.allTree().then(response => {
const res = response.data
this.menuTree = res.data
})
},
initRoleAuthority() {
return {
roleId: "",
menuIdList: [],
resourceIdList: []
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return "90%"
} else if (this.screenWidth < 1400) {
return "45%"
} else {
return "1000px"
}
},
setRoleAuthority(val) {
const vm = this
vm.roleAuthority.roleId = val.id
// vm.disabled = val.readonly
//
roleApi.findAuthorityIdByRoleId(val.id).then(response => {
const res = response.data
vm.roleAuthority.menuIdList = res.data.menuIdList
vm.roleAuthority.resourceIdList = res.data.resourceIdList
vm.echoResourceIdList = res.data.resourceIdList
vm.$refs.menuTree.setCheckedKeys(res.data.menuIdList)
res.data.menuIdList.forEach(item => {
vm.selectedParent(item)
})
})
},
close() {
this.$emit("close")
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.roleAuthority = this.initRoleAuthority()
this.$refs.menuTree.setCheckedKeys([])
this.$refs.table.clearSelection()
},
submitForm() {
const vm = this
this.$refs.form.validate(valid => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
this.roleAuthority.menuIdList = vm.$refs.menuTree
.getHalfCheckedKeys()
.concat(vm.$refs.menuTree.getCheckedKeys())
//
// this.roleAuthority.resourceIdList = vm.selection.map(item => item.id);
roleApi.saveRoleAuthority(this.roleAuthority).then(response => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t("tips.createSuccess"),
type: "success"
})
vm.$emit("success")
}
})
},
checkMenu(data, node) {
if (node.checkedKeys.length === 0) {
//
this.checkedMenu = false
this.isIndeterminate = false
} else if (node.checkedKeys.length === this.allMenuIdList().length) {
//
this.checkedMenu = true
this.isIndeterminate = false
} else {
//
this.checkedMenu = false
this.isIndeterminate = true
}
//
const selected = node.checkedKeys.indexOf(data.id) // -1
//
if (selected !== -1) {
//
this.selectedParent(data)
//
this.uniteChildSame(data, true)
} else {
//
if (data.children && data.children.length !== 0) {
this.uniteChildSame(data, false)
}
}
},
//
uniteChildSame(data, isSelected) {
this.$refs.menuTree.setChecked(data.id, isSelected)
if (data.children) {
for (let i = 0; i < data.children.length; i++) {
this.uniteChildSame(data.children[i], isSelected)
}
}
},
//
selectedParent(data) {
const currentNode = this.$refs.menuTree.getNode(data)
if (currentNode.parent.key !== undefined) {
this.$refs.menuTree.setChecked(currentNode.parent, true)
this.selectedParent(currentNode.parent)
}
}
}
}
</script>
<style lang="scss" scoped></style>

179
src/views/ceres/auth/role/UserRole.vue

@ -0,0 +1,179 @@
<template>
<el-dialog
:close-on-click-modal="false" :title="title" :visible.sync="isVisible"
:width="width"
top="50px"
>
<el-form
ref="form" :model="userRole" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item label="用户" prop="userIdList">
<el-transfer
v-model="userRole.userIdList"
:data="userList"
:filter-method="filterMethod"
:props="{
key: 'id',
label: 'name'
}"
:render-content="renderFunc"
:right-default-checked="userRole.userIdList"
:titles="['全部用户', '已选用户']"
filter-placeholder="用户名"
filterable
style="text-align: left; display: inline-block"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button :disabled="disabled" plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import roleApi from '@/api/Role.js'
import userApi from '@/api/User.js'
export default {
name: 'UserRoleEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data () {
return {
userRole: this.initUserRole(),
screenWidth: 0,
width: this.initWidth(),
userList: [],
userIdList: [],
disabled: false,
rules: {
},
renderFunc (h, option) {
// return <span title='option.account - {option.name}'>{option.account} - {option.name}</span>
return h("span", {
"attrs": {
"title": option.account + " - " + option.name
}
}, [option.account, " - ", option.name])
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return '分配角色成员'
}
},
watch: {
},
mounted () {
this.initUserList()
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initUserRole () {
return {
roleId: '',
userIdList: []
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
initUserList () {
userApi.page({ current: 1, size: 100000, model: { status: true }})
.then((response) => {
const res = response.data
this.userList = res.data.records
}).catch(() => {
this.$message({
message: this.$t('tips.getDataFail'),
type: 'error'
})
})
},
setUserRole (val) {
const vm = this
vm.userRole.roleId = val.id
// vm.disabled = val.readonly
roleApi.findUserIdByRoleId(val.id)
.then((response) => {
const res = response.data
vm.userRole.userIdList = res.data
})
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.userRole = this.initUserRole()
this.disabled = false
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
console.log(this.userRole)
roleApi.saveUserRole(this.userRole)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
filterMethod (query, item) {
return item.name.indexOf(query) > -1 || item.account.indexOf(query) > -1
}
}
}
</script>
<style lang="scss" scoped>
</style>

393
src/views/ceres/base/area/Index.vue

@ -0,0 +1,393 @@
<template>
<div class="area">
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="app-container">
<div class="filter-container">
<el-input
v-model="label"
:placeholder="$t('table.area.label')"
class="filter-item search-item"
/>
<el-button
class="filter-item"
plain
type="primary"
@click="search"
>{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-button
v-has-permission="['area:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="[ 'area:delete', 'area:export']"
class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-has-permission="['area:delete']"
@click.native="deleteArea"
>{{ $t("table.delete") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-tree
ref="areaTree"
:check-strictly="true"
:data="areaTree"
:filter-node-method="filterNode"
:load="loadTree"
highlight-current
node-key="id"
:lazy="true"
show-checkbox
@node-click="nodeClick"
/>
</div>
</el-col>
<el-col :sm="12" :xs="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>{{
area.id === "" ? this.$t("common.add") : this.$t("common.edit")
}}</span>
</div>
<div>
<el-form
ref="form"
:model="area"
:rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item
:hidden="true"
:label="$t('table.area.parentId')"
prop="parentId"
>
<el-tooltip
:content="$t('tips.topId')"
class="item"
effect="dark"
placement="top-start"
>
<el-input v-model="area.parentId" readonly />
</el-tooltip>
</el-form-item>
<el-form-item
:label="$t('table.area.parentId')"
prop="parentLabel"
>
<el-input
v-model="area.parentLabel"
readonly
disabled="disabled"
/>
</el-form-item>
<el-form-item :label="$t('table.area.label')" prop="label">
<el-input v-model="area.label" />
</el-form-item>
<el-form-item :label="$t('table.area.code')" prop="code">
<el-input v-model="area.code" />
</el-form-item>
<el-form-item :label="$t('table.area.fullName')" prop="fullName">
<el-input v-model="area.fullName" />
</el-form-item>
<el-form-item
:label="$t('table.area.longitude')"
prop="longitude"
>
<el-input v-model="area.longitude" />
</el-form-item>
<el-form-item :label="$t('table.area.latitude')" prop="latitude">
<el-input v-model="area.latitude" />
</el-form-item>
<el-form-item :label="$t('table.area.source')" prop="source">
<el-input v-model="area.source" />
</el-form-item>
<el-form-item :label="$t('table.area.level')" prop="level">
<el-radio-group
v-model="area.level.key"
border="true"
size="small"
>
<el-radio-button v-for="(item, key, index) in dicts.AREA_LEVEL" :key="index" :label="item" :value="key">
{{ item }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('table.area.sortValue')"
prop="sortValue"
>
<el-input-number
v-model="area.sortValue"
:max="100"
:min="0"
@change="handleNumChange"
/>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="box-card" style="margin-top: -2rem;">
<el-row>
<el-col :span="24" style="text-align: right">
<el-button plain type="primary" @click="submit">{{
area.id === "" ? this.$t("common.add") : this.$t("common.edit")
}}
</el-button>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import areaApi from "@/api/Area.js"
import { initDicts } from '@/utils/commons'
export default {
name: "AreaManager",
data() {
return {
label: "",
areaTree: [],
dicts: { AREA_LEVEL: {}},
area: this.initArea(),
rules: {
label: [
{
required: true,
message: this.$t("rules.require"),
trigger: "blur"
},
{
min: 1,
max: 255,
message: this.$t("rules.range3to10"),
trigger: "blur"
}
],
code: [
{
required: true,
message: this.$t("rules.require"),
trigger: "blur"
},
{
min: 1,
max: 255,
message: this.$t("rules.range3to10"),
trigger: "blur"
},
{
validator: (rule, value, callback) => {
areaApi.check(value, this.area.id).then(response => {
const res = response.data
if (res.data) {
callback('编码重复')
} else {
callback()
}
}).catch(() => callback())
},
trigger: "blur"
}
]
}
}
},
mounted() {
initDicts('AREA_LEVEL', this.dicts)
},
methods: {
initArea() {
return {
id: "",
code: "",
label: "",
parentId: 0,
parentLabel: "",
fullName: "",
longitude: "",
latitude: "",
source: "",
level: {
key: "PROVINCE"
},
sortValue: 0
}
},
initAreaTree(parentId = 0) {
areaApi.query({ parentId: parentId }).then(response => {
const res = response.data
this.areaTree = res.data
})
},
loadTree(node, resolve) {
areaApi.query({ parentId: node.data.id }).then(response => {
const res = response.data
resolve(res.data)
})
},
handleNumChange(val) {
this.area.sortValue = val
},
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
nodeClick(data) {
this.area = { ...data }
const parent = this.$refs.areaTree.getNode(data.parentId)
if (parent) {
this.area.parentLabel = parent.label
}
this.$refs.form.clearValidate()
},
add() {
this.resetForm()
const checked = this.$refs.areaTree.getCheckedNodes()
if (checked.length > 1) {
this.$message({
message: this.$t("tips.onlyChooseOne"),
type: "warning"
})
} else if (checked.length > 0) {
this.area.parentId = checked[0].id
this.area.parentLabel = checked[0].label
} else {
this.area.parentId = 0
this.area.parentLabel = ""
}
},
deleteArea() {
const checked = this.$refs.areaTree.getCheckedKeys()
if (checked.length === 0) {
this.$message({
message: this.$t("tips.noNodeSelected"),
type: "warning"
})
} else {
this.$confirm(
this.$t("tips.confirmDeleteNode"),
this.$t("common.tips"),
{
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
}
)
.then(() => {
areaApi.delete({ ids: checked }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.reset()
})
})
.catch(() => {
this.$refs.areaTree.setCheckedKeys([])
})
}
},
search() {
this.$refs.areaTree.filter(this.label)
},
reset() {
this.initAreaTree()
this.label = ""
this.resetForm()
},
submit() {
this.$refs.form.validate(valid => {
if (valid) {
if (this.area.id) {
this.update()
} else {
this.save()
}
} else {
return false
}
})
},
save() {
areaApi.save({ ...this.area }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.createSuccess"),
type: "success"
})
}
this.reset()
})
},
update() {
areaApi.update({ ...this.area }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.updateSuccess"),
type: "success"
})
}
this.reset()
})
},
resetForm() {
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.area = this.initArea()
}
}
}
</script>
<style lang="scss" scoped>
.area {
margin: 10px;
.app-container {
margin: 0 0 10px 0 !important;
}
}
.el-card.is-always-shadow {
box-shadow: none;
}
.el-card {
border-radius: 0;
border: none;
.el-card__header {
padding: 10px 20px !important;
border-bottom: 1px solid #f1f1f1 !important;
}
}
</style>

278
src/views/ceres/base/dict/Dictionary.vue

@ -0,0 +1,278 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.type" :placeholder="$t('table.dictionary.type')" size="small"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.name" :placeholder="$t('table.dictionary.name')" size="small"
class="filter-item search-item"
/>
<el-button
class="filter-item" plain type="primary"
size="small"
@click="search"
>{{ $t('table.search') }}</el-button>
<el-button
class="filter-item" plain type="warning"
size="small"
@click="reset"
>{{ $t('table.reset') }}</el-button>
<el-dropdown
v-has-any-permission="['dict:delete','dict:export', 'dict:import']" class="filter-item"
trigger="click"
>
<el-button size="small">
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['dict:add']" @click.native="add">
{{ $t('table.add') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['dict:delete']" @click.native="batchDelete">
{{ $t('table.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" size="mini" style="width: 100%;"
@filter-change="filterChange"
@row-click="rowClick"
@selection-change="onSelectChange"
@sort-change="sortChange"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column :label="$t('table.dictionary.type')" :show-overflow-tooltip="true" align="center" prop="type">
<template slot-scope="scope">
<span>{{ scope.row.type }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.dictionary.name')" :show-overflow-tooltip="true" align="center" prop="name">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.dictionary.describe')" :show-overflow-tooltip="true" align="center"
prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
column-key="status"
:filters="[{ text: $t('common.status.valid'), value: true }, { text: $t('common.status.invalid'), value: false }]"
:label="$t('table.dictionary.status')"
class-name="status-col"
width="70px"
>
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">{{ row.status ? $t('common.status.valid') :
$t('common.status.invalid') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" align="center" prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" align="center" class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{row}">
<i
v-hasPermission="['dict:update']" class="el-icon-edit table-operation" style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-hasPermission="['dict:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link v-has-no-permission="['dict:update','dict:delete']" class="no-perm">{{ $t('tips.noPermission') }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<dictionary-edit
ref="edit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import DictionaryEdit from './Edit'
import dictionaryApi from '@/api/Dictionary.js'
import { initQueryParams } from '@/utils/commons'
export default {
name: 'DictionaryManage',
components: { Pagination, DictionaryEdit },
filters: {
statusFilter(status) {
const map = {
false: 'danger',
true: 'success'
}
return map[status] || 'success'
}
},
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
tableKey: 0,
queryParams: initQueryParams({}),
selection: [],
loading: false,
tableData: {
total: 0
}
}
},
computed: {},
watch: {},
mounted() {
this.fetch()
},
methods: {
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
this.$emit('dictionaryClick', { id: -1 })
},
reset() {
this.queryParams = initQueryParams({})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const ids = []
this.selection.forEach((u) => {
ids.push(u.id)
})
this.delete(ids)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
dictionaryApi.delete({ 'ids': ids })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
add() {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.edit.setDictionary(false)
},
edit(row) {
this.$refs.edit.setDictionary(row)
this.dialog.type = 'edit'
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
dictionaryApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
rowClick(row) {
this.$emit('dictionaryClick', row)
}
}
}
</script>
<style lang="scss" scoped>
</style>

394
src/views/ceres/base/dict/DictionaryItem.vue

@ -0,0 +1,394 @@
<template>
<div class="app-container">
<div v-if="queryParams.dictionaryId === -1">
<div class="my-code">点击字典查看详情</div>
</div>
<div v-else>
<div class="filter-container">
<el-input
v-model="queryParams.model.code" :placeholder="$t('table.dictionaryItem.code')" size="small"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.name" :placeholder="$t('table.dictionaryItem.name')" size="small"
class="filter-item search-item"
/>
<el-button
class="filter-item" plain type="primary"
size="small"
@click="search"
>{{ $t('table.search') }}</el-button>
<el-button
class="filter-item" plain type="warning"
size="small"
@click="reset"
>{{ $t('table.reset') }}</el-button>
<el-dropdown
v-has-any-permission="['dict:add','dict:delete','dict:export']" class="filter-item"
trigger="click"
>
<el-button size="small">
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['dict:add']" @click.native="add">{{ $t('table.add') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['dict:delete']" @click.native="batchDelete">{{ $t('table.delete') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['dict:export']" @click.native="exportExcel">{{ $t('table.export') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['dict:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['dict:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey" ref="table" v-loading="loading"
:data="tableData.records" border fit
row-key="id" size="mini" style="width: 100%;"
@selection-change="onSelectChange" @sort-change="sortChange" @cell-click="cellClick"
@filter-change="filterChange"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.dictionaryItem.code')" :show-overflow-tooltip="true" align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.dictionaryItem.name')" :show-overflow-tooltip="true" align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.dictionaryItem.describe')" :show-overflow-tooltip="true" align="center"
prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
column-key="status"
:filter-multiple="false"
:filters="[{ text: $t('common.status.valid'), value: true }, { text: $t('common.status.invalid'), value: false }]"
:label="$t('table.dictionaryItem.status')"
class-name="status-col"
width="70px"
>
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">{{ row.status ? $t('common.status.valid') :
$t('common.status.invalid') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" align="center" prop="createTime"
sortable="custom"
width="160px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" column-key="operation" sortable="custom"
align="center" class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{row}">
<i
v-hasPermission="['dict:update']" class="el-icon-edit table-operation" style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-hasPermission="['dict:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link v-has-no-permission="['dict:update','dict:delete']" class="no-perm">{{ $t('tips.noPermission') }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<dictionaryItem-edit
ref="edit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="editClose" @success="editSuccess"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import DictionaryItemEdit from './DictionaryItemEdit'
import dictionaryItemApi from '@/api/DictionaryItem.js'
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile, initQueryParams } from '@/utils/commons'
export default {
name: 'DictionaryItemManage',
directives: { elDragDialog },
components: { Pagination, DictionaryItemEdit, FileImport },
filters: {
statusFilter(status) {
const map = {
false: 'danger',
true: 'success'
}
return map[status] || 'success'
}
},
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/dictionaryItem/import`
},
tableKey: 0,
queryParams: initQueryParams({
model: {
dictionaryId: -1
},
sort: 'sortValue',
order: 'ascending'
}),
selection: [],
loading: false,
dictionary: {
id: null,
code: ''
},
tableData: {
total: 0
}
}
},
computed: {},
watch: {},
mounted() {
this.fetch({
...this.queryParams
})
},
methods: {
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: { dictionaryId: this.dictionary.id }
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出字典数据'
dictionaryItemApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出字典数据'
dictionaryItemApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const ids = []
this.selection.forEach((u) => {
ids.push(u.id)
})
this.delete(ids)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
dictionaryItemApi.delete({ 'ids': ids })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
add() {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.edit.setDictionaryItem({
id: null,
status: true,
dictionaryId: this.dictionary.id,
dictionaryType: this.dictionary.type
})
},
edit(row) {
this.$refs.edit.setDictionaryItem(row)
this.dialog.type = 'edit'
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
dictionaryItemApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
},
dictionaryClick(dictionary) {
this.queryParams.model.dictionaryId = dictionary.id
this.dictionary = dictionary
this.search()
}
}
}
</script>
<style lang="scss" scoped>
</style>

191
src/views/ceres/base/dict/DictionaryItemEdit.vue

@ -0,0 +1,191 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="dictionaryItem" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :hidden="true" :label="$t('table.dictionaryItem.dictionaryId')" prop="dictionaryId">
<el-input v-model="dictionaryItem.dictionaryId" :disabled="type==='edit'" />
</el-form-item>
<el-form-item :label="$t('table.dictionaryItem.code')" prop="code">
<el-input v-model="dictionaryItem.code" :disabled="type==='edit'" />
</el-form-item>
<el-form-item :label="$t('table.dictionaryItem.name')" prop="name">
<el-input v-model="dictionaryItem.name" />
</el-form-item>
<el-form-item :label="$t('table.dictionaryItem.status')" prop="status">
<el-radio-group v-model="dictionaryItem.status">
<el-radio :label="true">{{ $t('common.status.valid') }}</el-radio>
<el-radio :label="false">{{ $t('common.status.invalid') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.dictionaryItem.sortValue')" prop="sortValue">
<el-input v-model="dictionaryItem.sortValue" />
</el-form-item>
<el-form-item :label="$t('table.dictionaryItem.describe')" prop="describe">
<el-input v-model="dictionaryItem.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import dictionaryItemApi from '@/api/DictionaryItem.js'
export default {
name: 'DictionaryItemEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
dictionaryItem: this.initDictionaryItem(),
screenWidth: 0,
width: this.initWidth(),
rules: {
dictionaryId: { required: true, message: this.$t('rules.require'), trigger: 'blur' },
code: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 64, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
name: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 64, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
describe: [
{ min: 1, max: 200, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
status: { required: true, message: this.$t('rules.require'), trigger: 'blur' }
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initDictionaryItem () {
return {
id: '',
dictionaryId: null,
dictionaryType: '',
name: '',
code: '',
status: true,
sortValue: 1,
describe: ''
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
setDictionaryItem (val) {
const vm = this
if (val) {
vm.dictionaryItem = { ...val }
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.dictionaryItem = this.initDictionaryItem()
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save () {
const vm = this
dictionaryItemApi.save(this.dictionaryItem)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update () {
dictionaryItemApi.update(this.dictionaryItem)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

181
src/views/ceres/base/dict/Edit.vue

@ -0,0 +1,181 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="dictionary" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.dictionary.type')" prop="type">
<el-input v-model="dictionary.type" :disabled="type==='edit'" />
</el-form-item>
<el-form-item :label="$t('table.dictionary.name')" prop="name">
<el-input v-model="dictionary.name" />
</el-form-item>
<el-form-item :label="$t('table.dictionary.status')" prop="status">
<el-radio-group v-model="dictionary.status">
<el-radio :label="true">{{ $t('common.status.valid') }}</el-radio>
<el-radio :label="false">{{ $t('common.status.invalid') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.dictionary.describe')" prop="describe">
<el-input v-model="dictionary.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import dictionaryApi from '@/api/Dictionary.js'
export default {
name: 'DictionaryEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
dictionary: this.initDictionary(),
screenWidth: 0,
width: this.initWidth(),
rules: {
type: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 64, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
name: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 64, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
describe: [
{ min: 1, max: 200, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
status: { required: true, message: this.$t('rules.require'), trigger: 'blur' }
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initDictionary () {
return {
id: '',
name: '',
type: '',
status: true,
describe: ''
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
setDictionary (val) {
const vm = this
if (val) {
vm.dictionary = { ...val }
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.dictionary = this.initDictionary()
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save () {
const vm = this
dictionaryApi.save(this.dictionary)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update () {
dictionaryApi.update(this.dictionary)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

62
src/views/ceres/base/dict/Index.vue

@ -0,0 +1,62 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>字典列表</span>
</div>
<dictionary ref="dictionary" @dictionaryClick="dictionaryClick" @resetItem="resetItem" />
</el-card>
</el-col>
<el-col :sm="12" :xs="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>字典详情</span>
</div>
<dictionary-item ref="dictionaryItem" />
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import Dictionary from './Dictionary'
import DictionaryItem from './DictionaryItem'
export default {
name: 'DictionaryManage',
components: { Dictionary, DictionaryItem },
filters: {
},
data () {
return {
}
},
computed: {
},
watch: {
},
mounted () {
},
methods: {
dictionaryClick (row) {
this.$refs.dictionaryItem.dictionaryClick(row)
},
resetItem () {
this.$refs.dictionaryItem.dictionaryClick({ id: -1 })
}
}
}
</script>
<style lang="scss" scoped>
.el-card /deep/ .el-card__body {
padding: 0px;
}
</style>

184
src/views/ceres/base/parameter/Edit.vue

@ -0,0 +1,184 @@
<template>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
:title="title"
:type="type"
:visible.sync="isVisible"
:width="width"
top="50px"
>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">
{{ $t("common.cancel") }}
</el-button>
<el-button plain type="primary" @click="submitForm">
{{ $t("common.confirm") }}
</el-button>
</div>
<el-form
ref="form" :model="parameter" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.parameter.key')" prop="key">
<el-input v-model="parameter.key" :disabled="type==='edit'" type="" placeholder="参数键" />
</el-form-item>
<el-form-item :label="$t('table.parameter.name')" prop="name">
<el-input v-model="parameter.name" type="" placeholder="参数名称" />
</el-form-item>
<el-form-item :label="$t('table.parameter.value')" prop="value">
<el-input v-model="parameter.value" type="" placeholder="参数值" />
</el-form-item>
<el-form-item :label="$t('table.parameter.describe')" prop="describe">
<el-input v-model="parameter.describe" type="textarea" placeholder="描述" />
</el-form-item>
<el-form-item :label="$t('table.parameter.status')" prop="status">
<el-radio-group v-model="parameter.status" size="medium">
<el-radio-button :label="true">{{ $t("common.status.valid") }}</el-radio-button>
<el-radio-button :label="false">{{ $t("common.status.invalid") }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.parameter.readonly')" prop="readonly">
<el-radio-group v-model="parameter.readonly" size="medium">
<el-radio-button :label="true">{{ $t("common.yes") }}</el-radio-button>
<el-radio-button :label="false">{{ $t("common.no") }}</el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
import parameterApi from "@/api/Parameter.js"
export default {
name: "ParameterEdit",
components: { },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: "add"
}
},
data() {
return {
parameter: this.initParameter(),
screenWidth: 0,
width: this.initWidth(),
rules: {
}
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.$t("common." + this.type)
}
},
watch: {},
mounted() {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initParameter() {
return {
id: "",
key: '',
name: '',
value: '',
describe: '',
status: true,
readonly: false
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return "90%"
} else if (this.screenWidth < 1400) {
return "45%"
} else {
return "800px"
}
},
setParameter(val) {
const vm = this
if (val) {
vm.parameter = { ...val }
}
},
close() {
this.$emit("close")
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.parameter = this.initParameter()
},
submitForm() {
const vm = this
this.$refs.form.validate(valid => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
if (vm.type === "edit") {
vm.update()
} else {
vm.save()
}
},
save() {
const vm = this
parameterApi.save(this.parameter).then(response => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t("tips.createSuccess"),
type: "success"
})
vm.$emit("success")
}
})
},
update() {
parameterApi.update(this.parameter).then(response => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t("tips.updateSuccess"),
type: "success"
})
this.$emit("success")
}
})
}
}
}
</script>
<style lang="scss" scoped></style>

418
src/views/ceres/base/parameter/Index.vue

@ -0,0 +1,418 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.key"
:placeholder="$t('table.parameter.key')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.name"
:placeholder="$t('table.parameter.name')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.value"
:placeholder="$t('table.parameter.value')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-button
v-has-permission="['parameter:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="['parameter:delete', 'parameter:export', 'parameter:import']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['parameter:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['parameter:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['parameter:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['parameter:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.parameter.key')" :show-overflow-tooltip="true" align="center"
prop="key" width=""
>
<template slot-scope="scope">
<span>{{ scope.row.key }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.parameter.name')" :show-overflow-tooltip="true" align="center"
prop="name" width=""
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.parameter.value')" :show-overflow-tooltip="true" align="center"
prop="value" width=""
>
<template slot-scope="scope">
<span>{{ scope.row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.parameter.describe')" :show-overflow-tooltip="true" align="center"
prop="describe" width=""
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.parameter.status')" :show-overflow-tooltip="true" align="center"
:filter-multiple="false" column-key="status"
:filters="[{ text: $t('common.status.valid'), value: true },{ text: $t('common.status.invalid'), value: false }]"
prop="status" width="80px"
>
<template slot-scope="scope">
<el-tag slot :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? $t("common.status.valid") : $t("common.status.invalid") }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.parameter.readonly')" :show-overflow-tooltip="true" align="center"
:filter-multiple="false" column-key="readonly"
:filters="[{ text: $t('common.yes'), value: 'true' }, { text: $t('common.no'), value: 'false' }]"
prop="readonly" width="70px"
>
<template slot-scope="scope">
<el-tag slot :type="scope.row.readonly ? 'success' : 'danger'">
{{ scope.row.readonly ? $t("common.yes") : $t("common.no") }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" column-key="operation" align="center"
class-name="small-padding fixed-width" width="100px"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['parameter:add']" class="el-icon-copy-document table-operation" :title="$t('common.delete')"
style="color: #2db7f5;" @click="copy(row)"
></i>
<i
v-hasPermission="['parameter:update']" class="el-icon-edit table-operation" :title="$t('common.delete')"
style="color: #2db7f5;" @click="edit(row)"
></i>
<i
v-hasPermission="['parameter:delete']" class="el-icon-delete table-operation" :title="$t('common.delete')"
style="color: #f50;" @click="singleDelete(row)"
></i>
<el-link v-has-no-permission="['parameter:update', 'parameter:add', 'parameter:delete']" class="no-perm">
{{ $t("tips.noPermission") }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<parameter-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import ParameterEdit from "./Edit"
import parameterApi from "@/api/Parameter.js"
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile, initDicts, initEnums, initQueryParams } from '@/utils/commons'
export default {
name: "ParameterManage",
directives: { elDragDialog },
components: { Pagination, ParameterEdit, FileImport },
filters: {},
data() {
return {
dialog: {
isVisible: false,
type: "add"
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/parameter/import`
},
tableKey: 0,
queryParams: initQueryParams({
model: {}
}),
selection: [],
loading: false,
tableData: {
total: 0
}
}
},
computed: {},
watch: {},
mounted() {
this.fetch()
},
methods: {
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出参数数据'
parameterApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出参数数据'
parameterApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = this.selection.map(u => u.id)
this.delete(ids)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
parameterApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
add() {
this.dialog.type = "add"
this.dialog.isVisible = true
this.$refs.edit.setParameter(false)
},
copy(row) {
this.$refs.edit.setParameter(row)
this.dialog.type = "copy"
this.dialog.isVisible = true
},
edit(row) {
this.$refs.edit.setParameter(row)
this.dialog.type = "edit"
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
parameterApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped></style>

218
src/views/ceres/developer/application/Edit.vue

@ -0,0 +1,218 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="application" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item v-show="type!=='add' " :label="$t('table.application.clientId')" prop="clientId">
<el-input v-model="application.clientId" :disabled="type==='view' || type==='edit' " />
</el-form-item>
<el-form-item v-show="type!=='add' " :label="$t('table.application.clientSecret')" prop="clientSecret">
<el-input v-model="application.clientSecret" :disabled="type==='view' || type==='edit' " />
</el-form-item>
<el-form-item :label="$t('table.application.name')" prop="name">
<el-input v-model="application.name" :disabled="type==='view'" />
</el-form-item>
<el-form-item :label="$t('table.application.website')" prop="website">
<el-input v-model="application.website" :disabled="type==='view'" />
</el-form-item>
<el-form-item :label="$t('table.application.icon')" prop="icon">
<el-input v-model="application.icon" :disabled="type==='icon'" />
</el-form-item>
<el-form-item :label="$t('table.application.appType')" prop="appType">
<el-radio-group v-model="application.appType.code" border="true" size="small">
<el-radio-button
v-for="(item, key, index) in enums.ApplicationAppTypeEnum" :key="index" :label="key"
:value="key"
>{{ item }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.application.status')" prop="status">
<el-radio-group v-model="application.status" border="true" size="small">
<el-radio-button :label="true">{{ $t('common.status.valid') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.status.invalid') }}</el-radio-button>
</el-radio-group>
<aside class="tips">禁用提示"请求地址,禁止访问!";</aside>
</el-form-item>
<el-form-item :label="$t('table.application.describe')" prop="describe">
<el-input v-model="application.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import applicationApi from '@/api/Application.js'
import { initEnums } from '@/utils/commons.js'
export default {
name: 'ApplicationEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data() {
return {
remoteApplicationLoading: false,
application: this.initApplication(),
screenWidth: 0,
width: this.initWidth(),
orgList: [],
enums: { ApplicationAppTypeEnum: {}},
applicationList: [],
rules: {
name: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
status: { required: true, message: this.$t('rules.require'), trigger: 'blur' }
},
serviceList: [
{ id: "ceres-authority-server", name: "权限服务" },
{ id: "ceres-file-server", name: "文件服务" },
{ id: "ceres-msgs-server", name: "消息服务" },
{ id: "ceres-demo-server", name: "演示服务" },
{ id: "ceres-order-server", name: "订单服务" }
]
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {},
mounted() {
initEnums(['ApplicationAppTypeEnum'], this.enums)
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initApplication() {
return {
id: '',
clientId: '',
clientSecret: '',
website: '',
name: '',
icon: '',
appType: { code: 'SERVER' },
describe: '',
status: true
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
loadListOptions({ callback }) {
callback()
},
setApplication(val, orgs) {
const vm = this
vm.orgList = orgs
if (val) {
vm.application = { ...val }
}
},
close() {
this.$emit('close')
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.application = this.initApplication()
},
submitForm() {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save() {
const vm = this
applicationApi.save(this.application)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update() {
applicationApi.update(this.application)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
.tips {
margin-bottom: 0;
padding: 0px 10px;
}
</style>

394
src/views/ceres/developer/application/Index.vue

@ -0,0 +1,394 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.clientId" :placeholder="$t('table.application.clientId')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.name" :placeholder="$t('table.application.name')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
:start-placeholder="$t('table.createTime')"
class="filter-item search-item date-range-item"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
<el-button
v-has-permission="['application:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="['application:delete','application:export']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['application:delete']" @click.native="batchDelete">{{ $t('table.delete')
}}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['application:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['application:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @cell-click="cellClick"
@filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.application.clientId')" column-key="clientId" :show-overflow-tooltip="true"
align="center" prop="clientId"
>
<template slot-scope="scope">
<span>{{ scope.row.clientId }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.application.name')" :show-overflow-tooltip="true" align="left"
min-width="120px" prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.application.website')" :show-overflow-tooltip="true" align="left"
min-width="120px" prop="website"
>
<template slot-scope="scope">
<span>{{ scope.row.website }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.application.appType')" :show-overflow-tooltip="true" align="center"
min-width="50px" prop="appType"
>
<template slot-scope="scope">
<span>{{ scope.row.appType ? scope.row.appType.desc : '' }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.application.status')" :show-overflow-tooltip="true" align="center"
prop="status" width="70px"
>
<template slot-scope="scope">
<el-badge :type="scope.row.status ? 'success' :'danger'" class="status-item" is-dot />
<span>{{ scope.row.status? $t('common.status.valid') : $t('common.status.invalid') }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.application.describe')" :show-overflow-tooltip="true" align="left"
min-width="120px" prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" :show-overflow-tooltip="true" sortable="custom"
align="center"
prop="className" width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" align="center" column-key="operation"
class-name="small-padding fixed-width" fixed="right" width="110px"
>
<template slot-scope="{row}">
<i
v-has-permission="['application:update']" class="el-icon-edit table-operation" style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-has-permission="['application:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<application-edit
ref="edit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import ApplicationEdit from './Edit'
import applicationApi from '@/api/Application.js'
import elDragDialog from '@/directive/el-drag-dialog'
import { downloadFile, initQueryParams } from '@/utils/commons'
import { copy } from '@/utils/utils'
export default {
name: 'Application',
directives: { elDragDialog },
components: { Pagination, ApplicationEdit },
filters: {},
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
tableKey: 0,
loading: false,
queryParams: initQueryParams({
model: {}
}),
selection: [],
tableData: {
total: 0
}
}
},
computed: {},
mounted() {
this.fetch()
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出应用数据'
applicationApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出应用数据'
applicationApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
applicationApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
const isPersist = this.selection.findIndex(item => item.isPersist)
if (isPersist > -1) {
this.$message({
message: "不能删除内置数据",
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const logIds = this.selection.map(item => item.id)
this.delete(logIds)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(logIds) {
applicationApi.delete({ ids: logIds })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
closeDrawer(done) {
done()
this.currentRow = {}
},
cellClick(row, column) {
if (column['columnKey'] === "operation") {
return
}
if (column['columnKey'] === "clientId") {
copy(row[column.property])
this.$message({
message: this.$t('tips.copySelected'),
type: 'success'
})
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
},
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
add() {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.edit.setApplication(false)
},
edit(row) {
this.$refs.edit.setApplication(row)
this.dialog.type = 'edit'
this.dialog.isVisible = true
}
}
}
</script>
<style lang="scss" scoped>
.item {
margin-top: 7px;
}
.box-item {
margin-top: 15px;
aside {
word-wrap: break-word;
margin-top: 15px;
}
pre {
white-space: pre-wrap;
font-size: 0.8em;
line-height: 1.5em;
}
}
</style>

71
src/views/ceres/developer/db/Index.vue

@ -0,0 +1,71 @@
<template>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="权限服务" name="first">
<i-frame ref="authorityDruid" :src="authorityDruid" @refresh="authorityRefresh" />
</el-tab-pane>
<el-tab-pane label="文件服务" name="second">
<i-frame ref="fileDruid" :src="fileDruid" @refresh="fileRefresh" />
</el-tab-pane>
<el-tab-pane label="消息服务" name="third">
<i-frame ref="msgsDruid" :src="msgsDruid" @refresh="msgsRefresh" />
</el-tab-pane>
</el-tabs>
</template>
<script>
import iFrame from '@/components/iFrame'
import { druid } from '@/settings'
export default {
components: { iFrame },
data () {
return {
authorityDruid: '',
fileDruid: '',
msgsDruid: '',
activeName: 'first'
}
},
computed: {
},
mounted () {
setTimeout(() => {
this.$notify.info({
title: this.$t('common.tips'),
message: this.$t('tips.iframeGrant'),
duration: 5000
})
}, 1000)
this.authorityDruid = this.druidUrl("authority")
this.$refs.authorityDruid.url = this.authorityDruid
this.fileDruid = this.druidUrl("file")
this.$refs.fileDruid.url = this.fileDruid
this.msgsDruid = this.druidUrl("msgs")
this.$refs.msgsDruid.url = this.msgsDruid
},
methods: {
authorityRefresh (u) {
this.authorityDruid = u
},
fileRefresh (u) {
this.fileDruid = u
},
msgsRefresh (u) {
this.msgsDruid = u
},
handleClick (tab, event) {
console.log(tab, event)
},
druidUrl (service) {
if (druid[service]) {
return druid[service][process.env.NODE_ENV]
} else {
return service
}
}
}
}
</script>

433
src/views/ceres/developer/loginLog/Index.vue

@ -0,0 +1,433 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.account"
:placeholder="$t('table.loginLog.account')" class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.location" :placeholder="$t('table.loginLog.location')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.requestIp"
:placeholder="$t('table.loginLog.requestIp')" class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
:start-placeholder="$t('table.createTime')"
class="filter-item search-item date-range-item"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-dropdown
v-has-any-permission="['loginLog:delete', 'loginLog:export']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['loginLog:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['loginLog:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['loginLog:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown v-has-any-permission="['loginLog:delete']" class="filter-item" trigger="click">
<el-button>
清理日志
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="clear(1)">
保留一个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(2)">
保留三个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(3)">
保留六个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(4)">
保留一年
</el-dropdown-item>
<el-dropdown-item @click.native="clear(5)">
保留一千条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(6)">
保留一万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(7)">
保留三万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(8)">
保留十万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(9)">
清空所有
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.loginLog.userName')"
:show-overflow-tooltip="true"
align="center"
min-width="80px"
prop="userName"
>
<template slot-scope="scope">
<span>{{ scope.row.userName }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.requestIp')"
:show-overflow-tooltip="true"
align="center"
min-width="80px"
prop="requestIp"
>
<template slot-scope="scope">
<span>{{ scope.row.requestIp }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.browser')"
:show-overflow-tooltip="true"
align="center"
prop="browser"
width="120px"
>
<template slot-scope="scope">
<span>{{ scope.row.browser }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.browserVersion')"
:show-overflow-tooltip="true"
align="center"
prop="browserVersion"
width="120px"
>
<template slot-scope="scope">
<span>{{ scope.row.browserVersion }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.operatingSystem')"
:show-overflow-tooltip="true"
align="center"
prop="operatingSystem"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.operatingSystem }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.location')"
:show-overflow-tooltip="true"
align="center"
min-width="150px"
prop="location"
>
<template slot-scope="scope">
<span>{{ scope.row.location }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.loginDate')"
:show-overflow-tooltip="true"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.loginLog.description')"
:show-overflow-tooltip="true"
align="left"
column-key="description"
prop="description"
>
<template slot-scope="scope">
<span>
<el-badge
:type="scope.row.description && scope.row.description == '登录成功' ? 'success' : 'danger'"
class="item" is-dot
/>
{{ scope.row.description }}
</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
column-key="operation"
align="center"
class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{ row }">
<i
v-has-permission="['loginLog:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link v-has-no-permission="['loginLog:delete']" class="no-perm">{{
$t("tips.noPermission")
}}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import loginLogApi from "@/api/LoginLog.js"
import elDragDialog from '@/directive/el-drag-dialog'
import { downloadFile, initQueryParams } from '@/utils/commons'
export default {
name: "LoginLog",
directives: { elDragDialog },
components: { Pagination },
filters: {},
data() {
return {
preview: {
isVisible: false,
context: ''
},
tableKey: 0,
loading: false,
queryParams: initQueryParams(),
selection: [],
tableData: {
total: 0
},
enums: {},
dicts: {}
}
},
computed: {},
mounted() {
this.fetch()
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出登录日志数据'
loginLogApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出登录日志数据'
loginLogApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
loginLogApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
clear(type) {
this.$confirm('确认要清除日志吗?', this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
loginLogApi.clear({ type: type }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
})
.catch(() => {
})
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const logIds = this.selection.map(item => item.id)
this.delete(logIds)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(logIds) {
loginLogApi.delete({ ids: logIds }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams()
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
.item {
margin-top: 7px;
}
</style>

513
src/views/ceres/developer/optLog/Index.vue

@ -0,0 +1,513 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.userName" :placeholder="$t('table.optLog.userName')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.requestIp" :placeholder="$t('table.optLog.requestIp')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
:start-placeholder="$t('table.createTime')"
class="filter-item search-item date-range-item"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
<el-dropdown v-has-any-permission="['optLog:delete','optLog:export']" class="filter-item" trigger="click">
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['optLog:delete']" @click.native="batchDelete">{{ $t('table.delete') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['optLog:export']" @click.native="exportExcel">{{ $t('table.export') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['optLog:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown v-has-any-permission="['optLog:delete']" class="filter-item" trigger="click">
<el-button>
清理日志
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="clear(1)">
保留一个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(2)">
保留三个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(3)">
保留六个月
</el-dropdown-item>
<el-dropdown-item @click.native="clear(4)">
保留一年
</el-dropdown-item>
<el-dropdown-item @click.native="clear(5)">
保留一千条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(6)">
保留一万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(7)">
保留三万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(8)">
保留十万条
</el-dropdown-item>
<el-dropdown-item @click.native="clear(9)">
清空所有
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.optLog.userName')" :show-overflow-tooltip="true" align="center"
prop="userName"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.userName }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.requestUri')" :show-overflow-tooltip="true" align="left"
min-width="120px" prop="requestUri"
>
<template slot-scope="scope">
<span>{{ scope.row.requestUri }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="httpMethodFilters"
:label="$t('table.optLog.httpMethod')"
:show-overflow-tooltip="true"
align="center"
column-key="httpMethod"
prop="httpMethod"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.httpMethod.desc }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.requestIp')" :show-overflow-tooltip="true" align="center"
prop="requestIp" width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.requestIp }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.description')" :show-overflow-tooltip="true" align="center"
min-width="100px" prop="description"
>
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false" :filters="typeFilters" :label="$t('table.optLog.type')"
:show-overflow-tooltip="true" align="center" column-key="type"
prop="type" width="80px"
>
<template slot-scope="scope">
<span>
<el-badge
:type="(scope.row.type && scope.row.type.code == 'OPT')? 'success':'danger' " class="item"
is-dot
/>
{{ scope.row.type.desc }}
</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.actionMethod')" :show-overflow-tooltip="true" align="center"
min-width="120px" prop="actionMethod"
>
<template slot-scope="scope">
<span>{{ scope.row.classPath + '.' + scope.row.actionMethod }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.startTime')" :show-overflow-tooltip="true" align="center"
prop="startTime" sortable="custom" width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.startTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.optLog.consumingTime')" :show-overflow-tooltip="true" align="center"
prop="consumingTime" width="110px"
>
<template slot-scope="{row}">
<el-tag :type="row.consumingTime | timeFilter">{{ transTime(row.consumingTime) }}</el-tag>
</template>
</el-table-column>
<el-table-column :formatter="uaForamt" label="终端 | 浏览器" prop="ua" width="120" />
<el-table-column
:label="$t('table.operation')" column-key="operation" align="center"
class-name="small-padding fixed-width"
width="110px"
>
<template slot-scope="{row}">
<i
v-has-permission="['optLog:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
<i
v-has-permission="['optLog:view']" class="el-icon-view table-operation" style="color: #87d068;"
@click="onView(row)"
></i>
<el-link v-has-no-permission="['optLog:delete','optLog:view']" class="no-perm">{{ $t('tips.noPermission') }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<el-drawer v-model="currentRow" :before-close="closeDrawer" :visible.sync="drawer" direction="rtl">
<div slot="title" class="clearfix">
<el-badge :type="(currentRow.type && currentRow.type.code == 'OPT')? 'success':'danger' " class="item" is-dot />
{{ currentRow.type ? currentRow.type.desc : '' }}
{{ currentRow.requestUri }}
</div>
<el-scrollbar style="height: 100%;">
<el-card class="box-card">
<div class="box-item">
<span class="field-label">执行方法:</span>
<aside>{{ currentRow.classPath + '.' + currentRow.actionMethod + '()' }}</aside>
</div>
<div class="box-item">
<span class="field-label">请求参数:</span>
<aside style>{{ currentRow.params }}</aside>
</div>
<div class="box-item">
<span class="field-label">响应信息:</span>
<aside style>
<pre style="white-space: pre-wrap;">{{ currentRow.result ? JSON.stringify(JSON.parse(currentRow.result), null, 4) : '' }}</pre>
</aside>
</div>
<div v-if="currentRow.type && currentRow.type['code']==='EX'" class="box-item">
<span class="field-label">错误信息:</span>
<aside>{{ currentRow.exDetail }}</aside>
</div>
</el-card>
</el-scrollbar>
</el-drawer>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import { readUserAgent } from '@/utils/utils'
import optLogApi from '@/api/OptLog.js'
import { convertEnum } from '@/utils/utils'
import elDragDialog from '@/directive/el-drag-dialog'
import { downloadFile, initEnums, initQueryParams } from '@/utils/commons'
export default {
name: 'OptLog',
directives: { elDragDialog },
components: { Pagination },
filters: {
timeFilter(time) {
if (time < 500) {
return 'success'
} else if (time < 1000) {
return ''
} else if (time < 1500) {
return 'warning'
} else {
return 'danger'
}
}
},
data() {
return {
tableKey: 0,
loading: false,
preview: {
isVisible: false,
context: ''
},
queryParams: initQueryParams({
model: {
type: {
key: null
},
httpMethod: {
key: null
}
}
}),
selection: [],
tableData: {},
enums: {
LogType: {},
HttpMethod: {}
},
drawer: false,
currentRow: {}
}
},
computed: {
typeFilters() {
return convertEnum(this.enums.LogType)
},
httpMethodFilters() {
return convertEnum(this.enums.HttpMethod)
}
},
mounted() {
initEnums(['LogType', 'HttpMethod'], this.enums)
this.fetch()
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出操作日志'
optLogApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出操作日志'
optLogApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
optLogApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
clear(type) {
this.$confirm('确认要清除日志吗?', this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
optLogApi.clear({ type: type }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
})
.catch(() => {
})
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const logIds = this.selection.map(item => item.id)
this.delete(logIds)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(logIds) {
optLogApi.delete({ ids: logIds })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {
type: {
key: null
},
httpMethod: {
key: null
}
}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
transTime(time) {
return `${time} ms`
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
},
onView(row) {
this.currentRow = row
this.drawer = true
},
closeDrawer(done) {
done()
this.currentRow = {}
},
uaForamt(row) {
const ua = readUserAgent(row.ua)
return ua.terminal + ' | ' + ua.browser
}
}
}
</script>
<style lang="scss" scoped>
.item {
margin-top: 7px;
}
.drawer-item {
margin-top: 6px;
}
.box-item {
margin-top: 15px;
aside {
word-wrap: break-word;
margin-top: 15px;
}
pre {
white-space: pre-wrap;
font-size: 0.8em;
line-height: 1.5em;
}
}
</style>

230
src/views/ceres/developer/systemApi/Edit.vue

@ -0,0 +1,230 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="systemApi" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.systemApi.serviceId')" prop="serviceId">
<el-select
v-model="systemApi.serviceId" :disabled="type==='view'" placeholder
style="width:100%"
value
>
<el-option v-for="item in serviceList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.systemApi.code')" prop="code">
<el-input v-model="systemApi.code" :disabled="type==='view' || type==='edit' " />
</el-form-item>
<el-form-item :label="$t('table.systemApi.name')" prop="name">
<el-input v-model="systemApi.name" :disabled="type==='view'" />
</el-form-item>
<el-form-item :label="$t('table.systemApi.path')" prop="path">
<el-input v-model="systemApi.path" :disabled="type==='view'" />
</el-form-item>
<el-form-item :label="$t('table.systemApi.status')" prop="status">
<el-radio-group v-model="systemApi.status" border="true" size="small">
<el-radio-button :label="true">{{ $t('common.status.valid') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.status.invalid') }}</el-radio-button>
</el-radio-group>
<aside class="tips">禁用提示"请求地址,禁止访问!";</aside>
</el-form-item>
<el-form-item :label="$t('table.systemApi.isAuth')" prop="isAuth">
<el-radio-group v-model="systemApi.isAuth" size="small">
<el-radio-button :label="true">{{ $t('common.yes') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.no') }}</el-radio-button>
</el-radio-group>
<aside class="tips">未认证登录,提示"认证失败,请重新登录!";: 不需要认证登录</aside>
</el-form-item>
<!-- <el-form-item :label="$t('table.systemApi.isOpen')" prop="isOpen">
<el-radio-group size="small" v-model="systemApi.isOpen">
<el-radio-button :label="true">{{ $t('common.yes') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.no') }}</el-radio-button>
</el-radio-group>
<aside class="tips">:提示"请求地址,拒绝访问!"</aside>
</el-form-item>-->
<el-form-item :label="$t('table.systemApi.describe')" prop="describe">
<el-input v-model="systemApi.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import systemApiApi from '@/api/SystemApi.js'
export default {
name: 'SystemApiEdit',
components: { Treeselect },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
remoteSystemApiLoading: false,
systemApi: this.initSystemApi(),
screenWidth: 0,
width: this.initWidth(),
orgList: [],
systemApiList: [],
rules: {
name: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
status: { required: true, message: this.$t('rules.require'), trigger: 'blur' },
serviceId: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 50, message: this.$t('rules.range4to10'), trigger: 'blur' }
]
},
serviceList: [
{ id: "ceres-authority-server", name: "权限服务" },
{ id: "ceres-file-server", name: "文件服务" },
{ id: "ceres-msgs-server", name: "消息服务" },
{ id: "ceres-demo-server", name: "演示服务" },
{ id: "ceres-order-server", name: "订单服务" }
]
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initSystemApi () {
return {
id: '',
code: '',
describe: '',
requestMethod: '',
contentType: '',
serviceId: '',
path: '',
status: true,
isOpen: true,
isAuth: false
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
loadListOptions ({ callback }) {
callback()
},
setSystemApi (val, orgs) {
const vm = this
vm.orgList = orgs
if (val) {
vm.systemApi = { ...val }
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.systemApi = this.initSystemApi()
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save () {
const vm = this
systemApiApi.save(this.systemApi)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update () {
systemApiApi.update(this.systemApi)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
.tips {
margin-bottom: 0;
padding: 0px 10px;
}
</style>

512
src/views/ceres/developer/systemApi/Index.vue

@ -0,0 +1,512 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.code" :placeholder="$t('table.systemApi.code')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.name" :placeholder="$t('table.systemApi.name')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.serviceId" :placeholder="$t('table.systemApi.serviceId')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
:start-placeholder="$t('table.createTime')"
class="filter-item search-item date-range-item"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
<el-button
v-has-permission="['systemApi:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-button
v-has-permission="['systemApi:add']" class="filter-item" plain
type="danger"
@click="scan"
>
扫描接口
</el-button>
<el-dropdown v-has-any-permission="['systemApi:delete','systemApi:export']" class="filter-item" trigger="click">
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['systemApi:delete']" @click.native="batchDelete">
{{ $t('table.delete') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['systemApi:export']" @click.native="exportExcel">
{{ $t('table.export') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['systemApi:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @cell-click="cellClick"
@filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column :label="$t('table.systemApi.code')" :show-overflow-tooltip="true" align="center" prop="code">
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.name')" :show-overflow-tooltip="true" align="left"
min-width="120px"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.describe')" :show-overflow-tooltip="true" align="left"
min-width="120px" prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.requestMethod')" :show-overflow-tooltip="true" align="left"
min-width="80px" prop="requestMethod"
>
<template slot-scope="scope">
<span>{{ scope.row.requestMethod }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.serviceId')" :show-overflow-tooltip="true" align="center"
min-width="180px" prop="serviceId"
>
<template slot-scope="scope">
<span>{{ scope.row.serviceId }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.path')" :show-overflow-tooltip="true" align="left"
min-width="200px"
prop="path"
>
<template slot-scope="scope">
<span>{{ scope.row.path }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.status')" :show-overflow-tooltip="true" align="center"
prop="status"
width="70px"
>
<template slot-scope="scope">
<el-badge :type="scope.row.status ? 'success' :'danger'" class="status-item" is-dot />
<span>{{ scope.row.status? $t('common.status.valid') : $t('common.status.invalid') }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.isAuth')" :show-overflow-tooltip="true" align="center"
prop="isAuth"
width="70px"
>
<template slot-scope="scope">
<span>{{ scope.row.isAuth ? $t('common.yes') : $t('common.no') }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.isOpen')" :show-overflow-tooltip="true" align="center"
prop="isOpen"
width="80px"
>
<template slot-scope="scope">
<span>{{ scope.row.isOpen? $t('common.yes') : $t('common.no') }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.systemApi.isPersist')" :show-overflow-tooltip="true" align="center"
prop="isOpen" width="80px"
>
<template slot-scope="scope">
<span>{{ scope.row.isPersist? $t('common.yes') : $t('common.no') }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" sortable="custom" :show-overflow-tooltip="true"
align="center"
prop="className" width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" column-key="operation" align="center"
class-name="small-padding fixed-width"
fixed="right" width="110px"
>
<template slot-scope="{row}">
<i
v-has-permission="['systemApi:update']" class="el-icon-edit table-operation" style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-has-permission="['systemApi:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
<i class="el-icon-view table-operation" style="color: #87d068;" @click="onView(row)"></i>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<el-drawer v-model="currentRow" :before-close="closeDrawer" :visible.sync="drawer" direction="rtl">
<div slot="title" class="clearfix">{{ currentRow.path }}</div>
<el-scrollbar style="height: 100%;">
<el-card class="box-card">
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.code') }}:</span>
<aside>{{ currentRow.code }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.name') }}:</span>
<aside style>{{ currentRow.name }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.describe') }}:</span>
<aside style>{{ currentRow.describe }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.requestMethod') }}:</span>
<aside style>{{ currentRow.requestMethod }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.contentType') }}:</span>
<aside style>{{ currentRow.contentType }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.serviceId') }}:</span>
<aside style>{{ currentRow.serviceId }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.path') }}:</span>
<aside style>{{ currentRow.path }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.status') }}:</span>
<aside style>{{ currentRow.status ? '是' : '否' }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.isPersist') }}:</span>
<aside style>{{ currentRow.isPersist ? '是' : '否' }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.isAuth') }}:</span>
<aside style>{{ currentRow.isAuth ? '是' : '否' }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.isOpen') }}:</span>
<aside style>{{ currentRow.isOpen ? '是' : '否' }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.isPersist') }}:</span>
<aside style>{{ currentRow.isPersist ? '是' : '否' }}</aside>
</div>
<div class="box-item">
<span class="field-label">{{ $t('table.systemApi.className') }}:</span>
<aside style>{{ currentRow.className + '.' + currentRow.methodName }}</aside>
</div>
</el-card>
</el-scrollbar>
</el-drawer>
<system-api-edit
ref="edit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<system-api-scan
ref="scan" :dialog-visible="dialogScan.isVisible" :type="dialogScan.type"
@close="scanClose"
@success="editSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import SystemApiEdit from './Edit'
import SystemApiScan from './Scan'
import systemApiApi from '@/api/SystemApi.js'
import elDragDialog from '@/directive/el-drag-dialog'
import { downloadFile, initQueryParams } from '@/utils/commons'
export default {
name: 'SystemApi',
directives: { elDragDialog },
components: { Pagination, SystemApiEdit, SystemApiScan },
filters: {},
data() {
return {
dialogScan: {
isVisible: false,
type: 'add'
},
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
tableKey: 0,
loading: false,
queryParams: initQueryParams({
model: {}
}),
selection: [],
tableData: {
total: 0
},
drawer: false,
currentRow: {}
}
},
computed: {},
mounted() {
this.fetch()
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出接口数据'
systemApiApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出接口数据'
systemApiApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
systemApiApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
const isPersist = this.selection.findIndex(item => item.isPersist)
if (isPersist > -1) {
this.$message({
message: "不能删除内置数据",
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const logIds = this.selection.map(item => item.id)
this.delete(logIds)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(logIds) {
systemApiApi.delete({ ids: logIds })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = {}
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
onView(row) {
this.currentRow = row
this.drawer = true
},
closeDrawer(done) {
done()
this.currentRow = {}
},
cellClick(row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
},
editClose() {
this.dialog.isVisible = false
},
scanClose() {
this.dialogScan.isVisible = false
},
editSuccess() {
this.search()
},
add() {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.edit.setSystemApi(false)
},
scan() {
this.dialogScan.type = 'add'
this.dialogScan.isVisible = true
},
edit(row) {
this.$refs.edit.setSystemApi(row)
this.dialog.type = 'edit'
this.dialog.isVisible = true
}
}
}
</script>
<style lang="scss" scoped>
.item {
margin-top: 7px;
}
.drawer-item {
margin-top: 6px;
}
.box-item {
margin-top: 15px;
aside {
word-wrap: break-word;
margin-top: 15px;
}
pre {
white-space: pre-wrap;
font-size: 0.8em;
line-height: 1.5em;
}
}
</style>

127
src/views/ceres/developer/systemApi/Scan.vue

@ -0,0 +1,127 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="systemApi" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.systemApi.serviceId')" prop="serviceId">
<el-select
v-model="systemApi.serviceId" :disabled="type==='view'" placeholder
style="width:100%"
value
>
<el-option v-for="item in serviceList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import systemApiApi from '@/api/SystemApi.js'
export default {
name: 'SystemApiScan',
components: { },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
screenWidth: 0,
width: this.initWidth(),
rules: {
},
systemApi: {
serviceId: 'authority'
},
serviceList: [
{ id: "authority", name: "权限服务" },
{ id: "file", name: "文件服务" },
{ id: "msgs", name: "消息服务" },
{ id: "demo", name: "演示服务" },
{ id: "order", name: "订单服务" }
]
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return '扫描Rest接口'
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.serviceId = 'authority'
},
submitForm() {
systemApiApi.scan(this.systemApi.serviceId).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: "扫描成功,请稍后刷新数据",
type: 'success'
})
}
})
}
}
}
</script>
<style lang="scss" scoped>
.tips {
margin-bottom: 0;
padding: 0px 10px;
}
</style>

188
src/views/ceres/file/attachment/Edit.vue

@ -0,0 +1,188 @@
<template>
<el-dialog
v-el-drag-dialog :close-on-click-modal="false" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
@dragDialog="handleDrag"
>
<el-form
ref="form" :model="attachment" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.attachment.bizId')" prop="bizId">
<el-input v-model="attachment.bizId" />
</el-form-item>
<el-form-item :label="$t('table.attachment.bizType')" prop="bizType">
<el-input v-model="attachment.bizType" />
</el-form-item>
<el-form-item label="文件" prop="fileLength">
<fileUpload
ref="fileRef" :accept-size="2*1024*1024" :auto-upload="false"
:limit="5"
@fileLengthVaild="fileLengthVaild" @setId="setIdAndSubmit"
>
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<div slot="tip" class="el-upload__tip">文件不超过2MB</div>
</fileUpload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button :disabled="disabled" plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import elDragDialog from '@/directive/el-drag-dialog'
import fileUpload from "@/components/ceres/fileUpload"
export default {
name: 'AttachmentEdit',
directives: { elDragDialog, fileUpload },
components: { fileUpload },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
accept: "image/jpeg, image/gif, image/png",
attachment: this.initAttachment(),
screenWidth: 0,
width: this.initWidth(),
fileLength: 0,
disabled: false,
rules: {
bizType: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 0, max: 255, message: this.$t('rules.range0to255'), trigger: 'blur' }
],
bizId: { min: 0, max: 255, message: this.$t('rules.range0to255'), trigger: 'blur' },
fileLength: { required: true, trigger: "change",
validator: (rule, value, callback) => {
const vm = this
if (vm.fileLength === 0) {
callback(new Error("请上传文件"))
} else if (vm.fileLength > 5) {
callback(new Error("一次性只能上传5个文件"))
} else {
callback()
}
}
}
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.$t('common.upload')
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initAttachment () {
return {
id: '',
bizId: '',
bizType: '',
file: null,
isSingle: false
}
},
handleDrag () {
console.log(`我被拖动了`)
},
//
fileLengthVaild (data) {
const vm = this
vm.fileLength = data || 0
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
setAttachment (val) {
const vm = this
if (val) {
vm.attachment = { ...val }
}
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.attachment = this.initAttachment()
this.$refs.fileRef.init({
id: "",
bizId: "",
bizType: ""
})
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
vm.disabled = true
vm.$refs.fileRef.submitFile(this.attachment.id, this.attachment.bizId, this.attachment.bizType)
},
setIdAndSubmit (isUploadCompleted) {
const vm = this
if (isUploadCompleted) {
vm.disabled = false
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

435
src/views/ceres/file/attachment/Index.vue

@ -0,0 +1,435 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.submittedFileName"
:placeholder="$t('table.attachment.submittedFileName')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-button
v-has-permission="['file:add']" class="filter-item" plain
type="danger"
@click="upload"
>
{{ $t("table.upload") }}
</el-button>
<el-dropdown
v-has-any-permission="['file:delete', 'file:download']"
class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-has-permission="['file:delete']"
@click.native="batchDelete"
>{{ $t("table.delete") }}</el-dropdown-item>
<el-dropdown-item
v-has-permission="['file:download']"
@click.native="batchDownload"
>{{ $t("table.download") }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.attachment.submittedFileName')"
:show-overflow-tooltip="true"
align="left"
prop="submittedFileName"
>
<template slot-scope="scope">
<div class="hover-effect" @click="onView(scope.row)">
<i :class="scope.row.icon" class="button-list"></i>
<span>{{ scope.row.submittedFileName }}</span>
</div>
</template>
</el-table-column>
<el-table-column
:label="$t('table.attachment.dataType')"
:show-overflow-tooltip="true"
align="center"
prop="dataType"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.dataType.desc }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.attachment.bizType')"
:show-overflow-tooltip="true"
align="center"
width="120px"
>
<template slot-scope="scope">
<span>{{ scope.row.bizType }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.attachment.bizId')"
:show-overflow-tooltip="true"
align="center"
width="180px"
>
<template slot-scope="scope">
<span>{{ scope.row.bizId }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.attachment.size')"
align="center"
:show-overflow-tooltip="true"
width="120px"
>
<template slot-scope="scope">
<span>{{ formatSize(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
align="center" column-key="operation"
class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['file:download']"
class="el-icon-download table-operation"
style="color: #f50;"
@click="singleDownload(row)"
></i>
<i
v-hasPermission="['file:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link
v-has-no-permission="['file:update', 'file:delete']"
class="no-perm"
>{{ $t("tips.noPermission") }}</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<attachment-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<el-dialog :visible.sync="dialogVisible">
<img
v-if="dialogImageUrl"
:key="dialogImageUrl"
:src="dialogImageUrl"
alt="图片飞到火星了"
height="500px"
width="100%"
@error="errorImage()"
/>
<span v-else>图片飞到火星了~</span>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import AttachmentEdit from "./Edit"
import attachmentApi from "@/api/Attachment.js"
import { renderSize } from "@/utils/utils"
import { onlinePreview } from "@/settings"
import { downloadFile, initQueryParams } from '@/utils/commons'
export default {
name: "AttachmentManage",
components: { Pagination, AttachmentEdit },
filters: {},
data() {
return {
dialogVisible: false,
dialogImageUrl: "",
dialog: {
isVisible: false,
type: "add"
},
tableKey: 0,
queryParams: initQueryParams({
model: {}
}),
selection: [],
loading: false,
tableData: {
records: [],
total: 0
}
}
},
computed: {},
mounted() {
this.fetch()
},
methods: {
errorImage() {
const img = event.srcElement
img.src = require("@/assets/404_images/404_image.jpeg")
img.onerror = null //
},
formatSize(row) {
return renderSize(row.size)
},
filterStatus(value, row) {
return row.status === value
},
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
singleDownload(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDownload()
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDownload() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm("确认下载吗?", this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.download(ids)
})
.catch(() => {
this.clearSelections()
})
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.delete(ids)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
download(ids) {
attachmentApi.download({ ids: ids }).then(response => {
downloadFile(response)
this.clearSelections()
})
},
delete(ids) {
attachmentApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
onView(row) {
if (row.url) {
window.open(onlinePreview + encodeURIComponent(row.url))
}
},
upload() {
this.dialog.type = "upload"
this.dialog.isVisible = true
this.$refs.edit.setAttachment(false)
},
edit(row) {
this.$refs.edit.setAttachment(row)
this.dialog.type = "edit"
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
attachmentApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
.file-breadcrumb {
margin: 10px 0px 20px;
}
.page {
text-align: center;
margin-top: 5px;
}
.button-list {
margin-right: 10px;
font-size: 20px !important;
color: #22a2ed;
vertical-align: middle;
}
.title {
color: #777;
font-size: 2em;
border-bottom: 1px solid #e5e5e5;
}
div{
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
</style>

524
src/views/ceres/msgs/myMsgs/Index.vue

@ -0,0 +1,524 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.title"
:placeholder="$t('table.msgs.title')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.content"
:placeholder="$t('table.msgs.content')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<router-link :to="{ path: '/msgs/sendMsgs', query: { type: 'add' } }">
<el-button v-has-permission="['msgs:add']" class="filter-item" plain type="danger">
{{ $t("table.add") }}
</el-button>
</router-link>
<el-dropdown
v-has-any-permission="['msgs:delete', 'msgs:export']"
class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<!-- <router-link :to="{ path: '/msgs/sendMsgs', query: { type: 'add' } }">-->
<!-- <el-dropdown-item v-has-permission="['msgs:add']">-->
<!-- {{ $t("table.add") }}-->
<!-- </el-dropdown-item>-->
<!-- </router-link>-->
<el-dropdown-item v-has-permission="['msgs:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['msgs:mark']" @click.native="batchMark">
标记已读
</el-dropdown-item>
<el-dropdown-item v-has-permission="['msgs:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['msgs:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['msgs:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.msgs.title')"
:show-overflow-tooltip="true"
align="center"
prop="templateId"
>
<template slot-scope="scope">
<span>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.msgs.content')"
:show-overflow-tooltip="true"
align="center"
prop="receiver"
>
<template slot-scope="scope">
<span>{{ scope.row.content }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.msgs.author')"
:show-overflow-tooltip="true"
align="center"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="bizTypeFilters"
:label="$t('table.msgs.bizType')"
:show-overflow-tooltip="true"
align="center"
column-key="bizType.code"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.bizType ? scope.row.bizType.desc : "" }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="msgsCenterTypeFilters"
:label="$t('table.msgs.msgsCenterType')"
:show-overflow-tooltip="true"
class-name="status-col"
column-key="msgsCenterType.code"
width="100px"
>
<template slot-scope="scope">
<span>{{
scope.row.msgsCenterType
? scope.row.msgsCenterType.desc
: scope.row.msgsCenterType
}}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="[
{ text: '已读', value: 'true' },
{ text: '未读', value: 'false' }
]"
:label="$t('table.msgs.isRead')"
align="center"
column-key="isRead"
prop="isRead"
width="100px"
>
<template slot-scope="scope">
<span>
<el-tag slot :type="scope.row.isRead ? 'success' : 'danger'">{{
scope.row.isRead ? "已读" : "未读"
}}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
align="center"
class-name="small-padding fixed-width"
width="100px"
column-key="operation"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['msgs:view']"
class="el-icon-view table-operation"
style="color: #2db7f5;"
@click="view(row)"
></i>
<i
v-hasPermission="['msgs:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link
v-has-no-permission="['msgs:view', 'msgs:delete']"
class="no-perm"
>{{ $t("tips.noPermission") }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import msgsApi from "@/api/Msgs.js"
import { convertEnum } from "@/utils/utils"
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile, initMsgsEnums, initQueryParams } from '@/utils/commons'
export default {
name: "MsgsList",
directives: { elDragDialog },
components: { Pagination, FileImport },
filters: {
statusFilter(status) {
const map = {
WAITING: "danger",
SUCCESS: "success",
FAIL: "error"
}
return map[status] || "success"
}
},
data() {
return {
dialog: {
isVisible: false,
type: "add"
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/msgs/msgsCenterInfo/import`
},
tableKey: 0,
queryParams: initQueryParams({
model: {
msgsCenterType: { code: null },
bizType: { code: null }
}
}),
selection: [],
loading: false,
tableData: {
total: 0
},
enums: {
MsgsCenterType: {},
MsgsBizType: {}
}
}
},
computed: {
msgsCenterTypeFilters() {
return convertEnum(this.enums.MsgsCenterType)
},
bizTypeFilters() {
return convertEnum(this.enums.MsgsBizType)
}
},
watch: {
'$route'(to) {
if (to.path === '/msgs/myMsgs') {
this.fetch()
}
}
},
mounted() {
initMsgsEnums(['MsgsCenterType', 'MsgsBizType'], this.enums)
this.fetch()
},
methods: {
filterStatus(value, row) {
return row.status === value
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {
msgsCenterType: { code: null },
bizType: { code: null }
}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出消息数据'
msgsApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出消息数据'
msgsApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.delete(ids)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
msgsApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
batchMark() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm("确认全部标记为已读吗?", this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.mark(ids)
})
.catch(() => {
this.clearSelections()
})
},
mark(ids, callback) {
msgsApi.mark({ msgCenterIds: ids }).then(response => {
const res = response.data
if (typeof callback === "function") {
callback(ids)
}
})
},
view(row) {
if (row.isRead) {
this.$router.push({
path: "/msgs/sendMsgs",
query: {
id: row.id,
type: "view"
}
})
} else {
this.mark([row.id], ids => {
this.$router.push({
path: "/msgs/sendMsgs",
query: {
id: row.id,
type: "view"
}
})
})
}
},
edit(row) {
this.$router.push({
path: "/msgs/sendMsgs",
query: {
id: row.id,
type: "edit"
}
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
msgsApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick(row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped></style>

412
src/views/ceres/msgs/sendMsgs/Index.vue

@ -0,0 +1,412 @@
<template>
<div class="app-container">
<el-form
ref="form" :model="msgsCenterInfo" :rules="rules"
label-position="right"
label-width="100px" status-icon
>
<el-row>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.title')" prop="msgsCenterInfoDTO.title">
<el-input v-model="msgsCenterInfo.msgsCenterInfoDTO.title" :disabled="type==='view'" :maxlength="255" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.bizType')" prop="msgsCenterInfoDTO.bizType">
<el-select
v-model="msgsCenterInfo.msgsCenterInfoDTO.bizType.code" :disabled="type==='view'" clearable
placeholder
style="width:100%" value
>
<el-option v-for="(item, key, index) in enums.MsgsBizType" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.bizId')" prop="msgsCenterInfoDTO.bizId">
<el-input v-model="msgsCenterInfo.msgsCenterInfoDTO.bizId" :disabled="type==='view'" :maxlength="255" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.msgsCenterType')" prop="msgsCenterInfoDTO.msgsCenterType">
<el-select
v-model="msgsCenterInfo.msgsCenterInfoDTO.msgsCenterType.code" :disabled="type==='view'" placeholder
style="width:100%"
value @change="msgsCenterTypeChange"
>
<el-option v-for="(item, key, index) in enums.MsgsCenterType" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item v-show="type!=='view'" label="接收类型" prop="msgsCenterInfoDTO.receiveType">
<el-radio-group v-model="msgsCenterInfo.receiveType" :disabled="type==='view' || disabledReceiveType">
<el-radio-button label="user">用户</el-radio-button>
<el-radio-button label="role">角色</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item v-if="msgsCenterInfo.receiveType==='user'" v-show="type!=='view'" label="接收用户" prop="userIdList">
<el-select
v-model="msgsCenterInfo.userIdList" :disabled="type==='view' || disabledReceiveType" collapse-tags
multiple
placeholder style="width:100%" value
@change="userSelect"
>
<el-option v-for="item in allUserList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item v-else v-show="type!=='view'" label="接收角色" prop="roleCodeList">
<el-select
v-model="msgsCenterInfo.roleCodeList" :disabled="type==='view' || disabledReceiveType" collapse-tags
multiple
placeholder style="width:100%" value
@change="roleSelect"
>
<el-option v-for="item in allRoleList" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.author')" prop="msgsCenterInfoDTO.author">
<el-input v-model="msgsCenterInfo.msgsCenterInfoDTO.author" :disabled="type==='view'" :maxlength="255" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.handlerUrl')" prop="msgsCenterInfoDTO.handlerUrl">
<el-input v-model="msgsCenterInfo.msgsCenterInfoDTO.handlerUrl" :disabled="type==='view'" :maxlength="255" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('table.msgs.isSingleHandle')" prop="msgsCenterInfoDTO.isSingleHandle">
<el-radio-group v-model="msgsCenterInfo.msgsCenterInfoDTO.isSingleHandle" :disabled="type==='view'">
<el-radio-button :label="true">{{ $t('common.yes') }}</el-radio-button>
<el-radio-button :label="false">{{ $t('common.no') }}</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item prop="msgsCenterInfoDTO.content" style="margin-bottom: 30px;">
<Tinymce ref="content" v-model="msgsCenterInfo.msgsCenterInfoDTO.content" :height="400" />
</el-form-item>
</el-form>
<div class="dialog-footer">
<el-button
v-show="type!=='view'" :disabled="disabled" plain
type="primary"
@click="submitForm(false)"
>发送</el-button>
</div>
</div>
</template>
<script>
import msgsApi from '@/api/Msgs.js'
import roleApi from '@/api/Role.js'
import userApi from '@/api/User.js'
import Tinymce from '@/components/Tinymce'
import { initMsgsEnums } from '@/utils/commons.js'
export default {
name: 'MsgsEdit',
components: { Tinymce },
filters: {
},
props: {
},
data () {
return {
allUserList: [],
allRoleList: [],
oldChooseUserIdList: [[]],
oldChooseRoleIdList: [[]],
dialog: {
isVisible: false
},
type: 'add',
msgsCenterInfo: this.initMsgsCenterInfo(),
disabled: false,
disabledReceiveType: false,
enums: {
MsgsBizType: {},
MsgsCenterType: {}
},
rules: {
"msgsCenterInfoDTO.title": [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: "长度在 1 到 255 个字符", trigger: 'blur' }
],
"msgsCenterInfoDTO.content": [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 65535, message: "长度在 1 到 65535 个字符", trigger: 'blur' }
],
"msgsCenterInfoDTO.msgsCenterType": { required: true, message: this.$t('rules.require'), trigger: 'change' },
"roleCodeList": {
validator: (rule, value, callback) => {
if (this.msgsCenterInfo.receiveType === 'role' && this.msgsCenterInfo.msgsCenterInfoDTO.msgsCenterType.code !== 'PUBLICITY' && this.msgsCenterInfo.roleCodeList.length <= 0) {
callback('请选择角色')
} else {
callback()
}
}, trigger: 'blur'
},
"userIdList": {
validator: (rule, value, callback) => {
if (this.msgsCenterInfo.receiveType === 'user' && this.msgsCenterInfo.msgsCenterInfoDTO.msgsCenterType.code !== 'PUBLICITY' && this.msgsCenterInfo.userIdList.length <= 0) {
callback('请选择用户')
} else {
callback()
}
}, trigger: 'blur'
},
"msgsCenterInfoDTO.handlerUrl": { min: 1, max: 255, message: "长度在 1 到 255 个字符", trigger: 'blur' }
}
}
},
computed: {
},
watch: {
$route () {
if (this.$route.path === '/msgs/sendMsgs') {
this.loadMsgs()
this.loadUserList()
this.loadRoleList()
}
}
},
mounted () {
this.loadMsgs()
this.loadUserList()
this.loadRoleList()
initMsgsEnums(['MsgsCenterType', 'MsgsBizType'], this.enums)
},
methods: {
initMsgsCenterInfo () {
return {
userIdList: [],
roleCodeList: [],
receiveType: 'user',
msgsCenterInfoDTO: {
id: '',
bizId: '',
bizType: {
code: ''
},
msgsCenterType: {
code: ''
},
title: '',
content: '',
author: '',
handlerUrl: '',
handlerParams: '',
isSingleHandle: true
}
}
},
loadMsgs () {
const type = this.$route.query.type
const id = this.$route.query.id
this.type = type
if (type === 'view') {
msgsApi.get(id)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.msgsCenterInfo.msgsCenterInfoDTO = res.data
if (this.msgsCenterInfo.msgsCenterInfoDTO.bizType === null) {
this.msgsCenterInfo.msgsCenterInfoDTO.bizType = { code: '' }
}
if (this.msgsCenterInfo.msgsCenterInfoDTO.msgsCenterType === null) {
this.msgsCenterInfo.msgsCenterInfoDTO.msgsCenterType = { code: '' }
}
}
})
} else {
this.reset()
}
},
loadUserList () {
userApi.page({ current: 1, size: 10000, model: { status: true }})
.then((response) => {
const res = response.data
if (res.isSuccess) {
if (res.data.records.length > 0) {
this.allUserList = [...[{ id: '-1', name: '全选' }], ...res.data.records]
}
}
})
},
loadRoleList () {
roleApi.page({ current: 1, size: 10000, model: { status: true }})
.then((response) => {
const res = response.data
if (res.isSuccess) {
if (res.data.records.length > 0) {
this.allRoleList = [...[{ code: '-1', name: '全选' }], ...res.data.records]
}
}
})
},
msgsCenterTypeChange (select) {
if (select === 'PUBLICITY') {
this.disabledReceiveType = true
} else {
this.disabledReceiveType = false
}
},
userSelect (val) {
//
const allValues = this.allUserList.map(item => item.id)
//
const oldVal = this.oldChooseUserIdList.length === 1 ? [] : this.oldChooseUserIdList[1]
//
if (val.includes('-1')) {
this.msgsCenterInfo.userIdList = allValues
}
//
if (oldVal.includes('-1') && !val.includes('-1')) {
this.msgsCenterInfo.userIdList = []
}
//
//
if (oldVal.includes('-1') && val.includes('-1')) {
const index = val.indexOf('-1')
val.splice(index, 1) //
this.msgsCenterInfo.userIdList = val
}
//
if (!oldVal.includes('-1') && !val.includes('-1')) {
if (val.length === allValues.length - 1) {
this.msgsCenterInfo.userIdList = ['-1'].concat(val)
}
}
//
this.oldChooseUserIdList[1] = this.msgsCenterInfo.userIdList
},
roleSelect (val) {
//
const allValues = this.allRoleList.map(item => item.code)
//
const oldVal = this.oldChooseRoleIdList.length === 1 ? [] : this.oldChooseRoleIdList[1]
//
if (val.includes('-1')) {
this.msgsCenterInfo.roleCodeList = allValues
}
//
if (oldVal.includes('-1') && !val.includes('-1')) {
this.msgsCenterInfo.roleCodeList = []
}
//
//
if (oldVal.includes('-1') && val.includes('-1')) {
const index = val.indexOf('-1')
val.splice(index, 1) //
this.msgsCenterInfo.roleCodeList = val
}
//
if (!oldVal.includes('-1') && !val.includes('-1')) {
if (val.length === allValues.length - 1) {
this.msgsCenterInfo.roleCodeList = ['-1'].concat(val)
}
}
//
this.oldChooseRoleIdList[1] = this.msgsCenterInfo.roleCodeList
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.msgsCenterInfo = this.initMsgsCenterInfo()
this.disabledReceiveType = false
//
// this.$nextTick(() =>
// this.$refs.content.setContent('')
// )
//
setTimeout(() => {
this.$refs.content.setContent('')
}, 1000)
},
submitForm (draft) {
const vm = this
console.log(vm.msgsCenterInfo)
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit(draft)
} else {
return false
}
})
},
editSubmit (draft) {
const vm = this
if (vm.type === 'edit') {
vm.update()
} else {
vm.save()
}
},
save () {
const vm = this
vm.disabled = true
msgsApi.save(vm.msgsCenterInfo)
.then((response) => {
vm.disabled = false
const res = response.data
if (res.isSuccess) {
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.reset()
vm.$router.push('/msgs/myMsgs')
}
})
},
update () {
const vm = this
vm.disabled = true
msgsApi.update(vm.msgs)
.then((response) => {
vm.disabled = false
const res = response.data
if (res.isSuccess) {
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.reset()
vm.$router.push('/msgs/myMsgs')
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

507
src/views/ceres/sms/manage/Edit.vue

@ -0,0 +1,507 @@
<template>
<div class="app-container">
<el-form
ref="form" :model="smsTask" :rules="rules"
label-position="right"
label-width="100px" status-icon
>
<el-row>
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item :label="$t('table.smsTask.templateId')" prop="templateId">
<el-select
v-model="smsTask.templateId" :disabled="type==='view'" :multiple="false"
filterable
placeholder="请输入关键词" style="width:300px;" @change="changeTemplate"
>
<el-option v-for="item in smsTemplateList" :key="item.id" :label="item.name + '('+item.customCode+')'" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item v-show="type==='view'" :label="$t('table.smsTask.status')" prop="status">
<el-tag :disabled="type==='view'" :type="smsTask.status | statusFilter">{{ smsTask.status.desc }}</el-tag>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('table.smsTask.receiver')" prop="receiver">
<el-tag
v-for="tag in receiverList" :key="tag" :closable="type!=='view'"
:disable-transitions="false"
@close="handleClose(tag)"
>{{ tag }}</el-tag>
<el-input
v-if="receiverVisible" ref="saveTagInput" v-model="receiver"
:disabled="type==='view'"
class="input-new-tag" @blur="handleInputConfirm" @keyup.enter.native="handleInputConfirm"
/>
<el-button v-else :disabled="type==='view'" class="button-new-tag" @click="showInput">添加</el-button>
</el-form-item>
<el-form-item :label="$t('table.smsTask.topic')" prop="topic">
<el-input v-model="smsTask.topic" :disabled="type==='view'" />
</el-form-item>
<el-form-item :label="$t('table.smsTask.content')" prop="content2">
<el-row class="message">
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item v-for="(item, key, index) in smsTask.templateParam" :key="index" :label="key" prop="content">
<el-input :disabled="type==='view'" :value="item" maxlength="255" @input="(value)=>{templateCode(value,key,index)}" />
</el-form-item>
</el-col>
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item label="预览:">
<div class="article" v-html="smsTask.content"></div>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-row>
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item label="定时发送" prop="sendTime">
<el-radio-group v-model="timing" :disabled="type==='view'" size="medium">
<el-radio-button :label="false"></el-radio-button>
<el-radio-button :label="true"></el-radio-button>
</el-radio-group>
<el-date-picker
v-show="timing"
v-model="smsTask.sendTime"
:disabled="type==='view'"
:picker-options="pickerOptions"
align="right"
format="yyyy-MM-dd HH:mm:ss"
placeholder="选择发送时间"
style="margin-left:20px"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
</el-col>
<el-col :sm="12" :xs="24" style="margin-top: 10px;">
<el-form-item v-show="type==='view'" label="是否草稿" prop="draft">
<el-radio-group v-model="smsTask.draft" :disabled="type==='view'" size="medium">
<el-radio-button :label="false"></el-radio-button>
<el-radio-button :label="true"></el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="dialog-footer">
<el-button
v-show="type!=='view'" :disabled="disabled" plain
type="primary"
@click="submitForm(false)"
>立即发送</el-button>
<el-button
v-show="type!=='view'" :disabled="disabled" plain
type="warning"
@click="submitForm(true)"
>存草稿</el-button>
</div>
<aside v-show="type!=='view'" class="tips">
模板提示
<p>1.长度不超过500字单条短信超过70字后按67字/条分多条计费</p>
<p>2.短信模板内容不能包含符号</p>
</aside>
<div v-show="type==='view'">
<send-status-index ref="statusList" :dialog-visible="dialog.isVisible" />
</div>
</div>
</template>
<script>
import smsTemplateApi from '@/api/SmsTemplate.js'
import smsTaskApi from '@/api/SmsTask.js'
import { validMobile } from '@/utils/my-validate'
import SendStatusIndex from './SendStatusIndex'
export default {
name: 'SmsTaskEdit',
components: { SendStatusIndex },
filters: {
statusFilter (status) {
const map = {
WAITING: 'danger',
SUCCESS: 'success',
FAIL: 'error'
}
return map[status] || 'success'
}
},
props: {
},
data () {
return {
dialog: {
isVisible: false
},
type: 'add',
smsTask: this.initSmsTask(),
smsTemplateList: [],
receiverList: [],
receiverVisible: false,
receiver: '',
timing: false,
disabled: false,
smsTemplate: '',
content: '',
rules: {
topic: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' }
],
templateId: { required: true, message: this.$t('rules.require'), trigger: 'blur' },
sendTime: {
validator: (rule, value, callback) => {
const vm = this
if (vm.timing) {
if (vm.smsTask.sendTime) {
callback()
} else {
callback('请选择发送日期')
}
} else {
callback()
}
}, trigger: 'change'
}
},
pickerOptions: {
shortcuts: [{
text: '一小时后',
onClick (picker) {
const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 1)
picker.$emit('pick', date)
}
}, {
text: '明天',
onClick (picker) {
const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 24)
picker.$emit('pick', date)
}
}, {
text: '一周后',
onClick (picker) {
const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 24 * 7)
picker.$emit('pick', date)
}
}]
}
}
},
computed: {
},
watch: {
$route () {
if (this.$route.path === '/sms/manage/edit') {
this.initSmsTemplateList()
this.loadSendStatus()
}
}
},
mounted () {
// vuemountmountasyncmount
this.initSmsTemplateList()
this.loadSendStatus()
},
methods: {
loadSendStatus () {
const type = this.$route.query.type
const id = this.$route.query.id
if (type === 'view') {
this.$refs.statusList.setTaskId(id)
}
},
async loadSmsTask () {
const type = this.$route.query.type
const id = this.$route.query.id
this.type = type
if (type) { //
// this.smsTask = this.initSmsTask()
this.reset()
}
if (type === 'view') {
this.disabled = true
} else {
this.disabled = false
}
if (id) {
await smsTaskApi.get(id)
.then(response => {
const res = response.data
this.smsTask = { ...this.smsTask, ...res.data }
if (type !== 'edit') {
this.smsTask.id = ''
}
this.changeTemplate(this.smsTask.templateId)
this.receiverList = this.smsTask.receiver.split(",")
if (this.smsTask.templateParams) {
this.smsTask.templateParam = JSON.parse(this.smsTask.templateParams)
}
this.smsTask.content = res.data.content
console.log('查询')
if (this.smsTask.sendTime) {
this.timing = true
} else {
this.timing = false
}
this.smsTemplate = this.smsTemplateList.find(item => item.id === this.smsTask.templateId)
})
}
},
changeTemplate (id) {
const vm = this
// vm.preSearch()
if (id) {
//
for (const item of vm.smsTemplateList) {
if (item.id === id) {
let templateParam = {}
if (typeof (item.templateParams) === 'string') {
templateParam = JSON.parse(item.templateParams)
} else {
templateParam = item.templateParams
}
for (const prop in templateParam) {
templateParam[prop] = ''
}
vm.smsTemplate = item
if (vm.type !== 'view') {
console.log('赋值')
vm.smsTask.templateParam = templateParam
// vm.smsTask.content = item.content
this.content = item.content
}
break
}
}
vm.changeContent()
}
},
//
templateCode (val, key) {
const vm = this
vm.smsTask.templateParam[key] = val
vm.changeContent()
},
//
changeContent () {
const vm = this
if (!vm.smsTemplate) {
return
}
const type = vm.smsTemplate.providerType.code
let content = vm.smsTemplate.content
for (const key in vm.smsTask.templateParam) {
let strs = ''
if (type == "TENCENT") {
strs = '{' + key + '}'
} else {
strs = '${' + key + '}'
}
if (vm.smsTask.templateParam[key]) {
content = content.replace(strs, vm.smsTask.templateParam[key])
}
}
if (vm.type !== 'view') {
console.log('赋值')
vm.smsTask.content = content
}
},
async initSmsTemplateList () {
await smsTemplateApi.page({ current: 1, size: 10000, model: {}})
.then(response => {
const res = response.data
if (res.isSuccess) {
this.smsTemplateList = res.data.records
}
})
await this.loadSmsTask() //
},
initSmsTask () {
return {
templateId: '',
receiver: '',
topic: '',
templateParam: {},
sendTime: null,
content: '',
draft: false,
status: {
code: '',
desc: ''
}
}
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.smsTask = this.initSmsTask()
this.receiverList = []
},
submitForm (draft) {
const vm = this
if (vm.smsTask.templateParam && Object.keys(vm.smsTask.templateParam).length > 0) {
let flag = false
for (const key in vm.smsTask.templateParam) {
if (!vm.smsTask.templateParam[key]) {
flag = true
break
}
}
if (flag) {
vm.$message({
message: '发送内容不能为空',
type: 'error'
})
return
}
} else {
vm.$message({
message: '发送内容不能为空',
type: 'error'
})
return
}
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit(draft)
} else {
return false
}
})
},
editSubmit (draft) {
const vm = this
vm.smsTask.draft = draft
vm.smsTask.receiver = vm.receiverList.join(',')
if (!vm.timing) {
vm.smsTask.sendTime = null
}
if (vm.type === 'edit') {
vm.update()
} else {
vm.save()
}
},
save () {
const vm = this
vm.disabled = true
smsTaskApi.save(vm.smsTask)
.then((response) => {
vm.disabled = false
const res = response.data
if (res.isSuccess) {
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.reset()
vm.$router.push('/sms/manage')
}
})
},
update () {
const vm = this
vm.disabled = true
smsTaskApi.update(vm.smsTask)
.then((response) => {
vm.disabled = false
const res = response.data
if (res.isSuccess) {
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.reset()
vm.$router.push('/sms/manage')
}
})
},
handleClose (tag) {
this.receiverList.splice(this.receiverList.indexOf(tag), 1)
},
showInput () {
this.receiverVisible = true
this.$nextTick(() => {
this.$refs.saveTagInput.$refs.input.focus()
})
},
handleInputConfirm () {
const vm = this
//
const inputValue = vm.receiver
if (inputValue) {
if (!validMobile(inputValue)) {
this.$message({
message: '该手机号不合法',
type: 'error'
})
vm.$refs.saveTagInput.focus()
return
}
if (this.receiverList.indexOf(inputValue) === -1) {
vm.receiverList.push(inputValue)
vm.receiverVisible = false
vm.receiver = ''
} else {
this.$message({
message: '该账号已经存在',
type: 'error'
})
vm.$refs.saveTagInput.focus()
}
} else {
this.receiverVisible = false
}
}
}
}
</script>
<style lang="scss" scoped>
.el-tag {
margin-right: 10px;
}
.button-new-tag {
margin-left: 10px;
height: 32px;
line-height: 30px;
padding-top: 0;
padding-bottom: 0;
}
.input-new-tag {
width: 120px;
vertical-align: bottom;
}
.message {
border: 1px solid #ddd;
padding-bottom: 10px;
}
aside {
margin-top: 10px;
margin-bottom: 0;
}
.tips {
border: 1px solid #ddd;
margin-left: 18px;
}
.tips p {
text-indent: 20px;
padding: 0;
margin: 0px;
}
.article {
font-size: 12px;
height: auto;
}
</style>

431
src/views/ceres/sms/manage/Index.vue

@ -0,0 +1,431 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.templateId" :placeholder="$t('table.smsTask.templateId')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.topic" :placeholder="$t('table.smsTask.topic')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.content" :placeholder="$t('table.smsTask.content')"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
<router-link :to="{path:'/sms/manage/edit',query: {type: 'add'}}">
<el-button v-has-permission="['sms:manage:add']" class="filter-item" plain type="danger">
{{ $t("table.add") }}
</el-button>
</router-link>
<el-dropdown v-has-any-permission="['sms:manage:delete','sms:manage:export']" class="filter-item" trigger="click">
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['sms:manage:delete']" @click.native="batchDelete">{{ $t('table.delete')
}}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:manage:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:manage:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:manage:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.smsTask.templateId')" :show-overflow-tooltip="true" align="center"
prop="templateId" width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.templateId }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.smsTask.receiver')" :show-overflow-tooltip="true" align="center"
prop="receiver"
>
<template slot-scope="scope">
<span>{{ scope.row.receiver }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTask.topic')" :show-overflow-tooltip="true" align="center" width="100px">
<template slot-scope="scope">
<span>{{ scope.row.topic }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTask.content')" :show-overflow-tooltip="true" align="center">
<template slot-scope="scope">
<span>{{ scope.row.content }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false" :filters="statusFilters" :label="$t('table.smsTask.status')"
:show-overflow-tooltip="true" class-name="status-col" column-key="status"
width="100px"
>
<template slot-scope="scope">
<span v-if="scope.row.sendTime">
<el-tooltip :content="'发送时间: '+ scope.row.sendTime" class="item" effect="dark" placement="top">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status.desc }}</el-tag>
</el-tooltip>
</span>
<span v-else>
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status.desc }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="[{ text: $t('common.yes'), value: 'true' }, { text: $t('common.no'), value: 'false' }]"
:label="$t('table.smsTask.draft')"
align="center"
column-key="draft"
prop="draft"
width="100px"
>
<template slot-scope="scope">
<span>
<el-tag slot :type="scope.row.draft?'danger':'success'">{{ scope.row.draft ? '是' : '否' }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" align="center" sortable="custom"
prop="createTime"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" align="center" column-key="operation"
class-name="small-padding fixed-width" width="100px"
>
<template slot-scope="{row}">
<i
v-hasPermission="['sms:manage:view']" class="el-icon-view table-operation" style="color: #2db7f5;"
@click="view(row)"
></i>
<i
v-show="row.draft" v-hasPermission="['sms:manage:update']" class="el-icon-edit table-operation"
style="color: #2db7f5;" @click="edit(row)"
></i>
<i
v-hasPermission="['sms:manage:add']" class="el-icon-copy-document table-operation" style="color: #909399;"
@click="copy(row)"
></i>
<i
v-hasPermission="['sms:manage:delete']" class="el-icon-delete table-operation" style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link
v-has-no-permission="['sms:manage:update','sms:manage:delete','sms:manage:add','sms:manage:view']"
class="no-perm"
>
{{ $t('tips.noPermission') }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import smsTaskApi from '@/api/SmsTask.js'
import { convertEnum } from '@/utils/utils'
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile, initMsgsEnums, initQueryParams } from '@/utils/commons'
export default {
name: 'StationManage',
directives: { elDragDialog },
components: { Pagination, FileImport },
filters: {
statusFilter(status) {
const map = {
WAITING: 'danger',
SUCCESS: 'success',
FAIL: 'error'
}
return map[status] || 'success'
}
},
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/msgs/smsTask/import`
},
tableKey: 0,
queryParams: initQueryParams({}),
selection: [],
//
loading: false,
tableData: {
total: 0
},
enums: {
TaskStatus: {}
}
}
},
computed: {
statusFilters() {
return convertEnum(this.enums.TaskStatus)
}
},
watch: {
'$route'(to) {
if (to.path === '/sms/manage') {
this.fetch()
}
}
},
mounted() {
initMsgsEnums(['TaskStatus'], this.enums)
this.fetch()
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
smsTaskApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
smsTaskApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const ids = []
this.selection.forEach((u) => {
ids.push(u.id)
})
this.delete(ids)
}).catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
smsTaskApi.delete({ 'ids': ids })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
copy(row) {
this.$router.push({
path: '/sms/manage/edit',
query: {
id: row.id,
type: 'copy'
}
})
},
view(row) {
this.$router.push({
path: '/sms/manage/edit',
query: {
id: row.id,
type: 'view'
}
})
},
edit(row) {
this.$router.push({
path: '/sms/manage/edit',
query: {
id: row.id,
type: 'edit'
}
})
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
smsTaskApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

249
src/views/ceres/sms/manage/SendStatusIndex.vue

@ -0,0 +1,249 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.receiver" :placeholder="$t('table.smsSendStatus.receiver')"
class="filter-item search-item"
/>
<el-input
v-model="queryParams.model.bizId" :placeholder="$t('table.smsSendStatus.bizId')"
class="filter-item search-item"
/>
<el-input v-model="queryParams.model.ext" :placeholder="$t('table.smsSendStatus.ext')" class="filter-item search-item" />
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.smsSendStatus.receiver')" :show-overflow-tooltip="true" align="center"
prop="receiver" width="120px"
>
<template slot-scope="scope">
<span>{{ scope.row.receiver }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="sendStatusFilters"
:label="$t('table.smsSendStatus.sendStatus')"
:show-overflow-tooltip="true"
align="center"
column-key="sendStatus"
prop="sendStatus"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.sendStatus.desc }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.smsSendStatus.bizId')" :show-overflow-tooltip="true" align="center"
prop="bizId"
>
<template slot-scope="scope">
<span>{{ scope.row.bizId }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.smsSendStatus.ext')" :show-overflow-tooltip="true" align="center"
prop="ext"
width="150px"
>
<template slot-scope="scope">
<span>{{ scope.row.ext }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.smsSendStatus.code')" :show-overflow-tooltip="true" align="center"
prop="code"
width="120px"
>
<template slot-scope="scope">
<span>{{ scope.row.code }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsSendStatus.message')" :show-overflow-tooltip="true" align="center">
<template slot-scope="scope">
<span>{{ scope.row.message }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsSendStatus.fee')" align="center" width="80px">
<template slot-scope="scope">
<span>{{ scope.row.fee }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" align="center" prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)" @pagination="fetch"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import smsSendStatusApi from '@/api/SmsSendStatus.js'
import { convertEnum } from '@/utils/utils'
import { initMsgsEnums, initQueryParams } from '@/utils/commons'
export default {
name: 'SmsSendStatusManage',
components: { Pagination },
filters: {
statusFilter(status) {
const map = {
false: 'danger',
true: 'success'
}
return map[status] || 'success'
}
},
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data() {
return {
dialog: {
isVisible: false,
type: 'add'
},
tableKey: 0,
queryParams: initQueryParams({
model: { taskId: 0 }
}),
selection: [],
//
loading: false,
tableData: {
total: 0
},
enums: { SendStatus: {}}
}
},
computed: {
sendStatusFilters() {
return convertEnum(this.enums.SendStatus)
},
isVisible: {
get() {
return this.dialogVisible
},
set() {
// this.close()
// this.reset()
}
}
},
mounted() {
initMsgsEnums('SendStatus', this.enums)
},
methods: {
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
setTaskId(taskId) {
this.queryParams.model.taskId = taskId
this.fetch({
...this.queryParams
})
},
reset() {
const taskId = this.queryParams.model.taskId
this.queryParams = initQueryParams({
model: { taskId: taskId }
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
clearSelections() {
this.$refs.table.clearSelection()
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
smsSendStatusApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

238
src/views/ceres/sms/template/Edit.vue

@ -0,0 +1,238 @@
<template>
<el-dialog
:close-on-click-modal="false" :close-on-press-escape="true" :title="title"
:type="type"
:visible.sync="isVisible" :width="width" top="50px"
>
<el-form
ref="form" :model="smsTemplate" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.smsTemplate.providerType')" prop="providerType">
<el-select v-model="smsTemplate.providerType.code" placeholder style="width:100%" value>
<el-option v-for="(item, key, index) in enums.ProviderType" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.appId')" prop="appId">
<el-input v-model="smsTemplate.appId" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.appSecret')" prop="appSecret">
<el-input v-model="smsTemplate.appSecret" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.url')" prop="url">
<el-input v-model="smsTemplate.url" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.customCode')" prop="customCode">
<el-input v-model="smsTemplate.customCode" :disabled="type === 'edit'" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.name')" prop="name">
<el-input v-model="smsTemplate.name" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.content')" prop="content">
<el-input v-model="smsTemplate.content" />
<aside>
百度云使用 ${xx} 作为占位符
<br />
阿里云使用 ${xx} 作为占位符
<br />
腾讯云使用 {xx} 作为占位符
</aside>
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.templateCode')" prop="templateCode">
<el-input v-model="smsTemplate.templateCode" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.signName')" prop="signName">
<el-input v-model="smsTemplate.signName" />
</el-form-item>
<el-form-item :label="$t('table.smsTemplate.templateDescribe')" prop="templateDescribe">
<el-input v-model="smsTemplate.templateDescribe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="primary" @click="submitForm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</template>
<script>
import smsTemplateApi from '@/api/SmsTemplate.js'
export default {
name: 'SmsTemplateEdit',
components: {},
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'add'
}
},
data () {
return {
smsTemplate: this.initSmsTemplate(),
screenWidth: 0,
width: this.initWidth(),
enums: {
ProviderType: {}
},
rules: {
providerType: [{ required: true, message: this.$t('rules.require'), trigger: 'change' }],
appId: [{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' }],
appSecret: [{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range4to10'), trigger: 'blur' }],
customCode: [
{ min: 0, max: 20, message: this.$t('rules.range4to10'), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (this.type === 'add' && value.trim() !== '') {
smsTemplateApi.check(value)
.then((response) => {
const res = response.data
if (res.data) {
callback('自定义编码重复')
} else {
callback()
}
})
} else {
callback()
}
}, trigger: 'blur'
}
],
content: { required: true, message: this.$t('rules.require'), trigger: 'blur' },
templateCode: { required: true, message: this.$t('rules.require'), trigger: 'blur' }
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
this.reset()
}
},
title () {
return this.type === 'add' ? this.$t('common.add') : this.$t('common.edit')
}
},
watch: {
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initSmsTemplate () {
return {
id: '',
providerType: { code: '' },
appId: '',
appSecret: '',
url: '',
customCode: '',
name: '',
content: '',
templateParams: '',
templateCode: '',
signName: '',
templateDescribe: ''
}
},
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return '90%'
} else if (this.screenWidth < 1400) {
return '45%'
} else {
return '800px'
}
},
loadListOptions ({ callback }) {
callback()
},
setSmsTemplate (val = {}) {
const vm = this
if (val['row']) {
vm.smsTemplate = { ...val['row'] }
}
vm.enums = val['enums']
},
close () {
this.$emit('close')
},
reset () {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.smsTemplate = this.initSmsTemplate()
},
submitForm () {
const vm = this
this.$refs.form.validate((valid) => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit () {
const vm = this
if (vm.type === 'add') {
vm.save()
} else {
vm.update()
}
},
save () {
const vm = this
smsTemplateApi.save(this.smsTemplate)
.then((response) => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t('tips.createSuccess'),
type: 'success'
})
vm.$emit('success')
}
})
},
update () {
smsTemplateApi.update(this.smsTemplate)
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
this.$emit('success')
}
})
}
}
}
</script>
<style lang="scss" scoped>
aside {
margin-top: 10px;
margin-bottom: 0;
}
</style>

401
src/views/ceres/sms/template/Index.vue

@ -0,0 +1,401 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="queryParams.model.appId" :placeholder="$t('table.smsTemplate.appId')" size="small" class="filter-item search-item" />
<el-input v-model="queryParams.model.customCode" :placeholder="$t('table.smsTemplate.customCode')" size="small" class="filter-item search-item" />
<el-input v-model="queryParams.model.name" :placeholder="$t('table.smsTemplate.name')" size="small" class="filter-item search-item" />
<el-input v-model="queryParams.model.templateCode" :placeholder="$t('table.smsTemplate.templateCode')" size="small" class="filter-item search-item" />
<el-input v-model="queryParams.model.signName" :placeholder="$t('table.smsTemplate.signName')" size="small" class="filter-item search-item" />
<el-date-picker
v-model="queryParams.timeRange"
size="small"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button
size="small" class="filter-item" plain
type="primary"
@click="search"
>{{ $t('table.search') }}</el-button>
<el-button
size="small" class="filter-item" plain
type="warning"
@click="reset"
>{{ $t('table.reset') }}</el-button>
<el-button
v-has-permission="['sms:template:add']" size="small" class="filter-item"
plain
type="danger" @click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown v-has-any-permission="['sms:template:delete','sms:template:export']" class="filter-item" trigger="click">
<el-button size="small">
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['sms:template:delete']" @click.native="batchDelete">{{ $t('table.delete') }}</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:template:export']" @click.native="exportExcel">{{ $t('table.export') }}</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:template:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['sms:template:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" />
<el-table-column
:filter-multiple="false"
:filters="providerTypeFilters"
column-key="providerType"
:label="$t('table.smsTemplate.providerType')"
:show-overflow-tooltip="true"
align="center"
prop="providerType"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.providerType.desc }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.appId')" :show-overflow-tooltip="true" align="center" prop="appId">
<template slot-scope="scope">
<span>{{ scope.row.appId }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.appSecret')" :show-overflow-tooltip="true" align="center" prop="appSecret">
<template slot-scope="scope">
<span>{{ scope.row.appSecret }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.smsTemplate.name')" :show-overflow-tooltip="true" align="center"
prop="name"
width="150px"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.customCode')" :show-overflow-tooltip="true" align="center" prop="customCode">
<template slot-scope="scope">
<span>{{ scope.row.customCode }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.templateCode')" align="center" width="150px">
<template slot-scope="scope">
<span>{{ scope.row.templateCode }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.signName')" align="center" width="150px">
<template slot-scope="scope">
<span>{{ scope.row.signName }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.smsTemplate.templateDescribe')" align="center" width="150px">
<template slot-scope="scope">
<span>{{ scope.row.templateDescribe }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')" align="center" prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')" align="center" column-key="operation"
class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{row}">
<i v-hasPermission="['sms:template:update']" class="el-icon-edit table-operation" style="color: #2db7f5;" @click="edit(row)"></i>
<i v-hasPermission="['sms:template:delete']" class="el-icon-delete table-operation" style="color: #f50;" @click="singleDelete(row)"></i>
<el-link v-has-no-permission="['sms:template:update','sms:template:delete']" class="no-perm">{{ $t('tips.noPermission') }}</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0" :limit.sync="queryParams.size" :page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<sms-template-edit
ref="edit" :dialog-visible="dialog.isVisible" :type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import SmsTemplateEdit from './Edit'
import smsTemplateApi from '@/api/SmsTemplate.js'
import { convertEnum } from '@/utils/utils'
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile, initDicts, initMsgsEnums, initQueryParams } from '@/utils/commons'
export default {
name: 'SmsTemplateManage',
directives: { elDragDialog },
components: { Pagination, SmsTemplateEdit, FileImport },
filters: {
statusFilter (status) {
const map = {
false: 'danger',
true: 'success'
}
return map[status] || 'success'
}
},
data () {
return {
dialog: {
isVisible: false,
type: 'add'
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/msgs/smsTemplate/import`
},
tableKey: 0,
queryParams: initQueryParams({
model: {}
}),
selection: [],
loading: false,
tableData: {
total: 0
},
enums: {
ProviderType: {}
}
}
},
computed: {
providerTypeFilters () {
return convertEnum(this.enums.ProviderType)
}
},
mounted () {
initMsgsEnums(['ProviderType'], this.enums)
this.fetch()
},
methods: {
editClose () {
this.dialog.isVisible = false
},
editSuccess () {
this.search()
},
onSelectChange (selection) {
this.selection = selection
},
search () {
this.fetch({
...this.queryParams
})
},
reset () {
this.queryParams = initQueryParams({
model: {}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
smsTemplateApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
smsTemplateApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete (row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete () {
if (!this.selection.length) {
this.$message({
message: this.$t('tips.noDataSelected'),
type: 'warning'
})
return
}
this.$confirm(this.$t('tips.confirmDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
const ids = []
this.selection.forEach((u) => {
ids.push(u.id)
})
this.delete(ids)
}).catch(() => {
this.clearSelections()
})
},
clearSelections () {
this.$refs.table.clearSelection()
},
delete (ids) {
smsTemplateApi.delete({ ids: ids })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.search()
})
},
add () {
this.dialog.type = 'add'
this.dialog.isVisible = true
this.$refs.edit.setSmsTemplate({ enums: this.enums })
},
edit (row) {
this.$refs.edit.setSmsTemplate({ row, enums: this.enums })
this.dialog.type = 'edit'
this.dialog.isVisible = true
},
fetch (params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
smsTemplateApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

343
src/views/ceres/user/org/Index.vue

@ -0,0 +1,343 @@
<template>
<div class="org">
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="app-container">
<div class="filter-container">
<el-input v-model="label" :placeholder="$t('table.org.label')" class="filter-item search-item" />
<el-button class="filter-item" plain type="primary" @click="search">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t('table.reset') }}</el-button>
<el-button
v-has-permission="['org:add']" class="filter-item" plain
type="danger"
@click="add"
>{{
$t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="['org:delete','org:export', 'org:import']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t('table.more') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['org:delete']" @click.native="deleteOrg">{{ $t('table.delete') }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['org:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['org:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['org:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-tree
ref="orgTree" :check-strictly="true" :data="orgTree"
:filter-node-method="filterNode"
default-expand-all highlight-current node-key="id"
show-checkbox @node-click="nodeClick"
/>
</div>
</el-col>
<el-col :sm="12" :xs="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>{{ org.id === '' ? this.$t('common.add') : this.$t('common.edit') }}</span>
</div>
<div>
<el-form
ref="form" :model="org" :rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.org.parentId')" prop="parentId">
<el-tooltip :content="$t('tips.topId')" class="item" effect="dark" placement="top-start">
<el-input v-model="org.parentId" readonly />
</el-tooltip>
</el-form-item>
<el-form-item :label="$t('table.org.label')" prop="label">
<el-input v-model="org.label" />
</el-form-item>
<el-form-item :label="$t('table.org.abbreviation')" prop="abbreviation">
<el-input v-model="org.abbreviation" />
</el-form-item>
<el-form-item :label="$t('table.org.describe')" prop="describe">
<el-input v-model="org.describe" />
</el-form-item>
<el-form-item :label="$t('table.org.status')" prop="status">
<el-radio-group v-model="org.status">
<el-radio :label="true">{{ $t('common.status.valid') }}</el-radio>
<el-radio :label="false">{{ $t('common.status.invalid') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.org.sortValue')" prop="sortValue">
<el-input-number v-model="org.sortValue" :max="100" :min="0" @change="handleNumChange" />
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="box-card" style="margin-top: -2rem;">
<el-row>
<el-col :span="24" style="text-align: right">
<el-button plain type="primary" @click="submit">{{ org.id === '' ? this.$t('common.add') :
this.$t('common.edit') }}
</el-button>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import orgApi from '@/api/Org.js'
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import { downloadFile } from '@/utils/commons'
export default {
name: 'OrgManager',
directives: { elDragDialog },
components: { FileImport },
data() {
return {
label: '',
orgTree: [],
org: this.initOrg(),
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/org/import`
},
rules: {
label: [
{ required: true, message: this.$t('rules.require'), trigger: 'blur' },
{ min: 1, max: 255, message: this.$t('rules.range3to10'), trigger: 'blur' }
]
}
}
},
mounted() {
this.initOrgTree()
},
methods: {
initOrg() {
return {
id: '',
abbreviation: '',
label: '',
parentId: 0,
status: true,
describe: '',
sortValue: 0
}
},
initOrgTree() {
orgApi.allTree({})
.then((response) => {
const res = response.data
this.orgTree = res.data
})
},
exportExcelPreview() {
const queryParams = {
model: {},
map: {
fileName: '导出组织数据'
},
size: 10000
}
orgApi.preview(queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
const queryParams = {
model: {},
map: {
fileName: '导出组织数据'
},
size: 10000
}
orgApi.export(queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.initOrgTree()
},
importClose() {
this.fileImport.isVisible = false
},
handleNumChange(val) {
this.org.sortValue = val
},
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
nodeClick(data) {
this.org = { ...data }
this.$refs.form.clearValidate()
},
add() {
this.resetForm()
const checked = this.$refs.orgTree.getCheckedKeys()
if (checked.length > 1) {
this.$message({
message: this.$t('tips.onlyChooseOne'),
type: 'warning'
})
} else if (checked.length > 0) {
this.org.parentId = checked[0]
} else {
this.org.parentId = 0
}
},
deleteOrg() {
const checked = this.$refs.orgTree.getCheckedKeys()
if (checked.length === 0) {
this.$message({
message: this.$t('tips.noNodeSelected'),
type: 'warning'
})
} else {
this.$confirm(this.$t('tips.confirmDeleteNode'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
}).then(() => {
orgApi.delete({ ids: checked })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.deleteSuccess'),
type: 'success'
})
}
this.reset()
})
}).catch(() => {
this.$refs.orgTree.setCheckedKeys([])
})
}
},
search() {
this.$refs.orgTree.filter(this.label)
},
reset() {
this.initOrgTree()
this.label = ''
this.resetForm()
},
submit() {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.org.id) {
this.update()
} else {
this.save()
}
} else {
return false
}
})
},
save() {
orgApi.save({ ...this.org })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.createSuccess'),
type: 'success'
})
}
this.reset()
})
},
update() {
orgApi.update({ ...this.org })
.then((response) => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t('tips.updateSuccess'),
type: 'success'
})
}
this.reset()
})
},
resetForm() {
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.org = this.initOrg()
}
}
}
</script>
<style lang="scss" scoped>
.org {
margin: 10px;
.app-container {
margin: 0 0 10px 0 !important;
}
}
.el-card.is-always-shadow {
box-shadow: none;
}
.el-card {
border-radius: 0;
border: none;
.el-card__header {
padding: 10px 20px !important;
border-bottom: 1px solid #f1f1f1 !important;
}
}
</style>

232
src/views/ceres/user/station/Edit.vue

@ -0,0 +1,232 @@
<template>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
:title="title"
:type="type"
:visible.sync="isVisible"
:width="width"
top="50px"
>
<el-form
ref="form"
:model="station"
:rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.station.name')" prop="name">
<el-input v-model="station.name" />
</el-form-item>
<el-form-item :label="$t('table.station.orgId')" prop="orgId">
<treeselect
v-model="station.org.key"
:clear-value-text="$t('common.clear')"
:load-options="loadListOptions"
:multiple="false"
:options="orgList"
:searchable="true"
placeholder=" "
style="width:100%"
/>
</el-form-item>
<el-form-item :label="$t('table.station.status')" prop="status">
<el-radio-group v-model="station.status">
<el-radio :label="true">{{ $t("common.status.valid") }}</el-radio>
<el-radio :label="false">{{ $t("common.status.invalid") }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.station.describe')" prop="describe">
<el-input v-model="station.describe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{
$t("common.cancel")
}}</el-button>
<el-button plain type="primary" @click="submitForm">{{
$t("common.confirm")
}}</el-button>
</div>
</el-dialog>
</template>
<script>
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import stationApi from "@/api/Station.js"
export default {
name: "StationEdit",
components: { Treeselect },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: "add"
}
},
data() {
return {
remoteStationLoading: false,
station: this.initStation(),
screenWidth: 0,
width: this.initWidth(),
orgList: [],
stationList: [],
rules: {
name: [
{
required: true,
message: this.$t("rules.require"),
trigger: "blur"
},
{
min: 1,
max: 255,
message: this.$t("rules.range4to10"),
trigger: "blur"
},
{
validator: (rule, value, callback) => {
if (!this.station.id) {
// this.$get(`system/user/check/${value}`).then((r) => {
// if (!r.data) {
// callback(this.$t('rules.usernameExist'))
// } else {
// callback()
// }
// })
} else {
// callback()
}
callback()
},
trigger: "blur"
}
],
status: {
required: true,
message: this.$t("rules.require"),
trigger: "blur"
}
}
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.type === "add"
? this.$t("common.add")
: this.$t("common.edit")
}
},
watch: {},
mounted() {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initStation() {
return {
id: "",
name: "",
org: {
key: null,
data: null
},
status: true,
describe: ""
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return "90%"
} else if (this.screenWidth < 1400) {
return "45%"
} else {
return "800px"
}
},
loadListOptions({ callback }) {
callback()
},
setStation(val, orgs) {
const vm = this
vm.orgList = orgs
if (val) {
vm.station = { ...val }
}
},
close() {
this.$emit("close")
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.station = this.initStation()
},
submitForm() {
const vm = this
this.$refs.form.validate(valid => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
if (vm.type === "add") {
vm.save()
} else {
vm.update()
}
},
save() {
const vm = this
stationApi.save(this.station).then(response => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t("tips.createSuccess"),
type: "success"
})
vm.$emit("success")
}
})
},
update() {
stationApi.update(this.station).then(response => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t("tips.updateSuccess"),
type: "success"
})
this.$emit("success")
}
})
}
}
}
</script>
<style lang="scss" scoped></style>

453
src/views/ceres/user/station/Index.vue

@ -0,0 +1,453 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.name" :placeholder="$t('table.station.name')"
class="filter-item search-item"
/>
<treeselect
v-model="queryParams.model.orgId.key"
:clear-value-text="$t('common.clear')"
:load-options="loadListOptions"
:multiple="false"
:options="orgList"
:placeholder="$t('table.station.orgId')"
:searchable="true"
class="filter-item search-item"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">{{ $t("table.search") }}</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">{{ $t("table.reset") }}</el-button>
<el-button
v-has-permission="['station:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="['station:delete','station:export','station:import']" class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['station:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['station:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['station:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['station:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.station.name')"
:show-overflow-tooltip="true"
align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.station.describe')"
:show-overflow-tooltip="true"
align="center"
prop="describe"
>
<template slot-scope="scope">
<span>{{ scope.row.describe }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.station.orgId')"
align="center"
:show-overflow-tooltip="true"
width="180px"
>
<template slot-scope="scope">
<span>
{{ scope.row.org.data ? scope.row.org.data.label : scope.row.org.key }}
</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
column-key="status"
:filters="[
{ text: $t('common.status.valid'), value: true },
{ text: $t('common.status.invalid'), value: false }
]"
:label="$t('table.station.status')"
class-name="status-col"
width="70px"
>
<template slot-scope="{ row }">
<el-tag :type="row.status | statusFilter">{{
row.status ? $t("common.status.valid") : $t("common.status.invalid")
}}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
align="center"
column-key="operation"
class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['station:update']"
class="el-icon-edit table-operation"
style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-hasPermission="['station:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link
v-has-no-permission="['station:update', 'station:delete']"
class="no-perm"
>{{ $t("tips.noPermission") }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<station-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible"
:type="fileImport.type" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import Pagination from "@/components/Pagination"
import StationEdit from "./Edit"
import stationApi from "@/api/Station.js"
import orgApi from "@/api/Org.js"
import { downloadFile, initQueryParams } from '@/utils/commons'
export default {
name: "StationManage",
directives: { elDragDialog },
components: { Pagination, StationEdit, Treeselect, FileImport },
filters: {
statusFilter(status) {
const map = {
false: "danger",
true: "success"
}
return map[status] || "success"
}
},
data() {
return {
dialog: {
isVisible: false,
type: "add"
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/station/import`
},
tableKey: 0,
orgList: [],
queryParams: initQueryParams({
model: {
orgId: {
key: null
}
}
}),
selection: [],
loading: false,
tableData: {
total: 0
}
}
},
computed: {},
watch: {
$route() {
if (this.$route.path === "/user/station") {
this.initOrg()
}
}
},
mounted() {
this.initOrg()
this.fetch()
},
methods: {
initOrg() {
orgApi
.allTree({ status: true })
.then(response => {
const res = response.data
this.orgList = res.data
})
.catch(() => {
this.$message({
message: this.$t("tips.getDataFail"),
type: "error"
})
})
},
loadListOptions({ callback }) {
callback()
},
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {
orgId: { key: null }
}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出岗位数据'
stationApi.preview({ ...this.queryParams, ...{ size: 10000 }}).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出岗位数据'
stationApi.export({ ...this.queryParams, ...{ size: 10000 }}).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
this.delete(ids)
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
stationApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
add() {
this.dialog.type = "add"
this.dialog.isVisible = true
this.$refs.edit.setStation(false, this.orgList)
},
edit(row) {
this.$refs.edit.setStation(row, this.orgList)
this.dialog.type = "edit"
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
stationApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped></style>

427
src/views/ceres/user/user/Edit.vue

@ -0,0 +1,427 @@
<template>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
:title="title"
:type="type"
:visible.sync="isVisible"
:width="width"
top="50px"
>
<el-form
ref="form"
:model="user"
:rules="rules"
label-position="right"
label-width="100px"
>
<el-form-item :label="$t('table.user.account')" prop="account">
<el-input
v-model="user.account"
:readonly="type === 'add' ? false : 'readonly'"
/>
</el-form-item>
<el-form-item :label="$t('table.user.name')" prop="name">
<el-input v-model="user.name" />
</el-form-item>
<el-form-item
v-show="type === 'add'"
:label="$t('table.user.password')"
prop="password"
>
<el-tooltip
:content="$t('tips.defaultPassword')"
class="item"
effect="dark"
placement="top-start"
>
<el-input type="password" value="123456" />
</el-tooltip>
</el-form-item>
<el-form-item :label="$t('table.user.avatar')" prop="avatar">
<imgUpload
ref="imgFileRef"
:accept="accept"
:accept-size="2 * 1024 * 1024"
:auto-upload="true"
:data="user.avatar"
:file-list="imgFileList"
:show-file-list="false"
list-type="picture-card"
@setId="setIdAndSubmit"
>
<i class="el-icon-plus"></i>
</imgUpload>
</el-form-item>
<el-form-item :label="$t('table.user.orgId')" prop="orgId">
<treeselect
v-model="user.org.key"
:clear-value-text="$t('common.clear')"
:load-options="loadListOptions"
:multiple="false"
:options="orgList"
:searchable="true"
placeholder=" "
style="width:100%"
/>
</el-form-item>
<el-form-item :label="$t('table.user.stationId')" prop="stationId">
<el-select
v-model="user.station.key"
:loading="remoteStationLoading"
:multiple="false"
filterable
placeholder="请输入关键词"
>
<el-option
v-for="item in stationList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('table.user.email')" prop="email">
<el-input v-model="user.email" />
</el-form-item>
<el-form-item :label="$t('table.user.mobile')" prop="mobile">
<el-input v-model="user.mobile" />
</el-form-item>
<el-form-item :label="$t('table.user.sex')" prop="sex">
<el-select v-model="user.sex.code" placeholder style="width:100%" value>
<el-option
v-for="(item, key, index) in enums.Sex" :key="index" :label="item"
:value="key"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('table.user.nation')" prop="nation">
<el-select v-model="user.nation.key" style="width:100%" :placeholder="$t('table.user.nation')" value>
<el-option v-for="(item, key, index) in dicts.NATION" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.user.education')" prop="education">
<el-select v-model="user.education.key" style="width:100%" :placeholder="$t('table.user.education')" value>
<el-option v-for="(item, key, index) in dicts.EDUCATION" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.user.positionStatus')" prop="positionStatus">
<el-select v-model="user.positionStatus.key" style="width:100%" :placeholder="$t('table.user.positionStatus')" value>
<el-option v-for="(item, key, index) in dicts.POSITION_STATUS" :key="index" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.user.status')" prop="status">
<el-radio-group v-model="user.status">
<el-radio :label="true">{{ $t("common.status.valid") }}</el-radio>
<el-radio :label="false">{{ $t("common.status.invalid") }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('table.user.workDescribe')" prop="workDescribe">
<el-input v-model="user.workDescribe" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button plain type="warning" @click="isVisible = false">{{
$t("common.cancel")
}}</el-button>
<el-button plain type="primary" @click="submitForm">{{
$t("common.confirm")
}}</el-button>
</div>
</el-dialog>
</template>
<script>
import { validMobile } from "@/utils/my-validate"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import imgUpload from "@/components/ceres/imgUpload"
import userApi from "@/api/User.js"
import stationApi from "@/api/Station.js"
export default {
name: "UserEdit",
components: { Treeselect, imgUpload },
props: {
dialogVisible: {
type: Boolean,
default: false
},
type: {
type: String,
default: "add"
}
},
data() {
return {
accept: "image/jpeg, image/gif, image/png",
remoteStationLoading: false,
user: this.initUser(),
screenWidth: 0,
width: this.initWidth(),
orgList: [],
stationList: [],
imgFileList: [],
imgFileData: {
bizId: "",
bizType: "USER_AVATAR"
},
//
imgFileTotal: 0,
//
successNum: 0,
enums: {
Sex: {}
},
dicts: {
NATION: {},
POSITION_STATUS: {},
EDUCATION: {}
},
rules: {
account: [
{
required: true,
message: this.$t("rules.require"),
trigger: "blur"
},
{
min: 1,
max: 255,
message: this.$t("rules.range4to10"),
trigger: "blur"
},
{
validator: (rule, value, callback) => {
if (!this.user.id) {
callback()
} else {
callback()
}
},
trigger: "blur"
}
],
email: {
type: "email",
message: this.$t("rules.email"),
trigger: "blur"
},
mobile: {
validator: (rule, value, callback) => {
if (value !== "" && !validMobile(value)) {
callback(this.$t("rules.mobile"))
} else {
callback()
}
},
trigger: "blur"
},
sex: {
required: true,
message: this.$t("rules.require"),
trigger: "change"
},
status: {
required: true,
message: this.$t("rules.require"),
trigger: "blur"
}
}
}
},
computed: {
isVisible: {
get() {
return this.dialogVisible
},
set() {
this.close()
this.reset()
}
},
title() {
return this.type === "add"
? this.$t("common.add")
: this.$t("common.edit")
}
},
watch: {
// deptId
"user.org.key": "orgSelect"
},
mounted() {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
myAvatar(avatar) {
if (!avatar) {
return require(`@/assets/avatar/default.jpg`)
} else {
if (avatar.startsWith("http://") || avatar.startsWith("https://")) {
return avatar
} else {
return require(`@/assets/avatar/${avatar}`)
}
}
},
initUser() {
return {
id: "",
account: "",
name: "",
org: {
key: null
},
station: { key: null },
email: "",
mobile: "",
sex: {
code: "N"
},
nation: {
key: ""
},
education: {
key: ""
},
positionStatus: {
key: ""
},
status: true,
avatar: "",
workDescribe: "",
password: "123456"
}
},
initWidth() {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 991) {
return "90%"
} else if (this.screenWidth < 1400) {
return "45%"
} else {
return "800px"
}
},
loadListOptions({ callback }) {
callback()
},
orgSelect(node) {
this.loadStation(node)
},
loadStation(orgId) {
this.user.station.key = null
if (orgId) {
stationApi.page({
size: 10000,
model: { orgId: { key: orgId }, status: true }
}).then(response => {
const res = response.data
this.stationList = res.data.records
})
} else {
this.stationList = []
}
},
setIdAndSubmit(bizId, url) {
const vm = this
vm.successNum += 1
vm.imgFileData.bizId = bizId
vm.user.avatar = url
vm.user.id = bizId
if (vm.successNum === vm.imgFileTotal) {
vm.$store.state.hasLoading = false
}
},
setUser(val, orgs, dicts, enums) {
const vm = this
if (val) {
vm.user = { ...val }
}
vm.dicts = dicts
vm.enums = enums
vm.orgList = orgs
vm.imgFileData.bizId = vm.user["id"]
vm.$nextTick(() => {
vm.$refs.imgFileRef.init({
bizId: vm.user["id"],
bizType: vm.imgFileData.bizType,
imageUrl: vm.myAvatar(vm.user["avatar"]),
isSingle: true,
isDetail: false
})
})
},
close() {
this.$emit("close")
},
reset() {
// bug
this.$refs.form.clearValidate()
this.$refs.form.resetFields()
this.user = this.initUser()
this.imgFileData.bizId = ""
this.$refs.imgFileRef.init({
bizId: "",
bizType: "",
imageUrl: "",
isSingle: true,
isDetail: false
})
},
submitForm() {
const vm = this
this.$refs.form.validate(valid => {
if (valid) {
vm.editSubmit()
} else {
return false
}
})
},
editSubmit() {
const vm = this
if (vm.type === "add") {
vm.save()
} else {
vm.update()
}
},
save() {
const vm = this
userApi.save(this.user).then(response => {
const res = response.data
if (res.isSuccess) {
vm.isVisible = false
vm.$message({
message: vm.$t("tips.createSuccess"),
type: "success"
})
vm.$emit("success")
}
})
},
update() {
userApi.update(this.user).then(response => {
const res = response.data
if (res.isSuccess) {
this.isVisible = false
this.$message({
message: this.$t("tips.updateSuccess"),
type: "success"
})
this.$emit("success")
}
})
}
}
}
</script>
<style lang="scss" scoped></style>

705
src/views/ceres/user/user/Index.vue

@ -0,0 +1,705 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="queryParams.model.account"
:placeholder="$t('table.user.account')" class="filter-item search-item" clearable
/>
<el-select
v-model="queryParams.model.nation.key" clearable :placeholder="$t('table.user.nation')"
class="filter-item search-item"
>
<el-option v-for="(item, key, index) in dicts.NATION" :key="index" :label="item" :value="key" />
</el-select>
<treeselect
v-model="queryParams.model.org.key"
clearable
:clear-value-text="$t('common.clear')"
:load-options="loadListOptions"
:multiple="false"
:options="orgList"
:searchable="true"
class="filter-item search-item"
placeholder="组织"
/>
<el-date-picker
v-model="queryParams.timeRange"
:range-separator="null"
class="filter-item search-item date-range-item"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
type="daterange"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button class="filter-item" plain type="primary" @click="search">
{{ $t("table.search") }}
</el-button>
<el-button class="filter-item" plain type="warning" @click="reset">
{{ $t("table.reset") }}
</el-button>
<el-button
v-has-permission="['user:add']" class="filter-item" plain
type="danger"
@click="add"
>
{{ $t("table.add") }}
</el-button>
<el-dropdown
v-has-any-permission="[
'user:delete',
'user:rest',
'user:export',
'user:import',
]" class="filter-item"
trigger="click"
>
<el-button>
{{ $t("table.more") }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-has-permission="['user:delete']" @click.native="batchDelete">
{{ $t("table.delete") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['user:reset']" @click.native="resetPassword">
{{ $t("table.resetPassword") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['user:export']" @click.native="exportExcel">
{{ $t("table.export") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['user:export']" @click.native="exportExcelPreview">
{{ $t("table.exportPreview") }}
</el-dropdown-item>
<el-dropdown-item v-has-permission="['user:import']" @click.native="importExcel">
{{ $t("table.import") }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table
:key="tableKey"
ref="table"
v-loading="loading"
:data="tableData.records"
border
fit
row-key="id" style="width: 100%;" @filter-change="filterChange"
@selection-change="onSelectChange"
@sort-change="sortChange"
@cell-click="cellClick"
>
<el-table-column align="center" type="selection" width="40px" :reserve-selection="true" />
<el-table-column
:label="$t('table.user.avatar')"
align="center"
prop="avatar"
width="100px"
>
<template slot-scope="scope">
<el-avatar
:key="scope.row.avatar"
:src="myAvatar(scope.row.avatar)"
fit="fill"
>
<el-avatar>{{ scope.row.name | userAvatarFilter }}</el-avatar>
</el-avatar>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.account')"
:show-overflow-tooltip="true"
align="center"
prop="account"
>
<template slot-scope="scope">
<span>{{ scope.row.account }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.name')"
:show-overflow-tooltip="true"
align="center"
prop="name"
>
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="sexList"
column-key="sex.code"
:label="$t('table.user.sex')"
class-name="status-col"
prop="sex.desc"
width="70px"
>
<template slot-scope="{ row }">
<el-tag :type="row.sex ? row.sex['code'] :'' | sexFilter">{{ row.sex ? row.sex.desc : '' }}</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.email')"
:show-overflow-tooltip="true"
align="center"
>
<template slot-scope="scope">
<span>{{ scope.row.email }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.nation')"
:show-overflow-tooltip="true"
align="center"
width="80px"
>
<template slot-scope="scope">
<span>{{ scope.row.nation['data'] ? scope.row.nation['data'] : scope.row.nation['key'] }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="educationList"
column-key="education.key"
:label="$t('table.user.education')"
:show-overflow-tooltip="true"
align="center"
width="80px"
>
<template slot-scope="scope">
<span>{{ scope.row.education['data'] ? scope.row.education['data'] : scope.row.education['key'] }}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
:filters="positionStatusList"
column-key="positionStatus.key"
:label="$t('table.user.positionStatus')"
:show-overflow-tooltip="true"
align="center"
width="100px"
>
<template slot-scope="scope">
<span>{{ scope.row.positionStatus['data'] ? scope.row.positionStatus['data'] : scope.row.positionStatus['key'] }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.orgId')"
align="center"
:show-overflow-tooltip="true"
width="100px"
>
<template slot-scope="scope">
<span>{{
scope.row.org["data"] ? scope.row.org.data.label : scope.row.org.key
}}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.stationId')"
align="center"
:show-overflow-tooltip="true"
width="100px"
>
<template slot-scope="scope">
<span>{{
scope.row.station["data"]
? scope.row.station.data
: scope.row.station.key
}}</span>
</template>
</el-table-column>
<el-table-column
:filter-multiple="false"
column-key="status"
:filters="[
{ text: $t('common.status.valid'), value: true },
{ text: $t('common.status.invalid'), value: false }
]"
:label="$t('table.user.status')"
class-name="status-col"
width="70px"
>
<template slot-scope="{ row }">
<el-tag :type="row.status | statusFilter">{{
row.status ? $t("common.status.valid") : $t("common.status.invalid")
}}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('table.user.createTime')"
align="center"
prop="createTime"
sortable="custom"
width="170px"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('table.operation')"
align="center"
column-key="operation"
class-name="small-padding fixed-width"
width="100px"
>
<template slot-scope="{ row }">
<i
v-hasPermission="['user:view']"
class="el-icon-view table-operation"
style="color: #87d068;"
@click="view(row)"
></i>
<i
v-hasPermission="['user:update']"
class="el-icon-edit table-operation"
style="color: #2db7f5;"
@click="edit(row)"
></i>
<i
v-hasPermission="['user:delete']"
class="el-icon-delete table-operation"
style="color: #f50;"
@click="singleDelete(row)"
></i>
<el-link
v-has-no-permission="['user:view', 'user:update', 'user:delete']"
class="no-perm"
>{{ $t("tips.noPermission") }}
</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="queryParams.size"
:page.sync="queryParams.current"
:total="Number(tableData.total)"
@pagination="fetch"
/>
<user-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:type="dialog.type"
@close="editClose"
@success="editSuccess"
/>
<user-view
ref="view"
:dialog-visible="userViewVisible"
@close="viewClose"
/>
<file-import
ref="import"
:dialog-visible="fileImport.isVisible" :type="fileImport.type"
:export-error-url="fileImport.exportErrorUrl" :action="fileImport.action"
accept=".xls,.xlsx"
@close="importClose"
@success="importSuccess"
/>
<el-dialog
v-el-drag-dialog
:close-on-click-modal="false"
:close-on-press-escape="true"
title="预览"
width="80%"
top="50px"
:visible.sync="preview.isVisible"
>
<el-scrollbar>
<div v-html="preview.context"></div>
</el-scrollbar>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import elDragDialog from '@/directive/el-drag-dialog'
import FileImport from "@/components/ceres/Import"
import UserEdit from "./Edit"
import UserView from "./View"
import userApi from "@/api/User.js"
import orgApi from "@/api/Org.js"
import { convertEnum } from '@/utils/utils'
import { downloadFile, initDicts, initEnums, initQueryParams } from '@/utils/commons'
export default {
name: "UserManage",
directives: { elDragDialog },
components: { Pagination, UserEdit, UserView, Treeselect, FileImport },
filters: {
userAvatarFilter(name) {
return name.charAt(0)
},
sexFilter(status) {
const map = {
W: "success",
M: "danger",
N: "info"
}
return map[status] || "info"
},
statusFilter(status) {
const map = {
false: "danger",
true: "success"
}
return map[status] || "success"
}
},
data() {
return {
orgList: [],
dialog: {
isVisible: false,
type: "add"
},
preview: {
isVisible: false,
context: ''
},
fileImport: {
isVisible: false,
type: "import",
action: `${process.env.VUE_APP_BASE_API}/authority/user/import`,
exportErrorUrl: `/authority/user/exportError`
},
userViewVisible: false,
tableKey: 0,
queryParams: initQueryParams({
model: {
nation: {
key: ''
},
education: {
key: ''
},
positionStatus: {
key: ''
},
org: {
key: null
},
station: {
key: null
},
sex: {
code: ''
}
}
}),
selection: [],
loading: false,
tableData: {
total: 0
},
enums: {
Sex: {}
},
dicts: {
NATION: {},
POSITION_STATUS: {},
EDUCATION: {}
}
}
},
computed: {
currentUser() {
return this.$store.state.account.user
},
sexList() {
return convertEnum(this.enums.Sex)
},
nationList() {
return convertEnum(this.dicts.NATION)
},
educationList() {
return convertEnum(this.dicts.EDUCATION)
},
positionStatusList() {
return convertEnum(this.dicts.POSITION_STATUS)
}
},
watch: {
$route() {
if (this.$route.path === "/user/user") {
this.initOrg()
}
}
},
mounted() {
initEnums('Sex', this.enums)
initDicts(['NATION', 'POSITION_STATUS', 'EDUCATION'], this.dicts)
this.fetch()
this.initOrg()
},
methods: {
initOrg() {
orgApi.allTree({ status: true }).then(response => {
const res = response.data
this.orgList = res.data
})
},
myAvatar(avatar) {
if (!avatar) {
return require(`@/assets/avatar/default.jpg`)
} else {
if (avatar.startsWith("http://") || avatar.startsWith("https://")) {
return avatar
} else {
return require(`@/assets/avatar/${avatar}`)
}
}
},
viewClose() {
this.userViewVisible = false
},
editClose() {
this.dialog.isVisible = false
},
editSuccess() {
this.search()
},
onSelectChange(selection) {
this.selection = selection
},
loadListOptions({ callback }) {
callback()
},
search() {
this.fetch({
...this.queryParams
})
},
reset() {
this.queryParams = initQueryParams({
model: {
nation: {
key: ''
},
education: {
key: ''
},
positionStatus: {
key: ''
},
org: {
key: null
},
station: {
key: null
},
sex: {
code: ''
}
}
})
this.$refs.table.clearSort()
this.$refs.table.clearFilter()
this.search()
},
exportExcelPreview() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
userApi.preview(this.queryParams).then(response => {
const res = response.data
this.preview.isVisible = true
this.preview.context = res.data
})
},
exportExcel() {
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.map.fileName = '导出用户数据'
userApi.export(this.queryParams).then(response => {
downloadFile(response)
})
},
importExcel() {
this.fileImport.type = "upload"
this.fileImport.isVisible = true
this.$refs.import.setModel(false)
},
importSuccess() {
this.search()
},
importClose() {
this.fileImport.isVisible = false
},
resetPassword() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
this.$confirm(
this.$t("tips.confirmRestPassword"),
this.$t("common.tips"),
{
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
}
)
.then(() => {
const ids = []
this.selection.forEach(u => {
ids.push(u.id)
})
userApi.reset({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.resetPasswordSuccess"),
type: "success"
})
}
this.clearSelections()
})
})
.catch(() => {
this.clearSelections()
})
},
singleDelete(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row, true)
this.batchDelete()
},
batchDelete() {
if (!this.selection.length) {
this.$message({
message: this.$t("tips.noDataSelected"),
type: "warning"
})
return
}
let contain = false
this.$confirm(this.$t("tips.confirmDelete"), this.$t("common.tips"), {
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning"
})
.then(() => {
const ids = []
this.selection.forEach(u => {
if (u.id === this.currentUser.id) {
contain = true
return
}
ids.push(u.id)
})
if (contain) {
this.$message({
message: this.$t("tips.containCurrentUser"),
type: "warning"
})
this.clearSelections()
} else {
this.delete(ids)
}
})
.catch(() => {
this.clearSelections()
})
},
clearSelections() {
this.$refs.table.clearSelection()
},
delete(ids) {
userApi.delete({ ids: ids }).then(response => {
const res = response.data
if (res.isSuccess) {
this.$message({
message: this.$t("tips.deleteSuccess"),
type: "success"
})
}
this.search()
})
},
add() {
this.dialog.type = "add"
this.dialog.isVisible = true
this.$refs.edit.setUser(false, this.orgList, this.dicts, this.enums)
},
view(row) {
this.$refs.view.setUser(row, this.orgList, this.dicts, this.enums)
this.userViewVisible = true
},
edit(row) {
this.$refs.edit.setUser(row, this.orgList, this.dicts, this.enums)
this.dialog.type = "edit"
this.dialog.isVisible = true
},
fetch(params = {}) {
this.loading = true
if (this.queryParams.timeRange) {
this.queryParams.map.createTime_st = this.queryParams.timeRange[0]
this.queryParams.map.createTime_ed = this.queryParams.timeRange[1]
}
this.queryParams.current = params.current ? params.current : this.queryParams.current
this.queryParams.size = params.size ? params.size : this.queryParams.size
userApi.page(this.queryParams).then(response => {
const res = response.data
if (res.isSuccess) {
this.tableData = res.data
}
}).finally(() => this.loading = false)
},
sortChange(val) {
this.queryParams.sort = val.prop
this.queryParams.order = val.order
if (this.queryParams.sort) {
this.search()
}
},
filterChange(filters) {
for (const key in filters) {
if (key.includes('.')) {
const val = {}
val[key.split('.')[1]] = filters[key][0]
this.queryParams.model[key.split('.')[0]] = val
} else {
this.queryParams.model[key] = filters[key][0]
}
}
this.search()
},
cellClick (row, column) {
if (column['columnKey'] === "operation") {
return
}
let flag = false
this.selection.forEach((item) => {
if (item.id === row.id) {
flag = true
this.$refs.table.toggleRowSelection(row)
}
})
if (!flag) {
this.$refs.table.toggleRowSelection(row, true)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

222
src/views/ceres/user/user/View.vue

@ -0,0 +1,222 @@
<template>
<el-dialog :title="$t('common.view')" :visible.sync="isVisible" :width="width" class="user-view">
<el-row :gutter="10">
<el-col :sm="24" :xs="24">
<div class="img-wrapper">
<el-avatar :key="user.avatar" :src="user.avatar" fit="fill">
<el-avatar>{{ user.name | userAvatarFilter }}</el-avatar>
</el-avatar>
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-user"></i>
<span>{{ $t('table.user.account') +':' }}</span>
{{ user.account }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-suitcase"></i>
<span>{{ $t('table.user.name') +':' }}</span>
{{ user.name }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-user"></i>
<span>{{ $t('table.user.email') +':' }}</span>
{{ user.email }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-suitcase"></i>
<span>{{ $t('table.user.mobile') +':' }}</span>
{{ user.mobile }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-trophy"></i>
<span>{{ $t('table.user.orgId') +':' }}</span>
{{ user.orgId }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-phone-outline"></i>
<span>{{ $t('table.user.stationId') +':' }}</span>
{{ user.stationId }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-star-off"></i>
<span>{{ $t('table.user.sex') +':' }}</span>
{{ user.sex.desc }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-bangzhu"></i>
<span>{{ $t('table.user.status') +':' }}</span>
{{ user.status ? $t('common.status.valid') : $t('common.status.invalid') }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-time"></i>
<span>{{ $t('table.user.createTime') +':' }}</span>
{{ user.createTime }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-brush"></i>
<span>{{ $t('table.user.updateTime') +':' }}</span>
{{ user.updateTime }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-date"></i>
<span>{{ $t('table.user.lastLoginTime') +':' }}</span>
{{ user.lastLoginTime ? user.lastLoginTime: $t('tips.neverLogin') }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-date"></i>
<span>{{ $t('table.user.passwordExpireTime') +':' }}</span>
{{ user.passwordExpireTime }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-date"></i>
<span>{{ $t('table.user.passwordErrorLastTime') +':' }}</span>
{{ user.passwordErrorLastTime }}
</div>
</el-col>
<el-col :sm="12" :xs="24">
<div class="view-item">
<i class="el-icon-date"></i>
<span>{{ $t('table.user.passwordErrorNum') +':' }}</span>
{{ user.passwordErrorNum }}
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :sm="24" :xs="24">
<div class="view-item">
<i class="el-icon-document"></i>
<span>{{ $t('table.user.workDescribe') +':' }}</span>
{{ user.workDescribe ? user.workDescribe: $t('tips.nothing') }}
</div>
</el-col>
</el-row>
</el-dialog>
</template>
<script>
export default {
name: 'UserView',
filters: {
userAvatarFilter (name) {
return name ? name.charAt(0) : '无'
}
},
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data () {
return {
screenWidth: 0,
width: this.initWidth(),
user: {
sex: {
desc: ''
}
}
}
},
computed: {
isVisible: {
get () {
return this.dialogVisible
},
set () {
this.close()
}
}
},
mounted () {
window.onresize = () => {
return (() => {
this.width = this.initWidth()
})()
}
},
methods: {
initWidth () {
this.screenWidth = document.body.clientWidth
if (this.screenWidth < 550) {
return '95%'
} else if (this.screenWidth < 990) {
return '580px'
} else if (this.screenWidth < 1400) {
return '600px'
} else {
return '650px'
}
},
setUser (val) {
this.user = { ...val }
},
close () {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.user-view {
.img-wrapper {
text-align: center;
margin-top: -1.5rem;
margin-bottom: 10px;
img {
width: 4rem;
border-radius: 50%;
}
}
.view-item {
margin: 7px;
i {
font-size: 0.97rem;
}
span {
margin-left: 5px;
}
}
}
</style>

235
src/views/finance/overview/index.vue

@ -0,0 +1,235 @@
<template>
<div class="overview_page">
<div class="total_info">
<ul>
<li v-for="(item,index) in totalList" :key="index">
<p>{{ item.value }}</p>
<p>
{{ item.name }}
<i class="el-icon-warning-outline icon" :title="item.tips"></i>
</p>
</li>
</ul>
</div>
<div class="toolbar">
<el-form
:inline="true"
:model="formParams"
>
<el-form-item label="商家名称">
<el-input v-model="formParams.model.tenantName" size="mini" />
</el-form-item>
<el-form-item label="商家编码">
<el-input v-model="formParams.model.tenantCode" size="mini" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="mini"
@click="query"
>
查询
</el-button>
<el-button
plain
size="mini"
@click="reset"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData.records"
border
style="width: 100%"
>
<el-table-column
prop="tenantName"
label="商家名称"
/>
<el-table-column
prop="tenantCode"
label="商家编码"
/>
<el-table-column
:formatter="getPrice"
prop="incomeAmount"
label="营收总额"
/>
<el-table-column
:formatter="getPrice"
prop="freezesAmount"
label="冻结金额"
/>
<el-table-column
:formatter="getPrice"
prop="withdrawAmount"
label="已提金额"
/>
<el-table-column
:formatter="getPrice"
prop="refundAmount"
label="已退款金额"
/>
</el-table>
<pagination
v-show="tableData.total > 0"
:limit.sync="formParams.size"
:page.sync="formParams.current"
:total="Number(tableData.total)"
@pagination="query"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import Finance from '@/api/Finance'
export default {
components: {
Pagination
},
data() {
return {
formParams: {
"current": 1,
"map": {},
"model": {
"canWithdrawAmount": 0,
"freezesAmount": 0,
"incomeAmount": 0,
"refundAmount": 0,
"tenantCode": "",
"tenantName": "",
"withdrawAmount": 0
},
"order": "descending",
"size": 10,
"sort": "id"
},
totalList: [
{ name: '营收总额', value: 0, field: 'incomeAmount', tips: '所有商家支付成功的订单总金额' },
{ name: '冻结总金额', value: 0, field: 'freezesAmount', tips: '所有商家交易尚未完成仍在冻结中的金额' },
{ name: '可提现金额', value: 0, field: 'canWithdrawAmount', tips: '所有商家可以提现但尚未提现的的金额' },
{ name: '已提金额', value: 0, field: 'withdrawAmount', tips: '所有商家的售后订单中产生退款并退款成功的总金额' },
{ name: '已退款金额', value: 0, field: 'refundAmount', tips: '' }
],
tableData: {}
}
},
created() {
this.getDayList()
this.getSummary()
},
methods: {
async getDayList() {
const res = await Finance.getDayList(this.formParams)
const resData = res.data
if (resData.code === 0) {
this.tableData = resData.data
}
},
async getSummary() {
const res = await Finance.getSummary()
const resData = res.data
if (resData.code === 0) {
const o = resData.data
this.totalList.map(item => {
item.value = o[item.field] / 100 || 0
})
}
},
query() {
this.getDayList()
},
getPrice(item, row, value) {
return value / 100
},
reset() {
this.formParams = {
"current": 1,
"map": {},
"model": {
"canWithdrawAmount": 0,
"freezesAmount": 0,
"incomeAmount": 0,
"refundAmount": 0,
"tenantCode": "",
"tenantName": "",
"withdrawAmount": 0
},
"order": "descending",
"size": 10,
"sort": "id"
}
}
}
}
</script>
<style lang='less' scoped>
.overview_page {
margin-top: 20px;
padding: 20px;
background-color: #fff;
height: calc(100% -20px);
.total_info {
border-bottom: 1px solid #E0E5EB;
margin-bottom: 20px;
ul {
overflow: hidden;
display: flex;
list-style: none;
li {
flex: 2.4;
text-align: center;
padding: 20px 0;
p {
margin: 0;
&:nth-child(1) {
font-weight: 600;
font-size: 30px;
color: #ffae11;
margin-bottom: 20px;
}
&:nth-child(2) {
font-size: 18px;
.icon {
&:hover {
cursor: pointer;
}
color: red;
}
}
}
}
}
}
}
/deep/ .el-table {
th {
background: #EEF3FF;
color:#333333;
font-size:16px;
font-weight: 400;
border-color: #E0E5EB;
text-align: center;
}
td {
font-size: 14px;
text-align: center;
color: #666666;
}
.cell {
text-align: center;
white-space: pre-line;/*保留换行符*/
}
}
</style>

88
src/views/management/apply/detail.vue

@ -20,6 +20,29 @@
</p>
</div>
<div v-if="Number(info.organizationType) !== 1" class="main_info">
<p class="detail_title">主体信息</p>
<p v-for="(item, index) in mainInfo" :key="index" class="detail_text">
<span>
<font>*</font>
{{ item.name + ':' }}
</span>
<span>{{ item.value }}</span>
</p>
<p class="detail_img">
<span>
<font>*</font>
营业执照
</span>
<img
v-for="(src,index) in businessImage" :key="'bussiness' + index" :src="src"
:preview="1"
/>
</p>
</div>
<div class="person_info">
<p class="detail_title">个人信息</p>
<p v-for="(item, index) in personInfoList" :key="index" class="detail_text">
@ -34,15 +57,24 @@
<font>*</font>
证件照片
</span>
<img :src="info.idCardCopyFilePath" alt="" />
<img :src="info.idCardNationalFilePath" alt="" />
<img
v-for="(src,index) in idCardCopyFilePath" :key="'id' + index" :src="src"
:preview="2"
/>
<img
v-for="(src,index) in idCardNationalFilePath" :key="'idCard' + index" :src="src"
:preview="3"
/>
</p>
<p v-if="Number(info.organizationType) === 1" class="detail_img">
<span>
<font>*</font>
手持证件照
</span>
<img :src="info.handHoldCardPath" alt="" />
<img
v-for="(src,index) in handHoldCardPath" :key="'hand' + index" :src="src"
:preview="4"
/>
</p>
</div>
@ -90,8 +122,24 @@ export default {
{ name: '生效日期', value: '', field: 'effectTime' },
{ name: '生效时限', value: '', field: 'effectTimeLimit' }
],
mainInfo: [
{ name: '商户名称', value: '', field: 'merchantName' },
{ name: '社会信用代码', value: '', field: 'socialCreditCode' },
{ name: '注册地址', value: '', field: 'companyAddressProvince' },
{ name: '营业期限', value: '', field: 'businessTimeStart' }
],
dialogData: {},
info: {}
info: {},
orgType: {
1: '个人',
2: '个体工商户',
3: '企业',
4: '其他组织'
},
businessImage: [],
idCardCopyFilePath: [],
idCardNationalFilePath: [],
handHoldCardPath: []
}
},
computed: {
@ -111,6 +159,11 @@ export default {
const d = resData.data
vm.info = d
vm.getData(d)
vm.businessImage[0] = d.businessLicenseCopyFilePath
vm.idCardCopyFilePath[0] = d.idCardCopyFilePath
vm.idCardNationalFilePath[0] = d.idCardNationalFilePath
vm.handHoldCardPath[0] = d.handHoldCardPath
this.$previewRefresh()
}
},
getTitle() {
@ -126,15 +179,11 @@ export default {
// const info = vm.$route.query.info
vm.getObjData(vm.shopInfoList, info)
vm.getObjData(vm.personInfoList, info)
vm.getObjData(vm.mainInfo, info)
vm.getObjData(vm.authInfoList, info)
},
getObjData(arr, o) {
const t = {
1: '个人',
2: '个体工商户',
3: '企业',
4: '其他组织'
}
const vm = this
const ob = {
1: '中国大陆居民身份证',
2: '中国香港居民来往内地通行证',
@ -142,10 +191,15 @@ export default {
4: '中国台湾居民来往内地通行证',
5: '其他国家或地区居民护照'
}
const org = {
2: '商户名称',
3: '企业名称',
4: '组织名称'
}
arr.map(item => {
item.value = o[item.field]
if (item.field === 'organizationType') {
item.value = t[Number(o.organizationType || '')]
item.value = vm.orgType[Number(o.organizationType || '')]
}
if (item.field === 'storeAddressProvince') {
item.value = `${o.storeAddressProvince || ''}${o.storeAddressCity || ''}${o.storeAddressDetail || ''}`
@ -162,6 +216,18 @@ export default {
if (item.field === 'effectTime') {
item.value = o.effectDateType === 1 ? o.auditTime : o.effectTime
}
if (item.field === 'merchantName') {
item.name = org[Number(o.organizationType || '')]
}
if (item.field === 'socialCreditCode' ) {
item.name = Number(o.organizationType || '') === 4 ? '机构证件' : '营业执照'
}
if (item.field === 'companyAddressProvince' ) {
item.value = `${o.companyAddressProvince || ''}${o.companyAddressCity || ''}${o.companyAddressDistrict || ''}${o.companyAddress || ''}`
}
if (item.field === 'businessTimeStart') {
item.value = `${o.businessTimeStart || ''}──${o.businessTimeEnd || ''}`
}
})
},
cancel() {

2
src/views/management/apply/dialog.vue

@ -32,7 +32,7 @@
/>
</el-form-item>
<el-form-item label="生效时限" prop="effectTimeLimit">
<el-col :span="22">
<el-col :span="18">
<el-input v-model.number="form.effectTimeLimit" placeholder="请输入生效时限" />
</el-col>
<el-col :span="2">

2
src/views/management/apply/index.vue

@ -111,7 +111,7 @@
v-if="scope.row.auditState===2 || scope.row.auditState===3"
type="text"
size="small"
@click.native.prevent="details(scope.row)"
@click.native.prevent="handle(scope.row)"
>
详情
</el-button>

11
src/views/management/merchantList/index.vue

@ -5,7 +5,7 @@
@query="query"
@addManagement="addManagement"
/>
<Container :data-list="dataList" />
<Container :data-list="dataList" @fetch="fetch" />
<Dialog
title="新建商家"
:visible.sync="dialogVisible"
@ -32,7 +32,7 @@ export default {
data() {
return {
dialogVisible: false,
dataList: [],
dataList: {},
params: {
current: 1,
map: {},
@ -65,7 +65,12 @@ export default {
async getManagementList() {
const res = await Management.getAllTenant(this.params)
const resData = res.data
this.dataList = resData.data.records || []
this.dataList = resData.data || {}
},
fetch(v) {
Object.assign(this.params, v)
console.log(this.params, v)
this.getManagementList()
}
}
}

320
src/views/menu/empower/index.vue

@ -0,0 +1,320 @@
<template>
<div class="empower_page">
<div class="top_btn">
菜单赋权
<el-button type="primary" class="save_btn" :loading="loading" @click="save">保存</el-button>
</div>
<div class="empower_content">
<!-- <div class="item">
<div class="top_box">
平台菜单
<el-select v-model="type" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="menu_tree">
<el-tree
ref="platFormTree"
:data="platFormMenu"
show-checkbox
default-expand-all
node-key="id"
highlight-current
:props="defaultProps"
/>
</div>
</div> -->
<!-- <div class="item">
<div class="btn_list">
<p @click="empower">
<img :src="right" alt="" />
</p>
<p @click="recycleMenu">
<img :src="left" alt="" />
</p>
</div>
</div> -->
<div class="item">
<div class="top_box">
<el-form inline label-width="100px">
<el-form-item class="select_type">
<el-select v-model="type" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="type" label="商家编号">
<el-col :span="15">
<el-input v-model="form.tenantCode" placeholder="请输入商家编号" />
</el-col>
<el-col :span="6">
<el-button @click="getSpecialMerchantMenu">搜索</el-button>
</el-col>
</el-form-item>
</el-form>
</div>
<div class="menu_tree">
<el-checkbox v-model="checked" @change="changeCheck">全选</el-checkbox>
<el-tree
ref="merchantTree"
:data="form.menuList"
show-checkbox
default-expand-all
node-key="id"
highlight-current
:props="defaultProps"
@check-change="checkChange"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import Menu from '@/api/Menu'
export default {
data() {
return {
checked: false,
loading: false,
options: [
{ label: '同步所有商家', value: 0 },
{ label: '同步指定商家', value: 1 }
],
type: 0,
tenantCode: '',
defaultProps: {
children: 'children',
label: 'label'
},
platFormMenu: [],
mechantMenu: [],
selected: [],
form: {
menuList: [],
tenantCode: null
},
allKeys: []
}
},
computed: {
left () {
return require('@/assets/menu-left.png')
},
right () {
return require('@/assets/menu-right.png')
}
},
created() {
// this.getPlatformMenu()
this.getAllMerchanMenu()
},
mounted() {
},
methods: {
changeCheck(v) {
if (v) {
this.$refs.merchantTree.setCheckedKeys(this.allKeys)
} else {
this.$refs.merchantTree.setCheckedKeys([])
}
},
async getPlatformMenu() {
const res = await Menu.getPlatformMenu()
const resData = res.data
if (resData.code === 0) {
this.platFormMenu = resData.data
}
},
async getAllMerchanMenu() {
const res = await Menu.getAllMerchanMenu()
const resData = res.data
if (resData.code === 0) {
const t = resData.data
this.changeTreeData(t)
this.form.menuList = t
this.getSelected(t)
this.$nextTick(() => {
this.setCheckedNodes()
})
}
},
changeTreeData(data) {
if (data && data.length) {
data.map(item => {
if (item.children && item.children.length && (item.children.findIndex(k => k.isSync === 0) > -1)) {
item.isSync = 0
}
})
}
},
//
async getSpecialMerchantMenu() {
const res = await Menu.getSpecialMerchantMenu(this.form.tenantCode)
const resData = res.data
if (resData.code === 0) {
this.form.menuList = resData.data
}
},
save() {
this.loading = true
Menu.saveMenu(this.form).then(res => {
this.loading = false
if (res.data.code === 0) {
this.$message.success('菜单同步成功')
}
})
},
getSelected(data) {
const t = []
const arr = []
if (data && data.length) {
data.forEach(item => {
if (item.isSync) {
t.push(item.id)
}
arr.push(item.id)
if (item.children && item.children.length) {
item.children.forEach(ob => {
if (ob.isSync) {
t.push(ob.id)
}
arr.push(ob.id)
})
}
})
}
this.selected = t
this.allKeys = arr
if (t.length === arr.length) {
this.checked = true
}
},
setCheckedNodes() {
if (this.selected.length) {
this.$refs.merchantTree.setCheckedKeys(this.selected)
}
},
checkChange(a, b, c, d) {
a.isSync = b ? 1 : 0
}
}
}
</script>
<style lang='less' scoped>
.empower_page {
min-height: 500px;
margin-top:20px;
padding: 20px;
background-color: #ffffff;
.top_btn {
height: 50px;
font-size: 20px;
line-height: 50px;
margin-bottom: 20px;
border-bottom: 1px #E0E5EB solid;
.save_btn {
float: right;
}
}
.empower_content {
// overflow: hidden;
.item {
min-height: 700px;
// float: left;
box-sizing: border-box;
&:nth-child(1), &:nth-child(3) {
// width:calc(50% - 75px);
width: 50%;
margin: 20px auto;
background:rgba(255,255,255,1);
box-shadow:0px 0px 10px 0px rgba(51,51,51,0.15);
// border: 1px #E0E5EB solid;
border-radius:4px;
.top_box {
height: 60px;
line-height: 60px;
font-size: 18px;
border-radius: 4px 4px 0 0;
background-color: #F7F7F7;
padding: 0 10px;
}
.menu_tree {
padding: 0 20px;
}
}
&:nth-child(2) {
width: 150px;
height: 100%;
position: relative;
.btn_list {
height: 150px;
width: 40px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -75px;
margin-left: -20px;
p {
height: 60px;
text-align: center;
background-color: #EEEEEE;
border-radius:4px;
padding-top: 18px;
img {
width:24px;
height:24px;
}
&:hover {
cursor: pointer;
}
}
}
}
}
}
}
.select_type {
float: left;
}
/deep/.el-form {
overflow: hidden;
}
/deep/.el-form-item {
float: right;
}
/deep/.select_type {
float: left;
}
/deep/.el-form-item__content {
line-height: 60px !important;
}
/deep/ label {
font-weight: 400;
}
/deep/.el-col-6 {
margin-left: 10px;
}
</style>
Loading…
Cancel
Save