加入消息通知功能

master
toesbieya 6 years ago
parent 38859853be
commit db2a28329d
  1. 2
      java/src/main/java/com/toesbieya/my/aspect/LockAspect.java
  2. 2
      java/src/main/java/com/toesbieya/my/aspect/TimeCostAspect.java
  3. 5
      java/src/main/java/com/toesbieya/my/config/HttpSessionConfig.java
  4. 11
      java/src/main/java/com/toesbieya/my/constant/MsgConstant.java
  5. 1
      java/src/main/java/com/toesbieya/my/constant/SocketConstant.java
  6. 23
      java/src/main/java/com/toesbieya/my/controller/BizPurchaseInboundController.java
  7. 16
      java/src/main/java/com/toesbieya/my/controller/BizPurchaseOrderController.java
  8. 23
      java/src/main/java/com/toesbieya/my/controller/BizSellOrderController.java
  9. 19
      java/src/main/java/com/toesbieya/my/controller/BizSellOutboundController.java
  10. 2
      java/src/main/java/com/toesbieya/my/controller/LoginController.java
  11. 123
      java/src/main/java/com/toesbieya/my/controller/MsgController.java
  12. 5
      java/src/main/java/com/toesbieya/my/controller/SysRoleController.java
  13. 28
      java/src/main/java/com/toesbieya/my/controller/SysUserController.java
  14. 2
      java/src/main/java/com/toesbieya/my/enumeration/BizDocumentFinishEnum.java
  15. 2
      java/src/main/java/com/toesbieya/my/enumeration/BizDocumentHistoryEnum.java
  16. 2
      java/src/main/java/com/toesbieya/my/enumeration/BizDocumentStatusEnum.java
  17. 2
      java/src/main/java/com/toesbieya/my/enumeration/GeneralStatusEnum.java
  18. 2
      java/src/main/java/com/toesbieya/my/enumeration/RecLoginHistoryEnum.java
  19. 2
      java/src/main/java/com/toesbieya/my/enumeration/UserActionEnum.java
  20. 7
      java/src/main/java/com/toesbieya/my/handler/GlobalExceptionHandler.java
  21. 6
      java/src/main/java/com/toesbieya/my/interceptor/SecurityInterceptor.java
  22. 31
      java/src/main/java/com/toesbieya/my/mapper/MsgMapper.java
  23. 23
      java/src/main/java/com/toesbieya/my/model/entity/Msg.java
  24. 11
      java/src/main/java/com/toesbieya/my/model/entity/MsgState.java
  25. 23
      java/src/main/java/com/toesbieya/my/model/vo/result/PageResult.java
  26. 14
      java/src/main/java/com/toesbieya/my/model/vo/search/MsgPersonalSearch.java
  27. 15
      java/src/main/java/com/toesbieya/my/model/vo/search/MsgSearch.java
  28. 25
      java/src/main/java/com/toesbieya/my/module/SocketModule.java
  29. 8
      java/src/main/java/com/toesbieya/my/module/redis/RedisLockHelper.java
  30. 6
      java/src/main/java/com/toesbieya/my/service/BizPurchaseInboundService.java
  31. 6
      java/src/main/java/com/toesbieya/my/service/BizPurchaseOrderService.java
  32. 6
      java/src/main/java/com/toesbieya/my/service/BizSellOrderService.java
  33. 6
      java/src/main/java/com/toesbieya/my/service/BizSellOutboundService.java
  34. 8
      java/src/main/java/com/toesbieya/my/service/BizStockService.java
  35. 116
      java/src/main/java/com/toesbieya/my/service/MsgService.java
  36. 10
      java/src/main/java/com/toesbieya/my/service/RecService.java
  37. 6
      java/src/main/java/com/toesbieya/my/service/SysCustomerService.java
  38. 6
      java/src/main/java/com/toesbieya/my/service/SysRoleService.java
  39. 6
      java/src/main/java/com/toesbieya/my/service/SysSupplierService.java
  40. 6
      java/src/main/java/com/toesbieya/my/service/SysUserService.java
  41. 8
      java/src/main/java/com/toesbieya/my/utils/ExcelUtil.java
  42. 3
      java/src/main/java/com/toesbieya/my/utils/IpUtil.java
  43. 14
      java/src/main/java/com/toesbieya/my/utils/QiniuUtil.java
  44. 1
      java/src/main/resources/application.yml
  45. 119
      java/src/main/resources/mapping/Msg.xml
  46. 4277
      vue/package-lock.json
  47. 27
      vue/package.json
  48. 19
      vue/public/index.html
  49. 419
      vue/public/static/tinymce/langs/zh_CN.js
  50. 27
      vue/src/api/message/manage/index.js
  51. 15
      vue/src/api/message/user/index.js
  52. 1
      vue/src/assets/icons/svg/message.svg
  53. 2
      vue/src/assets/styles/element-ui.scss
  54. 3
      vue/src/assets/styles/nprogress.scss
  55. 1
      vue/src/assets/styles/tagsView.scss
  56. 36
      vue/src/bizComponents/UserSelector/SimpleMultipleUserSelector.vue
  57. 5
      vue/src/bizComponents/UserSelector/index.js
  58. 5
      vue/src/components/DialogForm/DialogFormItem.vue
  59. 6
      vue/src/components/DialogForm/index.vue
  60. 49
      vue/src/components/Empty/DefaultEmptyImage.vue
  61. 82
      vue/src/components/Empty/index.vue
  62. 0
      vue/src/components/SearchForm/SearchFormItem.vue
  63. 3
      vue/src/components/SearchForm/index.vue
  64. 123
      vue/src/components/TinymceEditor/index.vue
  65. 0
      vue/src/components/UploadFile/index.vue
  66. 32
      vue/src/layout/components/Navbar/components/Bell.vue
  67. 9
      vue/src/layout/components/Navbar/index.vue
  68. 22
      vue/src/mixins/bizDocumentDialogMixin.js
  69. 2
      vue/src/mixins/tablePageMixin.js
  70. 2
      vue/src/plugin/canvasAnimation/particleNetwork/index.js
  71. 2
      vue/src/router/authorityRoutes/index.js
  72. 26
      vue/src/router/index.js
  73. 2
      vue/src/router/modules/example.js
  74. 25
      vue/src/router/modules/message.js
  75. 2
      vue/src/router/modules/purchase.js
  76. 2
      vue/src/router/modules/sell.js
  77. 2
      vue/src/router/modules/system.js
  78. 8
      vue/src/store/modules/iframe.js
  79. 22
      vue/src/store/modules/message.js
  80. 9
      vue/src/store/modules/resource.js
  81. 53
      vue/src/store/modules/socket.js
  82. 2
      vue/src/store/modules/tagsView.js
  83. 4
      vue/src/utils/auth.js
  84. 19
      vue/src/utils/index.js
  85. 2
      vue/src/views/example/components/upload.vue
  86. 2
      vue/src/views/example/stylePage.vue
  87. 241
      vue/src/views/message/manage/EditDialog.vue
  88. 165
      vue/src/views/message/manage/index.vue
  89. 183
      vue/src/views/message/user/MessageStream.vue
  90. 65
      vue/src/views/message/user/index.vue
  91. 5
      vue/src/views/purchase/inbound/index.vue
  92. 5
      vue/src/views/purchase/order/index.vue
  93. 5
      vue/src/views/sell/order/index.vue
  94. 5
      vue/src/views/sell/outbound/index.vue
  95. 2
      vue/src/views/stock/current/DetailDialog.vue
  96. 4
      vue/src/views/stock/current/index.vue
  97. 5
      vue/src/views/system/customer/EditDialog.vue
  98. 4
      vue/src/views/system/customer/index.vue
  99. 2
      vue/src/views/system/department/components/EditDialog.vue
  100. 2
      vue/src/views/system/resource/EditDialog.vue
  101. Some files were not shown because too many files have changed in this diff Show More

@ -22,7 +22,7 @@ import java.util.ArrayList;
@Slf4j
@Order(Integer.MAX_VALUE - 1)
public class LockAspect {
private ThreadLocal<ArrayList<LockHelper>> lockHelperThreadLocal = new ThreadLocal<>();
private final ThreadLocal<ArrayList<LockHelper>> lockHelperThreadLocal = new ThreadLocal<>();
@Pointcut("@annotation(com.toesbieya.my.annoation.Lock)&&execution(com.toesbieya.my.utils.Result com.toesbieya.my..*.*(..))")
public void pointCut() {

@ -19,7 +19,7 @@ import java.time.temporal.ChronoUnit;
@Slf4j
@Order(1)
public class TimeCostAspect {
private ThreadLocal<Instant> instantThreadLocal = new ThreadLocal<>();
private final ThreadLocal<Instant> instantThreadLocal = new ThreadLocal<>();
@Pointcut("@annotation(com.toesbieya.my.annoation.TimeCost)")
public void pointCut() {

@ -22,7 +22,8 @@ public class HttpSessionConfig {
public HttpSessionListener httpSessionEventPublisher() {
return new HttpSessionListener() {
@Override
public void sessionCreated(HttpSessionEvent se) { }
public void sessionCreated(HttpSessionEvent se) {
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
@ -43,7 +44,7 @@ public class HttpSessionConfig {
private final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Class<T> clazz;
private final Class<T> clazz;
public JsonRedisSerializer(Class<T> clazz) {
this.clazz = clazz;

@ -0,0 +1,11 @@
package com.toesbieya.my.constant;
public class MsgConstant {
public static final Integer TYPE_NOTICE = 0; //通知提醒
public static final Integer TYPE_ANNOUNCEMENT = 1; //系统公告
public static final Integer STATUS_DRAFT = 0; //拟定
public static final Integer STATUS_PUBLISHED = 1; //已发布
public static final Integer STATUS_WITHDREW = 2; //已撤回
public static final Integer TO_ALL = 1; //发送给全体用户
public static final Integer TO_RANGE = 0; //已发送给指定用户
}

@ -8,4 +8,5 @@ public class SocketConstant {
public static final int MAX_FRAME_PAYLOAD = (int) YmlUtil.get("socket.max-frame-payload");
public static final int MAX_HTTP_CONTENT = (int) YmlUtil.get("socket.max-http-content");
public static final String EVENT_LOGOUT = (String) YmlUtil.get("socket.event.logout");
public static final String EVENT_NEW_MESSAGE = (String) YmlUtil.get("socket.event.new-message");
}

@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@RestController
@ -23,8 +22,6 @@ import java.util.List;
public class BizPurchaseInboundController {
@Resource
private BizPurchaseInboundService purchaseInboundService;
@Resource
private HttpSession session;
@GetMapping("getById")
public Result getById(@RequestParam String id) {
@ -54,10 +51,9 @@ public class BizPurchaseInboundController {
String errMsg = validateSub(inbound.getData());
if (errMsg != null) return Result.fail(errMsg);
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
inbound.setCid(sysUser.getId());
inbound.setCname(sysUser.getName());
SysUser user = Util.getUser();
inbound.setCid(user.getId());
inbound.setCname(user.getName());
inbound.setCtime(System.currentTimeMillis());
inbound.setStatus(BizDocumentStatusEnum.DRAFT.getCode());
@ -83,10 +79,9 @@ public class BizPurchaseInboundController {
inbound.setStatus(BizDocumentStatusEnum.WAIT_VERIFY.getCode());
if (isFirst) {
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
inbound.setCid(sysUser.getId());
inbound.setCname(sysUser.getName());
SysUser user = Util.getUser();
inbound.setCid(user.getId());
inbound.setCname(user.getName());
inbound.setCtime(System.currentTimeMillis());
}
return purchaseInboundService.commit(inbound);
@ -94,18 +89,18 @@ public class BizPurchaseInboundController {
@PostMapping("withdraw")
public Result withdraw(@RequestBody DocumentStatusUpdate vo) {
return purchaseInboundService.withdraw(vo, Util.getUser(session));
return purchaseInboundService.withdraw(vo, Util.getUser());
}
@PostMapping("pass")
public Result pass(@RequestBody DocumentStatusUpdate vo) {
if (StringUtils.isEmpty(vo.getPid())) return Result.fail("参数错误");
return purchaseInboundService.pass(vo, Util.getUser(session));
return purchaseInboundService.pass(vo, Util.getUser());
}
@PostMapping("reject")
public Result reject(@RequestBody DocumentStatusUpdate vo) {
return purchaseInboundService.reject(vo, Util.getUser(session));
return purchaseInboundService.reject(vo, Util.getUser());
}
@GetMapping("del")

@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@RestController
@ -23,8 +22,6 @@ import java.util.List;
public class BizPurchaseOrderController {
@Resource
private BizPurchaseOrderService purchaseOrderService;
@Resource
private HttpSession session;
@GetMapping("getById")
public Result getById(@RequestParam String id) {
@ -59,9 +56,7 @@ public class BizPurchaseOrderController {
String errMsg = validateSub(order.getData());
if (errMsg != null) return Result.fail(errMsg);
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
SysUser sysUser = Util.getUser();
order.setCid(sysUser.getId());
order.setCname(sysUser.getName());
order.setCtime(System.currentTimeMillis());
@ -90,8 +85,7 @@ public class BizPurchaseOrderController {
order.setStatus(BizDocumentStatusEnum.WAIT_VERIFY.getCode());
if (isFirst) {
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
SysUser sysUser = Util.getUser();
order.setCid(sysUser.getId());
order.setCname(sysUser.getName());
order.setCtime(System.currentTimeMillis());
@ -102,17 +96,17 @@ public class BizPurchaseOrderController {
@PostMapping("withdraw")
public Result withdraw(@RequestBody DocumentStatusUpdate vo) {
return purchaseOrderService.withdraw(vo, Util.getUser(session));
return purchaseOrderService.withdraw(vo, Util.getUser());
}
@PostMapping("pass")
public Result pass(@RequestBody DocumentStatusUpdate vo) {
return purchaseOrderService.pass(vo, Util.getUser(session));
return purchaseOrderService.pass(vo, Util.getUser());
}
@PostMapping("reject")
public Result reject(@RequestBody DocumentStatusUpdate vo) {
return purchaseOrderService.reject(vo, Util.getUser(session));
return purchaseOrderService.reject(vo, Util.getUser());
}
@GetMapping("del")

@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@RestController
@ -23,8 +22,6 @@ import java.util.List;
public class BizSellOrderController {
@Resource
private BizSellOrderService sellOrderService;
@Resource
private HttpSession session;
@GetMapping("getById")
public Result getById(@RequestParam String id) {
@ -59,11 +56,10 @@ public class BizSellOrderController {
String errMsg = validateSub(order.getData());
if (errMsg != null) return Result.fail(errMsg);
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
SysUser user = Util.getUser();
order.setCid(sysUser.getId());
order.setCname(sysUser.getName());
order.setCid(user.getId());
order.setCname(user.getName());
order.setCtime(System.currentTimeMillis());
order.setStatus(BizDocumentStatusEnum.DRAFT.getCode());
@ -90,10 +86,9 @@ public class BizSellOrderController {
order.setStatus(BizDocumentStatusEnum.WAIT_VERIFY.getCode());
if (isFirst) {
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
order.setCid(sysUser.getId());
order.setCname(sysUser.getName());
SysUser user = Util.getUser();
order.setCid(user.getId());
order.setCname(user.getName());
order.setCtime(System.currentTimeMillis());
}
@ -102,17 +97,17 @@ public class BizSellOrderController {
@PostMapping("withdraw")
public Result withdraw(@RequestBody DocumentStatusUpdate vo) {
return sellOrderService.withdraw(vo, Util.getUser(session));
return sellOrderService.withdraw(vo, Util.getUser());
}
@PostMapping("pass")
public Result pass(@RequestBody DocumentStatusUpdate vo) {
return sellOrderService.pass(vo, Util.getUser(session));
return sellOrderService.pass(vo, Util.getUser());
}
@PostMapping("reject")
public Result reject(@RequestBody DocumentStatusUpdate vo) {
return sellOrderService.reject(vo, Util.getUser(session));
return sellOrderService.reject(vo, Util.getUser());
}
@GetMapping("del")

@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@RestController
@ -23,8 +22,6 @@ import java.util.List;
public class BizSellOutboundController {
@Resource
private BizSellOutboundService sellOutboundService;
@Resource
private HttpSession session;
@GetMapping("getById")
public Result getById(@RequestParam String id) {
@ -54,10 +51,9 @@ public class BizSellOutboundController {
String errMsg = validateSub(outbound.getData());
if (errMsg != null) return Result.fail(errMsg);
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
outbound.setCid(sysUser.getId());
outbound.setCname(sysUser.getName());
SysUser user = Util.getUser();
outbound.setCid(user.getId());
outbound.setCname(user.getName());
outbound.setCtime(System.currentTimeMillis());
outbound.setStatus(BizDocumentStatusEnum.DRAFT.getCode());
@ -83,8 +79,7 @@ public class BizSellOutboundController {
outbound.setStatus(BizDocumentStatusEnum.WAIT_VERIFY.getCode());
if (isFirst) {
SysUser sysUser = Util.getUser(session);
assert sysUser != null;
SysUser sysUser = Util.getUser();
outbound.setCid(sysUser.getId());
outbound.setCname(sysUser.getName());
outbound.setCtime(System.currentTimeMillis());
@ -94,18 +89,18 @@ public class BizSellOutboundController {
@PostMapping("withdraw")
public Result withdraw(@RequestBody DocumentStatusUpdate vo) {
return sellOutboundService.withdraw(vo, Util.getUser(session));
return sellOutboundService.withdraw(vo, Util.getUser());
}
@PostMapping("pass")
public Result pass(@RequestBody DocumentStatusUpdate vo) {
if (StringUtils.isEmpty(vo.getPid())) return Result.fail("参数错误");
return sellOutboundService.pass(vo, Util.getUser(session));
return sellOutboundService.pass(vo, Util.getUser());
}
@PostMapping("reject")
public Result reject(@RequestBody DocumentStatusUpdate vo) {
return sellOutboundService.reject(vo, Util.getUser(session));
return sellOutboundService.reject(vo, Util.getUser());
}
@GetMapping("del")

@ -51,7 +51,7 @@ public class LoginController {
@GetMapping("logout")
public Result logout(HttpServletRequest request) {
SysUser user = Util.getUser(session);
SysUser user = Util.getUser();
if (user != null) {
recService.insertLoginHistory(user, IpUtil.getIp(request), RecLoginHistoryEnum.LOGOUT);
}

@ -0,0 +1,123 @@
package com.toesbieya.my.controller;
import com.toesbieya.my.constant.MsgConstant;
import com.toesbieya.my.model.entity.Msg;
import com.toesbieya.my.model.entity.SysUser;
import com.toesbieya.my.model.vo.search.MsgPersonalSearch;
import com.toesbieya.my.model.vo.search.MsgSearch;
import com.toesbieya.my.service.MsgService;
import com.toesbieya.my.utils.Result;
import com.toesbieya.my.utils.Util;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("message")
public class MsgController {
@Resource
private MsgService msgService;
@PostMapping("manage/search")
public Result search(@RequestBody MsgSearch vo) {
return Result.success(msgService.search(vo));
}
@PostMapping("manage/add")
public Result add(@RequestBody Msg msg) {
String errMsg = validateAdd(msg);
if (errMsg != null) return Result.fail(errMsg);
SysUser user = Util.getUser();
setAddInfo(user, msg);
return msgService.add(msg);
}
@PostMapping("manage/update")
public Result update(@RequestBody Msg msg) {
String errMsg = validateAdd(msg);
if (errMsg == null) errMsg = validateUpdate(msg);
if (errMsg != null) return Result.fail(errMsg);
return msgService.update(msg);
}
@PostMapping("manage/publish")
public Result publish(@RequestBody Msg msg) {
boolean isFirstCreate = msg.getId() == null;
String errMsg = validateAdd(msg);
if (errMsg == null && !isFirstCreate) errMsg = validateUpdate(msg);
if (errMsg != null) return Result.fail(errMsg);
SysUser user = Util.getUser();
if (isFirstCreate) setAddInfo(user, msg);
msg.setPid(user.getId());
msg.setPname(user.getName());
msg.setPtime(System.currentTimeMillis());
msg.setStatus(MsgConstant.STATUS_PUBLISHED);
return msgService.publish(msg);
}
@PostMapping("manage/withdraw")
public Result withdraw(@RequestBody Msg msg) {
if (msg.getId() == null) return Result.fail("参数错误");
SysUser user = Util.getUser();
msg.setWid(user.getId());
msg.setWname(user.getName());
msg.setWtime(System.currentTimeMillis());
msg.setStatus(MsgConstant.STATUS_WITHDREW);
return msgService.withdraw(msg);
}
@GetMapping("manage/del")
public Result del(@RequestParam Integer id) {
return msgService.del(id);
}
@PostMapping("user/search")
public Result searchPersonal(@RequestBody MsgPersonalSearch vo) {
SysUser user = Util.getUser();
vo.setUid(user.getId());
vo.setCtime(user.getCtime());
return Result.success(msgService.searchPersonal(vo));
}
@GetMapping("user/read")
public Result read(@RequestParam Integer id) {
return msgService.read(Util.getUser(), id);
}
@GetMapping("user/readAll")
public Result readAll() {
return msgService.readAll(Util.getUser());
}
private void setAddInfo(SysUser user, Msg msg) {
msg.setCid(user.getId());
msg.setCname(user.getName());
msg.setCtime(System.currentTimeMillis());
msg.setStatus(MsgConstant.STATUS_DRAFT);
}
private String validateAdd(Msg msg) {
if (StringUtils.isEmpty(msg.getTitle())
|| msg.getType() == null
|| msg.getAll() == null
|| msg.getAll().equals(MsgConstant.TO_RANGE) && StringUtils.isEmpty(msg.getRecipient())
) return "参数错误";
return null;
}
private String validateUpdate(Msg msg) {
if (StringUtils.isEmpty(msg.getId())
|| !msg.getStatus().equals(MsgConstant.STATUS_DRAFT)
) return "参数错误";
return null;
}
}

@ -36,11 +36,12 @@ public class SysRoleController {
|| role.getStatus() == null) {
return Result.fail("添加失败,参数错误");
}
SysUser user = Util.getUser(session);
assert user != null;
SysUser user = Util.getUser();
role.setCid(user.getId());
role.setCname(user.getName());
role.setCtime(System.currentTimeMillis());
return roleService.add(role);
}

@ -17,7 +17,6 @@ import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
@ -30,8 +29,6 @@ public class SysUserController {
private SysUserService userService;
@Resource
private RecService recService;
@Resource
private HttpSession session;
@PostMapping("search")
public Result search(@RequestBody UserSearch vo) {
@ -40,19 +37,19 @@ public class SysUserController {
@PostMapping("getLoginHistory")
public Result getLoginHistory(@RequestBody LoginHistorySearch vo) {
SysUser user = Util.getUser(session);
assert user != null;
SysUser user = Util.getUser();
vo.setUid(user.getId());
vo.setStartTime(DateUtil.getTimestampBeforeNow(7));
return Result.success(recService.searchLoginHistory(vo));
}
@PostMapping("getUserAction")
public Result getUserAction(@RequestBody UserActionSearch vo) {
SysUser user = Util.getUser(session);
assert user != null;
SysUser user = Util.getUser();
vo.setUid(user.getId());
vo.setStartTime(DateUtil.getTimestampBeforeNow(7));
return Result.success(recService.searchUserAction(vo));
}
@ -97,15 +94,15 @@ public class SysUserController {
@PostMapping("updatePwd")
public Result updatePwd(@RequestBody UserUpdatePwd vo) {
SysUser user = Util.getUser(session);
assert user != null;
SysUser user = Util.getUser();
vo.setId(user.getId());
String errMsg = validateUpdatePwdParam(vo);
if (errMsg != null) {
return Result.fail(errMsg);
}
if (errMsg != null) return Result.fail(errMsg);
vo.setOld_pwd(DigestUtils.md5DigestAsHex(vo.getOld_pwd().getBytes()));
vo.setNew_pwd(DigestUtils.md5DigestAsHex(vo.getNew_pwd().getBytes()));
return userService.updatePwd(vo);
}
@ -120,14 +117,13 @@ public class SysUserController {
@GetMapping("updateAvatar")
public Result updateAvatar(@RequestParam String key) throws UnsupportedEncodingException {
if (StringUtils.isEmpty(key)) return Result.fail("参数错误");
SysUser user = Util.getUser(session);
assert user != null;
return userService.updateAvatar(user, URLDecoder.decode(key, "utf-8"), session);
return userService.updateAvatar(Util.getUser(), URLDecoder.decode(key, "utf-8"), Util.getSession());
}
@GetMapping("validate")
public Result validate(@RequestParam String pwd) {
SysUser current = Util.getUser(session);
SysUser current = Util.getUser();
if (!pwd.equals(current.getPwd())) return Result.fail("校验失败");
return Result.success("校验通过");
}

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum BizDocumentFinishEnum {
TO_BE_STARTED(0), UNDERWAY(1), FINISHED(2);
private int code;
private final int code;
BizDocumentFinishEnum(int code) {
this.code = code;

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum BizDocumentHistoryEnum {
WITHDRAW(0), COMMIT(1), PASS(2), REJECT(3);
private int code;
private final int code;
BizDocumentHistoryEnum(int code) {
this.code = code;

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum BizDocumentStatusEnum {
DRAFT(0), WAIT_VERIFY(1), VERIFIED(2);
private int code;
private final int code;
BizDocumentStatusEnum(int code) {
this.code = code;

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum GeneralStatusEnum {
DISABLED(0), ENABLED(1);
private int code;
private final int code;
GeneralStatusEnum(int code) {
this.code = code;

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum RecLoginHistoryEnum {
LOGOUT(0), LOGIN(1);
private int code;
private final int code;
RecLoginHistoryEnum(int code) {
this.code = code;

@ -3,7 +3,7 @@ package com.toesbieya.my.enumeration;
public enum UserActionEnum {
FAIL(0), SUCCESS(1);
private int code;
private final int code;
UserActionEnum(int code) {
this.code = code;

@ -9,6 +9,7 @@ import com.toesbieya.my.utils.ThreadUtil;
import com.toesbieya.my.utils.Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
@ -28,6 +29,12 @@ public class GlobalExceptionHandler {
return Result.fail(e.getMessage());
}
//请求方法有误
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
return Result.fail("请求方法有误");
}
//上传文件过大
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {

@ -21,7 +21,7 @@ public class SecurityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
HttpSession httpSession = request.getSession();
String url = request.getServletPath();
String method = request.getMethod();
String ip = IpUtil.getIp(request);
@ -33,10 +33,10 @@ public class SecurityInterceptor implements HandlerInterceptor {
return false;
}
SysUser sysUser = Util.getUser(session);
SysUser sysUser = Util.getUser(httpSession);
if (sysUser == null
|| request.getHeader(SessionConstant.TOKEN_KEY) == null
|| !request.getHeader(SessionConstant.TOKEN_KEY).equals(session.getAttribute(SessionConstant.TOKEN_KEY))) {
|| !request.getHeader(SessionConstant.TOKEN_KEY).equals(httpSession.getAttribute(SessionConstant.TOKEN_KEY))) {
Util.responseJson(response, Result.requireLogin());
ThreadUtil.clearUser();
return false;

@ -0,0 +1,31 @@
package com.toesbieya.my.mapper;
import com.toesbieya.my.model.entity.Msg;
import com.toesbieya.my.model.entity.MsgState;
import com.toesbieya.my.model.vo.search.MsgPersonalSearch;
import com.toesbieya.my.model.vo.search.MsgSearch;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface MsgMapper {
List<Msg> search(MsgSearch search);
int insert(Msg msg);
int update(Msg msg);
int withdraw(Msg msg);
int del(@Param("id") int id);
int insertState(MsgState state);
boolean checkRead(@Param("mid") int mid, @Param("uid") int uid);
List<Msg> getReadByUser(MsgPersonalSearch search);
List<Msg> getUnreadByUser(MsgPersonalSearch search);
Integer getUnreadCountByUser(MsgPersonalSearch search);
}

@ -0,0 +1,23 @@
package com.toesbieya.my.model.entity;
import lombok.Data;
@Data
public class Msg {
private Integer id;
private String title;
private String content;
private Integer type;
private Integer cid;
private String cname;
private Long ctime;
private Integer pid;
private String pname;
private Long ptime;
private Integer wid;
private String wname;
private Long wtime;
private Integer status;
private Integer all;
private String recipient;
}

@ -0,0 +1,11 @@
package com.toesbieya.my.model.entity;
import lombok.Data;
@Data
public class MsgState {
private Integer id;
private Integer mid;
private Integer uid;
private Long time;
}

@ -0,0 +1,23 @@
package com.toesbieya.my.model.vo.result;
import com.github.pagehelper.Page;
import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {
private long total;
private List<T> list;
private Object data;
public PageResult(List<T> list) {
this.list = list;
if (list instanceof Page) {
this.total = ((Page) list).getTotal();
}
else {
this.total = list.size();
}
}
}

@ -0,0 +1,14 @@
package com.toesbieya.my.model.vo.search;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class MsgPersonalSearch extends BaseSearch {
private Integer uid;
private Long ctime;
private boolean unread = true;
}

@ -0,0 +1,15 @@
package com.toesbieya.my.model.vo.search;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class MsgSearch extends BaseSearch {
private String title;
private String type;
private Integer uid;
private String status;
}

@ -18,6 +18,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
@Component
@Slf4j
@ -31,15 +32,21 @@ public class SocketModule {
server.start();
}
public static void logout(Integer uid, String msg) {
public static void sendEvent(String event, Integer uid, Object... data) {
sendEvent(event, uid, data, null);
}
public static void sendEvent(String event, Integer uid, Object data, BiConsumer<UserObject, SocketIOClient> consumer) {
UserObject obj = socketMap.get(uid);
if (obj == null) return;
SocketIOClient client = server.getClient(obj.getUuid());
if (client == null) return;
client.sendEvent(SocketConstant.EVENT_LOGOUT, msg);
RedisUtil.expire(SessionConstant.REDIS_NAMESPACE + obj.getSessionID());
client.disconnect();
socketMap.remove(uid);
client.sendEvent(event, data);
if (consumer != null) consumer.accept(obj, client);
}
public static void broadcast(String event, Object... data) {
server.getBroadcastOperations().sendEvent(event, data);
}
public static boolean online(Integer uid) {
@ -72,6 +79,14 @@ public class SocketModule {
client.sendEvent("response", "后台得到了数据");
}
public static void logout(Integer uid, String msg) {
sendEvent(SocketConstant.EVENT_LOGOUT, uid, msg, (obj, client) -> {
RedisUtil.expire(SessionConstant.REDIS_NAMESPACE + obj.getSessionID());
client.disconnect();
socketMap.remove(uid);
});
}
public static int getOnlineNum() {
return socketMap.size();
}

@ -11,10 +11,10 @@ import java.util.Objects;
@Slf4j
public class RedisLockHelper implements LockHelper {
private StringRedisTemplate template = RedisUtil.getStringRedisTemplate();
private String key;
private String value;
private String expire;
private final StringRedisTemplate template = RedisUtil.getStringRedisTemplate();
private final String key;
private final String value;
private final String expire;
public RedisLockHelper(String key) {
this.key = "lock:" + key;

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.Lock;
import com.toesbieya.my.annoation.Tx;
import com.toesbieya.my.annoation.UserAction;
@ -15,6 +14,7 @@ import com.toesbieya.my.mapper.BizPurchaseOrderMapper;
import com.toesbieya.my.mapper.BizStockMapper;
import com.toesbieya.my.model.entity.*;
import com.toesbieya.my.model.vo.export.PurchaseInboundExport;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.PurchaseInboundSearch;
import com.toesbieya.my.model.vo.update.DocumentStatusUpdate;
import com.toesbieya.my.utils.ExcelUtil;
@ -58,10 +58,10 @@ public class BizPurchaseInboundService {
return purchaseInboundMapper.getSubById(id);
}
public PageSerializable<BizPurchaseInbound> search(PurchaseInboundSearch vo) {
public PageResult<BizPurchaseInbound> search(PurchaseInboundSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<BizPurchaseInbound> list = purchaseInboundMapper.search(vo);
return new PageSerializable<>(list);
return new PageResult<>(list);
}
public void export(PurchaseInboundSearch vo, HttpServletResponse response) throws Exception {

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.Lock;
import com.toesbieya.my.annoation.Tx;
import com.toesbieya.my.annoation.UserAction;
@ -11,6 +10,7 @@ import com.toesbieya.my.mapper.BizDocumentHistoryMapper;
import com.toesbieya.my.mapper.BizPurchaseOrderMapper;
import com.toesbieya.my.model.entity.*;
import com.toesbieya.my.model.vo.export.PurchaseOrderExport;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.PurchaseOrderSearch;
import com.toesbieya.my.model.vo.update.DocumentStatusUpdate;
import com.toesbieya.my.utils.ExcelUtil;
@ -50,9 +50,9 @@ public class BizPurchaseOrderService {
return purchaseOrderMapper.getSubById(id);
}
public PageSerializable<BizPurchaseOrder> search(PurchaseOrderSearch vo) {
public PageResult<BizPurchaseOrder> search(PurchaseOrderSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
return new PageSerializable<>(purchaseOrderMapper.search(vo));
return new PageResult<>(purchaseOrderMapper.search(vo));
}
public void export(PurchaseOrderSearch vo, HttpServletResponse response) throws Exception {

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.Lock;
import com.toesbieya.my.annoation.Tx;
import com.toesbieya.my.annoation.UserAction;
@ -12,6 +11,7 @@ import com.toesbieya.my.mapper.BizSellOrderMapper;
import com.toesbieya.my.mapper.BizStockMapper;
import com.toesbieya.my.model.entity.*;
import com.toesbieya.my.model.vo.export.SellOrderExport;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.result.StockSearchResult;
import com.toesbieya.my.model.vo.search.SellOrderSearch;
import com.toesbieya.my.model.vo.search.StockSearch;
@ -57,9 +57,9 @@ public class BizSellOrderService {
return sellOrderMapper.getSubById(id);
}
public PageSerializable<BizSellOrder> search(SellOrderSearch vo) {
public PageResult<BizSellOrder> search(SellOrderSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
return new PageSerializable<>(sellOrderMapper.search(vo));
return new PageResult<>(sellOrderMapper.search(vo));
}
public void export(SellOrderSearch vo, HttpServletResponse response) throws Exception {

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.Lock;
import com.toesbieya.my.annoation.Tx;
import com.toesbieya.my.annoation.UserAction;
@ -15,6 +14,7 @@ import com.toesbieya.my.mapper.BizSellOutboundMapper;
import com.toesbieya.my.mapper.BizStockMapper;
import com.toesbieya.my.model.entity.*;
import com.toesbieya.my.model.vo.export.SellOutboundExport;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.SellOutboundSearch;
import com.toesbieya.my.model.vo.update.DocumentStatusUpdate;
import com.toesbieya.my.utils.ExcelUtil;
@ -62,10 +62,10 @@ public class BizSellOutboundService {
return sellOutboundMapper.getSubById(pid);
}
public PageSerializable<BizSellOutbound> search(SellOutboundSearch vo) {
public PageResult<BizSellOutbound> search(SellOutboundSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<BizSellOutbound> list = sellOutboundMapper.search(vo);
return new PageSerializable<>(list);
return new PageResult<>(list);
}
public void export(SellOutboundSearch vo, HttpServletResponse response) throws Exception {

@ -1,10 +1,10 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.mapper.BizStockMapper;
import com.toesbieya.my.model.entity.BizStock;
import com.toesbieya.my.model.vo.export.StockExport;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.result.StockSearchResult;
import com.toesbieya.my.model.vo.search.StockSearch;
import com.toesbieya.my.utils.ExcelUtil;
@ -21,13 +21,13 @@ public class BizStockService {
@Resource
private BizStockMapper stockMapper;
private ExcelUtil.CommonMergeOptions mergeOptions =
private final ExcelUtil.CommonMergeOptions mergeOptions =
new ExcelUtil.CommonMergeOptions(
new String[]{"id", "cname", "total_num", "total_price", "cgddid", "cg_price", "cg_num"}, "cid", "id");
public PageSerializable<StockSearchResult> search(StockSearch vo) {
public PageResult<StockSearchResult> search(StockSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
return new PageSerializable<>(stockMapper.search(vo));
return new PageResult<>(stockMapper.search(vo));
}
public List<BizStock> getDetail(String cid) {

@ -0,0 +1,116 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.toesbieya.my.constant.MsgConstant;
import com.toesbieya.my.constant.SocketConstant;
import com.toesbieya.my.mapper.MsgMapper;
import com.toesbieya.my.model.entity.Msg;
import com.toesbieya.my.model.entity.MsgState;
import com.toesbieya.my.model.entity.SysUser;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.MsgPersonalSearch;
import com.toesbieya.my.model.vo.search.MsgSearch;
import com.toesbieya.my.module.SocketModule;
import com.toesbieya.my.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
@Service
@Slf4j
public class MsgService {
@Resource
private MsgMapper msgMapper;
public PageResult<Msg> search(MsgSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<Msg> list = msgMapper.search(vo);
return new PageResult<>(list);
}
public Result add(Msg msg) {
msg.setId(msgMapper.insert(msg));
return Result.success("添加成功", msg);
}
public Result update(Msg msg) {
int rows = msgMapper.update(msg);
return rows > 0 ? Result.success("修改成功") : Result.fail("修改失败,请刷新重试");
}
public Result publish(Msg msg) {
boolean isFirstCreate = msg.getId() == null;
Result result = isFirstCreate ? add(msg) : update(msg);
if (result.isSuccess()) {
result.setMsg("提交成功");
result.setData(msg);
if (msg.getAll().equals(MsgConstant.TO_ALL)) {
SocketModule.broadcast(SocketConstant.EVENT_NEW_MESSAGE);
}
else {
String recipient = msg.getRecipient();
if (!StringUtils.isEmpty(recipient)) {
for (String id : recipient.split(",")) {
SocketModule.sendEvent(SocketConstant.EVENT_NEW_MESSAGE, Integer.valueOf(id));
}
}
}
}
else result.setMsg("发布失败," + result.getMsg());
return result;
}
public Result withdraw(Msg msg) {
int rows = msgMapper.withdraw(msg);
return rows > 0 ? Result.success("撤回成功", msg) : Result.fail("撤回失败,请刷新重试");
}
public Result del(int id) {
int rows = msgMapper.del(id);
return rows > 0 ? Result.success("删除成功") : Result.fail("删除失败,请刷新重试");
}
public PageResult<Msg> searchPersonal(MsgPersonalSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<Msg> list = vo.isUnread() ? msgMapper.getUnreadByUser(vo) : msgMapper.getReadByUser(vo);
PageResult<Msg> result = new PageResult<>(list);
if (vo.isUnread()) {
result.setData(msgMapper.getUnreadCountByUser(vo));
}
return result;
}
public Result read(SysUser user, int id) {
if (msgMapper.checkRead(id, user.getId())) {
return Result.success();
}
MsgState state = new MsgState();
state.setMid(id);
state.setUid(user.getId());
state.setTime(System.currentTimeMillis());
return Result.success(msgMapper.insertState(state));
}
public Result readAll(SysUser user) {
MsgPersonalSearch vo = new MsgPersonalSearch();
vo.setUid(user.getId());
vo.setCtime(user.getCtime());
MsgState state = new MsgState();
state.setUid(user.getId());
state.setTime(System.currentTimeMillis());
List<Msg> list = msgMapper.getUnreadByUser(vo);
for (Msg msg : list) {
state.setMid(msg.getId());
msgMapper.insertState(state);
}
return Result.success();
}
}

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.enumeration.RecLoginHistoryEnum;
import com.toesbieya.my.enumeration.UserActionEnum;
import com.toesbieya.my.mapper.RecAttachmentMapper;
@ -11,6 +10,7 @@ import com.toesbieya.my.model.entity.RecAttachment;
import com.toesbieya.my.model.entity.RecLoginHistory;
import com.toesbieya.my.model.entity.RecUserAction;
import com.toesbieya.my.model.entity.SysUser;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.LoginHistorySearch;
import com.toesbieya.my.model.vo.search.UserActionSearch;
import com.toesbieya.my.utils.IpUtil;
@ -32,16 +32,16 @@ public class RecService {
@Resource
private RecAttachmentMapper attachmentMapper;
public PageSerializable<RecLoginHistory> searchLoginHistory(LoginHistorySearch vo) {
public PageResult<RecLoginHistory> searchLoginHistory(LoginHistorySearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<RecLoginHistory> list = loginHistoryMapper.search(vo);
return new PageSerializable<>(list);
return new PageResult<>(list);
}
public PageSerializable<RecUserAction> searchUserAction(UserActionSearch vo) {
public PageResult<RecUserAction> searchUserAction(UserActionSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<RecUserAction> list = userActionMapper.search(vo);
return new PageSerializable<>(list);
return new PageResult<>(list);
}
public List<RecAttachment> getAttachmentByPid(String pid) {

@ -1,10 +1,10 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.UserAction;
import com.toesbieya.my.mapper.SysCustomerMapper;
import com.toesbieya.my.model.entity.SysCustomer;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.result.RegionValueResult;
import com.toesbieya.my.model.vo.search.CustomerSearch;
import com.toesbieya.my.utils.Result;
@ -26,10 +26,10 @@ public class SysCustomerService {
return customerMapper.get();
}
public PageSerializable<SysCustomer> search(CustomerSearch vo) {
public PageResult<SysCustomer> search(CustomerSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<SysCustomer> SysCustomers = customerMapper.search(vo);
return new PageSerializable<>(SysCustomers);
return new PageResult<>(SysCustomers);
}
@UserAction("'添加客户:'+ #customer.name")

@ -1,10 +1,10 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.UserAction;
import com.toesbieya.my.mapper.SysRoleMapper;
import com.toesbieya.my.model.entity.SysRole;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.RoleSearch;
import com.toesbieya.my.utils.Result;
import org.springframework.stereotype.Service;
@ -25,9 +25,9 @@ public class SysRoleService {
return roleMapper.getAll();
}
public PageSerializable<SysRole> search(RoleSearch vo) {
public PageResult<SysRole> search(RoleSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
return new PageSerializable<>(roleMapper.search(vo));
return new PageResult<>(roleMapper.search(vo));
}
@UserAction("'添加角色:'+#role.name")

@ -1,10 +1,10 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.UserAction;
import com.toesbieya.my.mapper.SysSupplierMapper;
import com.toesbieya.my.model.entity.SysSupplier;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.result.RegionValueResult;
import com.toesbieya.my.model.vo.search.SupplierSearch;
import com.toesbieya.my.utils.Result;
@ -26,10 +26,10 @@ public class SysSupplierService {
return supplierMapper.get();
}
public PageSerializable<SysSupplier> search(SupplierSearch vo) {
public PageResult<SysSupplier> search(SupplierSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<SysSupplier> sysSuppliers = supplierMapper.search(vo);
return new PageSerializable<>(sysSuppliers);
return new PageResult<>(sysSuppliers);
}
@UserAction("'添加供应商:'+ #supplier.name")

@ -1,7 +1,6 @@
package com.toesbieya.my.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.toesbieya.my.annoation.Tx;
import com.toesbieya.my.annoation.UserAction;
import com.toesbieya.my.constant.SessionConstant;
@ -11,6 +10,7 @@ import com.toesbieya.my.mapper.SysUserMapper;
import com.toesbieya.my.model.entity.RecUserAction;
import com.toesbieya.my.model.entity.SysResource;
import com.toesbieya.my.model.entity.SysUser;
import com.toesbieya.my.model.vo.result.PageResult;
import com.toesbieya.my.model.vo.search.UserSearch;
import com.toesbieya.my.model.vo.update.UserUpdatePwd;
import com.toesbieya.my.module.SocketModule;
@ -109,13 +109,13 @@ public class SysUserService {
return Result.success("注册成功");
}
public PageSerializable<SysUser> search(UserSearch vo) {
public PageResult<SysUser> search(UserSearch vo) {
PageHelper.startPage(vo.getPage(), vo.getPageSize());
List<SysUser> users = userMapper.search(vo);
for (SysUser user : users) {
user.setOnline(SocketModule.online(user.getId()));
}
return new PageSerializable<>(users);
return new PageResult<>(users);
}
@UserAction("'添加用户:'+#user.name")

@ -106,10 +106,10 @@ public class ExcelUtil {
private static class CommonMerge extends AbstractMergeStrategy {
private boolean finish = false;
private int ignoreRows = 1;
private Field[] attrs;
private Field primaryField;
private int[][] merge;
private int[] temp;
private final Field[] attrs;
private final Field primaryField;
private final int[][] merge;
private final int[] temp;
public CommonMerge(List data, CommonMergeOptions mergeOptions) throws Exception {
String[] attrs = mergeOptions.getAttrs();

@ -14,7 +14,7 @@ import java.util.function.Function;
public class IpUtil {
private static final RestTemplate restTemplate = new RestTemplate();
private static List<Function<String, String>> handlerList = new ArrayList<>();
private static final List<Function<String, String>> handlerList = new ArrayList<>();
static {
handlerList.add(new PconlineApi());
@ -50,7 +50,6 @@ public class IpUtil {
if (!StringUtils.isEmpty(cache)) {
return cache;
}
if (handlerList.size() == 0) return null;
for (Function<String, String> handler : handlerList) {
try {
cache = handler.apply(ip);

@ -9,11 +9,11 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class QiniuUtil {
private static String ACCESS_KEY = (String) YmlUtil.get("qiniu.access-key");
private static String SECRET_KEY = (String) YmlUtil.get("qiniu.secret-key");
private static String BUCKET = (String) YmlUtil.get("qiniu.bucket");
private static Auth AUTH = Auth.create(ACCESS_KEY, SECRET_KEY);
private static BucketManager BUCKET_MANAGER = new BucketManager(AUTH, new Configuration(Region.huadong()));
private static final String ACCESS_KEY = (String) YmlUtil.get("qiniu.access-key");
private static final String SECRET_KEY = (String) YmlUtil.get("qiniu.secret-key");
private static final String BUCKET = (String) YmlUtil.get("qiniu.bucket");
private static final Auth AUTH = Auth.create(ACCESS_KEY, SECRET_KEY);
private static final BucketManager BUCKET_MANAGER = new BucketManager(AUTH, new Configuration(Region.huadong()));
public static String getToken() {
return AUTH.uploadToken(BUCKET);
@ -23,7 +23,7 @@ public class QiniuUtil {
try {
BUCKET_MANAGER.delete(BUCKET, key);
} catch (QiniuException e) {
log.error("七牛云删除单个文件失败,{}", e.getMessage());
log.info("七牛云删除单个文件失败,{}", e.getMessage());
}
}
@ -33,7 +33,7 @@ public class QiniuUtil {
try {
BUCKET_MANAGER.batch(batchOperations);
} catch (QiniuException e) {
log.error("七牛云批量删除文件失败,{}", e.getMessage());
log.info("七牛云批量删除文件失败,{}", e.getMessage());
}
}
}

@ -46,6 +46,7 @@ socket:
max-http-content: 1048576
event:
logout: logout
new-message: new-message
file:
#临时文件保存目录

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.toesbieya.my.mapper.MsgMapper">
<sql id="searchCondition">
<if test="title != null and title != ''">
and title like "%"#{title}"%"
</if>
<if test="type != null and type != ''">
and type in (${type})
</if>
<if test="uid != null">
and uid=#{uid}
</if>
<if test="status != null and status != ''">
and status in (${status})
</if>
</sql>
<insert id="insert" parameterType="com.toesbieya.my.model.entity.Msg">
insert into msg (title, content, type, cid, cname, ctime,
pid, pname, ptime, wid, wname, wtime, status, `all`, recipient)
values (#{title}, #{content}, #{type}, #{cid}, #{cname}, #{ctime}, #{pid},
#{pname}, #{ptime}, #{wid}, #{wname}, #{wtime}, #{status}, #{all}, #{recipient})
</insert>
<insert id="insertState" parameterType="com.toesbieya.my.model.entity.MsgState">
insert into msg_state (mid, uid, time)
values (#{mid}, #{uid}, #{time})
</insert>
<select id="checkRead" resultType="boolean">
select if(count(1) > 0, 1, 0)
from msg_state
where mid = #{mid}
and uid = #{uid}
</select>
<select id="search" resultType="com.toesbieya.my.model.entity.Msg"
parameterType="com.toesbieya.my.model.vo.search.MsgSearch">
select *
from msg where 1
<include refid="searchCondition"/>
order by id desc
</select>
<update id="update" parameterType="com.toesbieya.my.model.entity.Msg">
update msg
set title=#{title},
content=#{content},
type=#{type},
pid=#{pid},
pname=#{pname},
ptime=#{ptime},
wid=#{wid},
wname=#{wname},
wtime=#{wtime},
status=#{status},
`all`=#{all},
recipient=#{recipient}
where id = #{id}
</update>
<update id="withdraw">
update msg
set status=#{status},
wid=#{wid},
wname=#{wname},
wtime=#{wtime}
where id = #{id}
and status = 1
</update>
<delete id="del">
delete
from msg
where id = #{id}
and status = 0
</delete>
<select id="getReadByUser" parameterType="com.toesbieya.my.model.vo.search.MsgPersonalSearch"
resultType="com.toesbieya.my.model.entity.Msg">
select a.id,
a.title,
a.content,
a.type,
a.pname,
a.ptime
from msg a
join msg_state b on a.id = b.mid
where b.uid = #{uid}
order by a.id desc
</select>
<select id="getUnreadByUser" parameterType="com.toesbieya.my.model.vo.search.MsgPersonalSearch"
resultType="com.toesbieya.my.model.entity.Msg">
select id,
title,
content,
type,
pname,
ptime
from msg
where status = 1
and ptime <![CDATA[ >= ]]> #{ctime}
and (`all` = 1 or `all` = 0 and find_in_set(recipient, #{uid}))
and id not in (select mid from msg_state where uid = #{uid})
order by id desc
</select>
<select id="getUnreadCountByUser" parameterType="com.toesbieya.my.model.vo.search.MsgPersonalSearch"
resultType="java.lang.Integer">
select count(1)
from msg
where status = 1
and ptime <![CDATA[ >= ]]> #{ctime}
and (`all` = 1 or `all` = 0 and find_in_set(recipient, #{uid}))
and id not in (select mid from msg_state where uid = #{uid})
</select>
</mapper>

4277
vue/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -9,35 +9,36 @@
},
"dependencies": {
"axios": "^0.19.2",
"core-js": "^3.6.4",
"core-js": "^3.6.5",
"decimal.js": "^10.2.0",
"element-ui": "^2.13.0",
"element-ui": "^2.13.2",
"fuse.js": "^3.6.1",
"js-md5": "^0.7.3",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"pako": "^1.0.11",
"path-to-regexp": "^6.1.0",
"socket.io-client": "^2.3.0",
"v-viewer": "^1.5.1",
"vue": "^2.6.11",
"vue-count-to": "^1.0.13",
"vue-cropper": "^0.5.0",
"vue-router": "^3.1.6",
"vuex": "^3.1.3"
"vue-cropper": "^0.5.3",
"vue-router": "^3.3.2",
"vuex": "^3.4.0"
},
"devDependencies": {
"@babel/core": "^7.8.7",
"@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-router": "^4.2.3",
"@vue/cli-plugin-vuex": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"@babel/core": "^7.10.2",
"@vue/cli-plugin-babel": "^4.4.1",
"@vue/cli-plugin-router": "^4.4.1",
"@vue/cli-plugin-vuex": "^4.4.1",
"@vue/cli-service": "^4.4.1",
"compression-webpack-plugin": "^3.1.0",
"html-webpack-plugin": "^4.0.0-beta.11",
"node-sass": "^4.13.1",
"html-webpack-plugin": "^4.3.0",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"script-ext-html-webpack-plugin": "^2.1.4",
"script-loader": "^0.7.2",
"svg-sprite-loader": "^4.2.1",
"svg-sprite-loader": "^4.3.0",
"svgo": "^1.3.2",
"vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^3.6.1"

@ -13,19 +13,20 @@
</body>
<!--echarts-->
<script src="https://cdn.bootcss.com/echarts/4.6.0/echarts.min.js"></script>
<script src="/static/echarts/macarons.js"></script>
<script defer src="https://cdn.bootcss.com/echarts/4.6.0/echarts.min.js"></script>
<script defer src="/static/echarts/macarons.js"></script>
<!--photoSwipe-->
<!--两种图片预览插件-->
<link href="https://cdn.bootcss.com/photoswipe/4.1.3/photoswipe.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/photoswipe/4.1.3/default-skin/default-skin.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/photoswipe/4.1.3/photoswipe.min.js"></script>
<script src="https://cdn.bootcss.com/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>
<!--viewer-->
<script defer src="https://cdn.bootcss.com/photoswipe/4.1.3/photoswipe.min.js"></script>
<script defer src="https://cdn.bootcss.com/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>
<link href="https://cdn.bootcss.com/viewerjs/1.5.0/viewer.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/viewerjs/1.5.0/viewer.min.js"></script>
<script async src="https://cdn.bootcss.com/viewerjs/1.5.0/viewer.min.js"></script>
<!--excel-->
<script src="/static/js/xlsx.full.min.js"></script>
<script async src="/static/js/xlsx.full.min.js"></script>
<!--富文本-->
<script async src="https://cdn.bootcdn.net/ajax/libs/tinymce/5.3.1/tinymce.min.js"></script>
</html>

@ -0,0 +1,419 @@
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u9500",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u533a\u5757",
"Inline": "\u6587\u672c",
"Blocks": "\u57fa\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Fonts": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "\u7c7b\u578b",
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
"OR": "\u6216",
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
"Upload": "\u4e0a\u4f20",
"Block": "\u5757",
"Align": "\u5bf9\u9f50",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor...": "\u951a\u70b9...",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "\u64cd\u4f5c",
"Shortcut": "\u5feb\u6377\u952e",
"Help": "\u5e2e\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u63d2\u4ef6",
"Handy Shortcuts": "\u5feb\u6377\u952e",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image...": "\u56fe\u7247...",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
"Current window": "\u5f53\u524d\u7a97\u53e3",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link...": "\u94fe\u63a5...",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"Link list": "\u94fe\u63a5\u5217\u8868",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media...": "\u591a\u5a92\u4f53...",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print...": "\u6253\u5370...",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Previous": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Find whole words only": "\u5168\u5b57\u5339\u914d",
"Spell check": "\u62fc\u5199\u68c0\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Show caption": "\u663e\u793a\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
"Templates": "\u6a21\u677f",
"Template": "\u6a21\u677f",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Remove color": "\u79fb\u9664\u989c\u8272",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Word count": "\u5b57\u6570",
"Count": "\u8ba1\u6570",
"Document": "\u6587\u6863",
"Selection": "\u9009\u62e9",
"Words": "\u5355\u8bcd",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"{0} words": "{0} \u5b57",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
"Image title": "\u56fe\u7247\u6807\u9898",
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
"Border style": "\u8fb9\u6846\u6837\u5f0f",
"Error": "\u9519\u8bef",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
"example": "\u793a\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8d27\u5e01",
"Text": "\u6587\u5b57",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6570\u5b66",
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
"Symbols": "\u7b26\u53f7",
"Arrows": "\u7bad\u5934",
"User Defined": "\u81ea\u5b9a\u4e49",
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
"colon sign": "\u5192\u53f7",
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
"naira sign": "\u5948\u62c9\u7b26\u53f7",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
"won sign": "\u97e9\u5143\u7b26\u53f7",
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
"cedi sign": "\u585e\u5730\u7b26\u53f7",
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
"spesmilo sign": "spesmilo\u7b26\u53f7",
"tenge sign": "\u575a\u6208\u7b26\u53f7",
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
"yen character": "\u65e5\u5143\u5b57\u6837",
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
"People": "\u4eba\u7c7b",
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
"Activity": "\u6d3b\u52a8",
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u5e1c",
"Characters": "\u5b57\u7b26",
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
"{0} characters": "{0} \u4e2a\u5b57\u7b26",
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u989c\u8272\u6837\u672c",
"Turquoise": "\u9752\u7eff\u8272",
"Green": "\u7eff\u8272",
"Blue": "\u84dd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6d77\u519b\u84dd",
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
"Dark Green": "\u6df1\u7eff\u8272",
"Medium Blue": "\u4e2d\u84dd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u6df1\u84dd\u8272",
"Yellow": "\u9ec4\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7ea2\u8272",
"Light Gray": "\u6d45\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6697\u9ec4\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6df1\u7ea2\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Light Green": "\u6d45\u7eff\u8272",
"Light Yellow": "\u6d45\u9ec4\u8272",
"Light Red": "\u6d45\u7ea2\u8272",
"Light Purple": "\u6d45\u7d2b\u8272",
"Light Blue": "\u6d45\u84dd\u8272",
"Dark Purple": "\u6df1\u7d2b\u8272",
"Dark Blue": "\u6df1\u84dd\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
"history": "\u5386\u53f2",
"styles": "\u6837\u5f0f",
"formatting": "\u683c\u5f0f\u5316",
"alignment": "\u5bf9\u9f50",
"indentation": "\u7f29\u8fdb",
"permanent pen": "\u8bb0\u53f7\u7b14",
"comments": "\u5907\u6ce8",
"Format Painter": "\u683c\u5f0f\u5237",
"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
"Capitalization": "\u5927\u5199",
"lowercase": "\u5c0f\u5199",
"UPPERCASE": "\u5927\u5199",
"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
"Font": "\u5b57\u4f53",
"Size": "\u5b57\u53f7",
"More...": "\u66f4\u591a...",
"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
"Select...": "\u9009\u62e9...",
"Preferences": "\u9996\u9009\u9879",
"Yes": "\u662f",
"No": "\u5426",
"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
"Version": "\u7248\u672c",
"Anchor": "\u951a\u70b9",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
"Color": "\u989c\u8272",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Image": "\u56fe\u7247",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"Link": "\u94fe\u63a5",
"Poster": "\u5c01\u9762",
"Media": "\u5a92\u4f53",
"Print": "\u6253\u5370",
"Prev": "\u4e0a\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Caption": "\u6807\u9898",
"Insert template": "\u63d2\u5165\u6a21\u677f"
});

@ -0,0 +1,27 @@
import request from '@/config/request'
const baseUrl = '/message/manage'
export function search(data) {
return request.post(baseUrl + '/search', data).then(({data}) => data.data)
}
export function add(data) {
return request.post(baseUrl + '/add', data).then(({data}) => data)
}
export function update(data) {
return request.post(baseUrl + '/update', data).then(({data}) => data)
}
export function publish(data) {
return request.post(baseUrl + '/publish', data).then(({data}) => data)
}
export function withdraw(data) {
return request.post(baseUrl + '/withdraw', data).then(({data}) => data)
}
export function del(id) {
return request.get(baseUrl + `/del?id=${id}`).then(({data}) => data)
}

@ -0,0 +1,15 @@
import request from '@/config/request'
const baseUrl = '/message/user'
export function search(data) {
return request.post(baseUrl + '/search', data).then(({data}) => data.data)
}
export function read(id) {
return request.get(baseUrl + `/read?id=${id}`).then(({data}) => data)
}
export function readAll() {
return request.get(baseUrl + '/readAll').then(({data}) => data)
}

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg>

After

Width:  |  Height:  |  Size: 669 B

@ -47,7 +47,7 @@
}
.el-pagination {
padding-top: 10px;
padding-top: 20px;
}
.el-scrollbar {

@ -1,6 +1,9 @@
@import "~nprogress/nprogress.css";
#nprogress {
.bar {
background: $--color-primary;
height: 5px;
.peg {
box-shadow: 0 0 10px $--color-primary, 0 0 5px $--color-primary;;

@ -17,6 +17,7 @@
line-height: 35px;
color: $--color-gray;
background: #fff;
border-radius: 3px;
padding: 0 12px;
font-size: 12px;
margin: 4px;

@ -0,0 +1,36 @@
<template>
<el-select :value="value" :disabled="disabled" multiple size="small" @input="emit">
<el-option
v-for="user in data"
:key="user.id"
:label="user.name"
:value="user.id"
/>
</el-select>
</template>
<script>
import {getUsers} from '@/api/system/user'
export default {
name: "SimpleMultipleUserSelector",
props: {
value: Array,
disabled: Boolean
},
data() {
return {
data: []
}
},
methods: {
emit(v) {
this.$emit('input', v)
}
},
mounted() {
getUsers({page: 1, pageSize: 9999})
.then(({list}) => this.data = list)
}
}
</script>

@ -0,0 +1,5 @@
import SimpleMultipleUserSelector from "./SimpleMultipleUserSelector"
export {
SimpleMultipleUserSelector
}

@ -2,11 +2,12 @@
export default {
name: "DialogFormItem",
functional: true,
props: {full: Boolean, label: String, prop: String},
props: {full: Boolean, dense: Boolean, label: String, prop: String},
render(h, context) {
let {full, label, prop} = context.props
let {full, dense, label, prop} = context.props
let attrs = {xs: 24, sm: 12, lg: 8}
if (full) attrs = {span: 24}
else if (dense) attrs.lg = 12
return (
<el-col {...{attrs}}>
<el-form-item label={label} prop={prop}>

@ -12,7 +12,7 @@
<div class="dialog-form-container">
<loading-mask :show="loading"/>
<el-scrollbar>
<div style="padding: 20px 20px 0">
<div class="dialog-form-body">
<slot/>
</div>
<div style="margin-bottom: 17px"/>
@ -48,6 +48,10 @@
position: relative;
width: 100%;
.dialog-form-body {
padding: 24px 24px 0
}
.el-form {
padding: 0;
}

@ -0,0 +1,49 @@
<script type="text/jsx">
export default {
name: 'DefaultEmptyImage',
functional: true,
render() {
return (
<svg width="184" height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<g transform="translate(24 31.67)">
<ellipse
fillOpacity=".8"
fill="#F5F5F7"
cx="67.797"
cy="106.89"
rx="67.797"
ry="12.668"
/>
<path
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
fill="#AEB8C2"
/>
<path
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
fill="url(#linearGradient-1)"
transform="translate(13.56)"
/>
<path
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
fill="#F5F5F7"
/>
<path
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
fill="#DCE0E6"
/>
</g>
<path
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
fill="#DCE0E6"
/>
<g transform="translate(149.65 15.383)" fill="#FFF">
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"/>
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"/>
</g>
</g>
</svg>
)
}
}
</script>

@ -0,0 +1,82 @@
<script type="text/jsx">
import DefaultEmptyImage from "./DefaultEmptyImage"
export default {
name: "AEmpty",
functional: true,
props: {
description: String,
image: [String, Object],
imageStyle: [String, Object]
},
render(h, context) {
const {description = '暂无数据', image, imageStyle} = context.props
const prefixCls = 'ant-empty'
const cls = {[prefixCls]: true}
let imageNode = null
if (typeof image === 'string') {
imageNode = <img alt={description} src={image}/>
}
else {
imageNode = <DefaultEmptyImage/>
}
return (
<div class={cls} {...{on: context.listeners}}>
<div class={`${prefixCls}-image`} style={imageStyle}>{imageNode}</div>
{description && <p class={`${prefixCls}-description`}>{description}</p>}
{context.children && <div class={`${prefixCls}-footer`}>{context.children}</div>}
</div>
)
}
}
</script>
<style lang="scss">
.ant-empty {
margin: 0 8px;
font-size: 14px;
line-height: 22px;
text-align: center;
&-image {
height: 100px;
margin-bottom: 8px;
img {
height: 100%;
}
svg {
height: 100%;
margin: auto;
}
}
&-description {
margin: 0;
}
&-footer {
margin-top: 16px;
}
&-normal {
margin: 32px 0;
color: rgba(0, 0, 0, .25);
.ant-empty-image {
height: 40px;
}
}
&-small {
margin: 8px 0;
color: rgba(0, 0, 0, .25);
.ant-empty-image {
height: 35px;
}
}
}
</style>

@ -1,6 +1,4 @@
<script type="text/jsx">
import {debounce} from "@/utils"
export default {
name: "SearchForm",
provide() {
@ -38,7 +36,6 @@
}
},
mounted() {
this.resize = debounce(this.resize)
this.resize()
window.addEventListener('resize', this.resize)
},

@ -0,0 +1,123 @@
<template>
<div v-loading="!hasInit" element-loading-text="正在加载编辑器..." class="tinymce-container" :style="{width}">
<div class="tinymce-wrapper">
<textarea :id="id" class="tinymce-textarea"/>
</div>
</div>
</template>
<script>
import {waitUntilSuccess} from "@/utils"
import {elError} from "@/utils/message"
const DEFAULT_OPTIONS = {
language: 'zh_CN',
language_url: '/static/tinymce/langs/zh_CN.js',
object_resizing: false,
end_container_on_empty_block: true,
default_link_target: '_blank',
convert_urls: false
}
export default {
name: "TinymceEditor",
props: {
value: String,
readonly: Boolean,
height: {
type: String,
default: '400px'
},
width: {
type: String,
default: '100%'
}
},
data() {
return {
id: 'tinymce-' + Date.now(),
fullscreen: false,
hasInit: false,
manualChange: false,
}
},
watch: {
value(value) {
if (!this.hasInit) return
if (this.manualChange) this.manualChange = false
else this.set(value)
},
readonly(value) {
this.hasInit && this.getInstance().mode.set(value ? 'readonly' : 'design')
}
},
methods: {
getInstance() {
return window.tinymce.get(this.id)
},
get() {
return this.getInstance().getContent()
},
set(content) {
this.getInstance().setContent(content || '')
},
init() {
const ctx = this
waitUntilSuccess(() => window.tinymce)
.then(() => {
window.tinymce.init({
...DEFAULT_OPTIONS,
selector: `#${ctx.id}`,
height: ctx.height,
init_instance_callback(editor) {
ctx.hasInit = true
editor.setContent(this.value || '')
},
setup(editor) {
editor.on('keyup undo redo', () => {
ctx.manualChange = true
ctx.$emit('input', editor.getContent())
})
editor.on('FullscreenStateChanged', e => {
ctx.fullscreen = e.state
})
}
})
})
.catch(() => elError('tinymce初始化失败,请检查js是否引入成功'))
},
destroy() {
const tinymce = this.getInstance()
if (!tinymce) return
this.fullscreen && tinymce.execCommand('mceFullScreen')
tinymce.destroy()
}
},
mounted() {
!this.readonly && this.init()
},
destroyed() {
this.destroy()
},
}
</script>
<style lang="scss">
.tinymce-container {
position: relative;
.tinymce-wrapper {
width: 100%;
.tinymce-textarea {
visibility: hidden;
}
}
}
.tox-tinymce-aux {
z-index: 10000!important;
}
</style>

@ -0,0 +1,32 @@
<template>
<el-badge :hidden="hidden" class="bell-badge" is-dot @click.native="jump">
<i class="el-icon-bell navbar-icon" title="消息提醒"/>
</el-badge>
</template>
<script>
export default {
name: "Bell",
computed: {
hidden() {
return this.$store.state.message.unreadCount < 1
}
},
methods: {
jump() {
let target = '/message/user'
if (this.$route.path === target) target = '/redirect' + target
this.$router.replace(target)
}
},
mounted() {
this.$store.dispatch('message/refresh')
}
}
</script>
<style>
.bell-badge .el-badge__content {
top: 15px !important;
right: 10px !important;
}
</style>

@ -7,7 +7,7 @@
<div class="right-menu">
<search id="header-search" class="right-menu-item hidden-xs"/>
<fullscreen v-model="fullscreen" class="fullscreen-btn right-menu-item hidden-xs" title="全屏开关"/>
<bell class="right-menu-item"/>
<div class="setting-btn right-menu-item hidden-xs" @click="settingDrawer=true">
<i class="el-icon-s-operation navbar-icon" title="个性设置"/>
@ -50,7 +50,7 @@
import Hamburger from './components/Hamburger'
import Breadcrumb from './components/Breadcrumb'
import Search from './components/HeaderSearch'
import Fullscreen from "./components/Fullscreen"
import Bell from "./components/Bell"
import SettingDrawer from './components/SettingDrawer'
import {mapState} from 'vuex'
import GuideMixin from '@/mixins/guide'
@ -60,7 +60,7 @@
export default {
name: 'navbar',
mixins: [GuideMixin.navbar],
components: {Hamburger, Breadcrumb, Search, Fullscreen, SettingDrawer},
components: {Hamburger, Breadcrumb, Search, Bell, SettingDrawer},
data() {
return {
fullscreen: false,
@ -122,6 +122,7 @@
.right-menu-item {
display: inline-block;
position: relative;
padding: 0 8px;
height: 100%;
font-size: 18px;
@ -136,8 +137,6 @@
}
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
margin-top: 5px;
position: relative;

@ -1,6 +1,6 @@
import UploadFile from '@/bizComponents/UploadFile'
import DialogForm from '@/bizComponents/DialogForm'
import DialogFormItem from "@/bizComponents/DialogForm/DialogFormItem"
import UploadFile from '@/components/UploadFile'
import DialogForm from '@/components/DialogForm'
import DialogFormItem from "@/components/DialogForm/DialogFormItem"
import DocumentSteps from '@/bizComponents/DocumentSteps'
import DocumentHistory from '@/bizComponents/DocumentHistory'
import dialogMixin from "@/mixins/dialogMixin"
@ -16,12 +16,12 @@ export default {
components: {DialogForm, DialogFormItem, DocumentSteps, DocumentHistory, UploadFile},
props: {
//dialog显隐
value: {type: Boolean, default: false},
value: Boolean,
//编辑模式,see,add,edit
type: {type: String, default: 'see'},
//单据id
id: String,
baseUrl: String,
baseUrl: String
},
data() {
return {
@ -103,7 +103,7 @@ export default {
this.$refs.form.validate(v => {
if (!v) return
if (this.validate) {
let valid = this.validate()
const valid = this.validate()
if (!isEmpty(valid)) return elAlert(valid)
}
this.loading = true
@ -112,7 +112,6 @@ export default {
.then(({data, msg}) => {
this.needSearch = true
elSuccess(msg)
this.$emit('success', this.type)
const id = this.type === 'add' ? data : this.form.id
this.$emit('update:type', 'edit')
this.init(id)
@ -136,7 +135,6 @@ export default {
.then(({data, msg}) => {
elSuccess(msg)
this.needSearch = true
this.$emit('success', 'commit')
const id = this.type === 'add' ? data : this.form.id
this.$emit('update:type', 'edit')
this.init(id)
@ -156,7 +154,6 @@ export default {
.then(({msg}) => {
elSuccess(msg)
this.needSearch = true
this.$emit('success', 'withdraw')
this.init(this.form.id)
})
.catch(() => this.loading = false)
@ -172,7 +169,6 @@ export default {
})
.then(({msg}) => {
elSuccess(msg)
this.$emit('success', 'pass')
this.needSearch = true
this.closeDialog()
})
@ -189,7 +185,6 @@ export default {
})
.then(({msg}) => {
elSuccess(msg)
this.$emit('success', 'reject')
this.needSearch = true
this.closeDialog()
})
@ -253,7 +248,8 @@ export default {
//关闭dialog并清除单据数据
cancel() {
this.closeDialog()
if (this.needSearch) this.$emit('search')
this.needSearch && this.$emit('search')
//删除未保存的上传附件
let deleteArr = []
if (this.form.uploadImageList.length > 0) {
@ -262,6 +258,8 @@ export default {
if (deleteArr.length > 0) {
deleteUpload(deleteArr).catch(e => ({}))
}
//延时清除表单数据
setTimeout(() => this.clearForm(), 200)
this.loading = false
},

@ -30,7 +30,7 @@ const mixin = {
this.search()
},
},
activated() {
mounted() {
this.search()
}
}

@ -90,7 +90,7 @@ export default class ParticleNetwork {
window.clearInterval(this.timer)
this.timer = setInterval(() => {
if (counter < quantity - 1) this.particles.push(new Particle(this))
else clearInterval(this.timer)
else window.clearInterval(this.timer)
counter++
}, 250)
}

@ -2,6 +2,7 @@
import purchaseRouter from '@/router/modules/purchase'
import sellRouter from '@/router/modules/sell'
import stockRouter from '@/router/modules/stock'
import messageRouter from '@/router/modules/message'
import systemRouter from '@/router/modules/system'
@ -9,6 +10,7 @@ const authorityRoutes = [
purchaseRouter,
sellRouter,
stockRouter,
messageRouter,
systemRouter,
]

@ -5,7 +5,6 @@
* 左侧菜单显示name && !hidden && meta.title
* 左侧菜单不折叠只有一个children的路由alwaysShow
* 面包屑显示meta.title
* 面包屑不跳转!meta.breadcrumb
* 搜索选项显示name && meta.title
* tab栏固定显示meta.affix
* 页面不缓存!name || meta.noCache
@ -13,8 +12,8 @@
* */
import Vue from 'vue'
import Router from 'vue-router'
import {pathToRegexp} from 'path-to-regexp'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import {isUserExist} from "@/utils/sessionStorage"
import {auth, needAuth} from "@/utils/auth"
import {isEmpty} from "@/utils"
@ -28,30 +27,27 @@ Vue.use(Router)
NProgress.configure({showSpinner: false})
const endRoute = [{path: '*', redirect: '/404', hidden: true}]
const urlNoNeedLogin = {
'/login': 1,
'/register': 1,
'/404': 1,
'/403': 1,
}
const urlNoNeedLogin = [
'/login', '/register', '/404', '/403'
].map(url => pathToRegexp(url))
metaExtend(constantRoutes)
metaExtend(authorityRoutes)
const createRouter = () => new Router({
const router = new Router({
scrollBehavior: () => ({y: 0}),
routes: constantRoutes.concat(authorityRoutes, endRoute)
})
const router = createRouter()
router.beforeEach(async (to, from, next) => {
NProgress.start()
//使用redirect进行跳转时不显示进度条
!to.path.startsWith('/redirect') && NProgress.start()
document.title = getPageTitle(to.meta.title)
//不需要登录的页面也不需要进行权限控制
if (urlNoNeedLogin[to.path]) return next()
if (urlNoNeedLogin.some(reg => reg.test(to.path))) return next()
const isLogin = isUserExist()
@ -74,9 +70,7 @@ router.beforeEach(async (to, from, next) => {
next({path: '/403'})
})
router.afterEach(() => {
NProgress.done()
})
router.afterEach(() => NProgress.done())
//初始化菜单和权限
function initMenu() {

@ -4,7 +4,7 @@ import Layout from '@/layout'
const router = {
path: '/example',
component: Layout,
meta: {title: '演示用例', icon: 'show', noCache: true, breadcrumb: false},
meta: {title: '演示用例', icon: 'show', noCache: true},
children: [
{
path: 'style-page',

@ -0,0 +1,25 @@
/*路由表:消息中心*/
import Layout from '@/layout'
const router = {
path: '/message',
component: Layout,
alwaysShow: true,
meta: {title: '消息中心', icon: 'message'},
children: [
{
path: 'manage',
name: 'messageManagement',
component: () => import('@/views/message/manage'),
meta: {title: '消息管理'}
},
{
path: 'user',
name: 'userMessage',
component: () => import('@/views/message/user'),
meta: {title: '个人消息', noAuth: true, noCache: true}
}
]
}
export default router

@ -5,7 +5,7 @@ const router = {
path: '/purchase',
component: Layout,
alwaysShow: true,
meta: {title: '采购管理', icon: 'shopping', breadcrumb: false},
meta: {title: '采购管理', icon: 'shopping'},
children: [
{
path: 'order',

@ -5,7 +5,7 @@ const router = {
path: '/sell',
component: Layout,
alwaysShow: true,
meta: {title: '销售管理', icon: 'sell', breadcrumb: false},
meta: {title: '销售管理', icon: 'sell'},
children: [
{
path: 'order',

@ -5,7 +5,7 @@ const router = {
path: '/system',
component: Layout,
alwaysShow: true,
meta: {title: '系统管理', icon: 'system', breadcrumb: false},
meta: {title: '系统管理', icon: 'system'},
children: [
{
path: 'department',

@ -8,11 +8,11 @@ const state = {
const mutations = {
...createMutations(state),
addIframe(state, src) {
add(state, src) {
if (state.list.some(i => i === src)) return
state.list.push(src)
},
delIframe(state, src) {
del(state, src) {
if (!src) return
let index = state.list.findIndex(i => i === src)
index > -1 && state.list.splice(index, 1)
@ -24,12 +24,12 @@ const actions = {
if (!src) return
commit('show', true)
commit('current', src)
commit('addIframe', src)
commit('add', src)
},
close({commit}, src) {
commit('show', false)
commit('current', '')
commit('delIframe', src)
commit('del', src)
}
}

@ -0,0 +1,22 @@
import {createMutations} from "@/utils"
import {search} from '@/api/message/user'
const state = {
unreadCount: 0
}
const mutations = createMutations(state)
const actions={
refresh({commit}) {
search({page: 1, pageSize: 1, unread: true})
.then(({data}) => commit('unreadCount', data))
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

@ -17,6 +17,7 @@ const state = {
routes: [],
sidebarMenus: [],
data: localResource,
dataMap: {},
tree: createTree(localResource),
hasInitRoutes: false
}
@ -32,7 +33,11 @@ const mutations = {
},
data(state, data) {
state.data = data || []
state.tree = createTree(data)
state.dataMap = state.data.reduce((map, item) => {
map[item.url] = item.id
return map
}, {})
state.tree = createTree(state.data)
setLocalResource(data)
},
hasInitRoutes(state, sign) {
@ -56,7 +61,7 @@ const actions = {
resolve()
})
},
initResource({state, commit}) {
initResource({commit}) {
return new Promise(resolve => {
//if (state.data.length > 0) return resolve();
getResources()

@ -3,42 +3,27 @@ import {socketUrl} from '@/config'
import SocketIO from 'socket.io-client'
import {createMutations} from "@/utils"
let socket
const state = {
socket: null,
online: true
}
const mutations = createMutations(state)
const actions = {
init({state, commit, dispatch, rootState}, user) {
init(context, user) {
if (!user || !user.id || !user.session_id) return
const socket = initSocket(user)
defaultEventBind(socket, {state, commit, dispatch})
socket = initSocket(user)
socket.on('logout', msg => {
if (rootState.user.prepare_logout) return
return MessageBox.alert(msg || '你已被强制下线,请重新登陆', {
type: 'warning',
beforeClose: (action, instance, done) => {
dispatch('user/logout', null, {root: true})
.then(() => {
done()
location.reload()
})
}
})
})
commit('socket', socket)
defaultEventBind(socket, context)
customEventBind(socket, context)
},
close({state, commit}) {
close() {
return new Promise(resolve => {
if (!state.socket) return resolve()
state.socket.close()
commit('socket', null)
if (!socket) return resolve()
socket.close()
resolve()
})
}
@ -88,6 +73,26 @@ function defaultEventBind(socket, {state, commit, dispatch}) {
})
}
function customEventBind(socket, {state, commit, dispatch, rootState}) {
socket.on('logout', msg => {
if (rootState.user.prepare_logout) return
return MessageBox.alert(msg || '你已被强制下线,请重新登陆', {
type: 'warning',
beforeClose: (action, instance, done) => {
dispatch('user/logout', null, {root: true})
.then(() => {
done()
location.reload()
})
}
})
})
socket.on('new-message', () => {
dispatch('message/refresh', null, {root: true})
})
}
export default {
namespaced: true,
state,

@ -48,7 +48,7 @@ const actions = {
delView({commit}, view) {
commit('delVisitedView', view)
commit('delCachedView', view)
commit('iframe/delIframe', view.meta ? view.meta.iframe : null, {root: true})
commit('iframe/del', view.meta ? view.meta.iframe : null, {root: true})
},
delOthersViews({state, commit}, view) {

@ -8,7 +8,9 @@ export function needAuth(route) {
export function auth(path) {
if (store.state.user.admin === 1) return true
const resources = store.state.user.resources
const resourceMap = store.state.resource.dataMap
if (!(path in resourceMap)) return true
const resources = store.state.user.resources
return resources && path in resources
}

@ -134,18 +134,27 @@ export function zip(str) {
* @param success 判断是否成功true or false
* @param callback 成功后的回调
* @param interval 循环间隔毫秒
* @param maxTryTime 最大循环次数超出reject小于1视为Infinity
*/
export function waitUntilSuccess(success, callback, interval = 1000) {
return new Promise(resolve => {
let fun = null
let check = () => {
export function waitUntilSuccess(success, callback, interval = 1000, maxTryTime = 0) {
return new Promise((resolve, reject) => {
let fun,
count = 0
const check = () => {
if (success()) {
clearInterval(fun)
window.clearInterval(fun)
typeof callback === 'function' && callback()
return resolve()
}
if (maxTryTime >= 1) {
count++
if (count >= maxTryTime) return reject()
}
}
if (success()) return check()
fun = setInterval(check, interval)
})
}

@ -8,7 +8,7 @@
</template>
<script>
import UploadFile from '@/bizComponents/UploadFile'
import UploadFile from '@/components/UploadFile'
import request from '@/config/request'
export default {

@ -61,7 +61,7 @@
<script>
import LinerProgress from '@/components/LinerProgress'
import DialogForm from "@/bizComponents/DialogForm"
import DialogForm from "@/components/DialogForm"
export default {
name: "stylePage",

@ -0,0 +1,241 @@
<template>
<dialog-form :loading="loading" :title="title" :value="value" width="50%" @close="cancel" @open="open">
<el-form
ref="form"
:model="form"
:rules="rules"
label-position="right"
label-width="100px"
size="small"
status-icon
>
<el-row :gutter="20">
<dialog-form-item label="标 题:" prop="title" dense>
<el-input v-model="form.title" maxlength="100" :readonly="!canSave"/>
</dialog-form-item>
<dialog-form-item label="消息类型:" prop="type" dense>
<el-select v-model="form.type" :disabled="!canSave">
<el-option :value="0" label="通知提醒"/>
<el-option :value="1" label="系统公告"/>
</el-select>
</dialog-form-item>
<dialog-form-item v-if="type!=='add'" label="创建人:" dense>
<el-input :value="form.cname" readonly/>
</dialog-form-item>
<dialog-form-item v-if="type!=='add'" label="创建时间:" dense>
<el-date-picker :value="form.ctime" format="yyyy-MM-dd HH:mm:ss" readonly type="date"/>
</dialog-form-item>
<dialog-form-item label="通知对象:" prop="all" dense>
<el-select v-model="form.all" :disabled="!canSave">
<el-option :value="0" label="指定用户"/>
<el-option :value="1" label="全体用户"/>
</el-select>
</dialog-form-item>
<dialog-form-item v-if="form.all===0" label="选择用户:" prop="recipient" dense>
<user-selector v-model="form.recipient" :disabled="!canSave"/>
</dialog-form-item>
<dialog-form-item v-if="form.pname" label="发布人:" dense>
<el-input :value="form.pname" readonly/>
</dialog-form-item>
<dialog-form-item v-if="form.ptime" label="发布时间:" dense>
<el-date-picker :value="form.ptime" format="yyyy-MM-dd HH:mm:ss" readonly type="date"/>
</dialog-form-item>
<dialog-form-item v-if="form.status===2" label="撤回人:" dense>
<el-input :value="form.wname" readonly/>
</dialog-form-item>
<dialog-form-item v-if="form.status===2" label="撤回时间:" dense>
<el-date-picker :value="form.wtime" format="yyyy-MM-dd HH:mm:ss" readonly type="date"/>
</dialog-form-item>
<dialog-form-item label="内 容:" full>
<tinymce-editor v-model="form.content" :readonly="!canSave"/>
</dialog-form-item>
</el-row>
</el-form>
<template v-slot:footer>
<el-button plain size="small" @click="closeDialog"> </el-button>
<el-button v-if="canSave" size="small" type="primary" @click="save"> </el-button>
<el-button v-if="canPublish" size="small" type="primary" @click="publish"> </el-button>
<el-button v-if="canWithdraw" size="small" type="danger" @click="withdraw"> </el-button>
</template>
</dialog-form>
</template>
<script>
import DialogForm from '@/components/DialogForm'
import DialogFormItem from "@/components/DialogForm/DialogFormItem"
import TinymceEditor from "@/components/TinymceEditor"
import {SimpleMultipleUserSelector as UserSelector} from '@/bizComponents/UserSelector'
import dialogMixin from "@/mixins/dialogMixin"
import {add, update, publish, withdraw} from "@/api/message/manage"
import {isEmpty, mergeObj, resetObj} from '@/utils'
import {elAlert, elConfirm, elSuccess} from "@/utils/message"
import {auth} from "@/utils/auth"
export default {
name: "EditDialog",
mixins: [dialogMixin],
components: {DialogForm, DialogFormItem, TinymceEditor, UserSelector},
props: {
value: {type: Boolean, default: false},
type: {type: String, default: 'see'},
data: {
type: Object,
default: () => ({})
},
baseUrl: String
},
data() {
return {
loading: false,
needSearch: false,
form: {
id: null,
title: null,
resume: null,
content: null,
type: 0,
cname: null,
ctime: null,
pname: null,
ptime: null,
wname: null,
wtime: null,
status: 0,
all: 1,
recipient: []
},
rules: {
title: [{required: true, message: '请输入标题', trigger: 'change'}],
type: [{required: true, message: '请选择消息类型', trigger: 'change'}],
all: [{required: true, message: '请选择通知对象', trigger: 'change'}]
}
}
},
computed: {
title() {
if (isEmpty(this.type)) return ''
switch (this.type) {
case 'see':
return '查看消息'
case 'add':
return '添加消息'
case 'edit':
return '编辑消息'
}
},
canSave() {
//addeditstatus=0
return this.type === 'add' && auth(this.baseUrl + '/add')
|| this.form.status === 0 && this.type === 'edit' && auth(this.baseUrl + '/update')
},
canPublish() {
//addeditstatus=0
return auth(this.baseUrl + '/publish') && (this.type === 'add' || this.type === 'edit' && this.form.status === 0)
},
canWithdraw() {
//editstatus=1
return auth(this.baseUrl + '/withdraw')
&& this.type === 'edit'
&& this.form.status === 1
},
},
methods: {
open() {
if (this.type === 'add') return
mergeObj(this.form, this.data)
const recipient = this.data.recipient || ''
this.form.recipient = recipient.split(',').map(i => parseInt(i))
},
clearForm() {
resetObj(this.form)
this.form.all = 1
this.needSearch = false
this.loading = false
this.$nextTick(() => this.$refs.form.clearValidate())
},
cancel() {
this.closeDialog()
this.needSearch && this.$emit('search')
setTimeout(() => this.clearForm(), 200)
this.loading = false
},
save() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
this.loading = true
const data = this.transformForm()
let promise = this.type === 'add' ? add(data) : update(data)
promise
.then(({data, msg}) => {
this.needSearch = true
elSuccess(msg)
if (this.type === 'add') {
this.form.id = data.id
this.form.cname = data.cname
this.form.ctime = data.ctime
}
this.$emit('update:type', 'edit')
})
.finally(() => this.loading = false)
})
},
publish() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
if (this.validate) {
let valid = this.validate()
if (!isEmpty(valid)) return elAlert(valid)
}
elConfirm('确认发布?')
.then(() => this.loading = true)
.then(() => publish(this.transformForm()))
.then(({data, msg}) => {
elSuccess(msg)
this.needSearch = true
this.form.id = data.id
this.form.cname = data.cname
this.form.ctime = data.ctime
this.form.pid = data.pid
this.form.pname = data.pname
this.form.ptime = data.ptime
this.form.status = data.status
this.$emit('update:type', 'edit')
})
.finally(() => this.loading = false)
})
},
withdraw() {
if (this.loading) return
elConfirm('确认撤回?')
.then(() => {
this.loading = true
return withdraw({id: this.form.id})
})
.then(({msg}) => {
elSuccess(msg)
this.needSearch = true
this.closeDialog()
})
.finally(() => this.loading = false)
},
transformForm() {
if (this.form.all === 1) return this.form
else return {...this.form, recipient: this.form.recipient.join(',')}
}
}
}
</script>

@ -0,0 +1,165 @@
<template>
<el-card v-loading="config.operating">
<search-form>
<search-form-item :sm="24" :lg="12" :xl="8" label="标 题:">
<el-input v-model="searchForm.title" clearable maxlength="100"/>
</search-form-item>
<search-form-item :sm="24" :lg="12" :xl="8" label="消息类型:">
<el-select v-model="searchForm.type" clearable @clear="searchForm.type=null">
<el-option :value="0" label="通知提醒"/>
<el-option :value="1" label="系统公告"/>
</el-select>
</search-form-item>
<search-form-item :sm="24" :lg="12" :xl="8" label="状 态:">
<el-select v-model="searchForm.status" clearable @clear="searchForm.status=null">
<el-option :value="0" label="拟定"/>
<el-option :value="1" label="已发布"/>
<el-option :value="2" label="已撤回"/>
</el-select>
</search-form-item>
</search-form>
<el-row class="button-group">
<el-button icon="el-icon-search" size="small" type="success" @click="search"> </el-button>
<el-button v-if="canAdd" icon="el-icon-plus" size="small" type="primary" @click="add"> </el-button>
<el-button icon="el-icon-view" size="small" type="primary" @click="see"> </el-button>
<el-button v-if="canUpdate" icon="el-icon-edit" size="small" type="primary" @click="edit"> </el-button>
<el-button v-if="canDel" icon="el-icon-delete" size="small" type="danger" @click="del"> </el-button>
</el-row>
<el-row v-loading="config.loading" class="table-container">
<el-table
ref="table"
:data="tableData"
current-row-key="id"
highlight-current-row
row-key="id"
@row-click="row=$event"
>
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="标 题" prop="title" show-overflow-tooltip/>
<el-table-column align="center" label="类 型" show-overflow-tooltip>
<template v-slot="{row}">{{row.type===0?'通知提醒':'系统公告'}}</template>
</el-table-column>
<el-table-column align="center" label="通知对象" show-overflow-tooltip>
<template v-slot="{row}">{{row.all===1?'全体用户':'指定用户'}}</template>
</el-table-column>
<el-table-column align="center" label="状 态" show-overflow-tooltip>
<template v-slot="{row}">{{transformStatus(row.status)}}</template>
</el-table-column>
<el-table-column align="center" label="发布人" prop="pname" show-overflow-tooltip/>
<el-table-column align="center" label="发布时间" width="150" show-overflow-tooltip>
<template v-slot="{row}">{{row.ptime | timestamp2Date}}</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
layout="total, prev, pager, next, jumper"
@current-change="pageChange"
/>
</el-row>
<edit-dialog
v-model="editDialog"
:base-url="baseUrl"
:data="row"
:type.sync="type"
@search="search"
/>
</el-card>
</template>
<script>
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import EditDialog from './EditDialog'
import {search, del} from "@/api/message/manage"
import {isEmpty} from '@/utils'
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {auth} from "@/utils/auth"
import tableMixin from '@/mixins/tablePageMixin'
export default {
name: "messageManagement",
mixins: [tableMixin],
components: {SearchForm, SearchFormItem, EditDialog},
data() {
return {
baseUrl: '/message/manage',
searchForm: {
title: null,
type: null,
status: null
},
type: 'add',
editDialog: false,
regionDialog: false
}
},
computed: {
canAdd() {
return auth(this.baseUrl + '/add')
},
canUpdate() {
return auth(this.baseUrl + '/update')
},
canDel() {
return auth(this.baseUrl + '/del')
}
},
methods: {
transformStatus(status) {
switch (status) {
case 0:
return '拟定'
case 1:
return '已发布'
case 2:
return '已撤回'
}
},
search() {
if (this.config.loading) return
this.config.loading = true
this.row = null
this.type = 'see'
search(this.searchForm)
.then(({list, total}) => {
this.searchForm.total = total
this.tableData = list
})
.finally(() => this.config.loading = false)
},
add() {
this.type = 'add'
this.editDialog = true
},
see() {
if (isEmpty(this.row)) return elError('请选择要查看的消息')
this.type = 'see'
this.editDialog = true
},
edit() {
if (isEmpty(this.row)) return elError('请选择要编辑的消息')
this.type = 'edit'
this.editDialog = true
},
del() {
if (isEmpty(this.row)) return elError('请选择要删除的消息')
if (this.row.status !== 0) return elError('只有状态为【拟定】时才能删除')
if (this.config.operating) return
elConfirm(`确定删除消息【${this.row.title}】?`)
.then(() => {
this.config.operating = true
return del(this.row.id)
})
.then(() => {
elSuccess('删除成功')
this.search()
})
.finally(() => this.config.operating = false)
}
}
}
</script>

@ -0,0 +1,183 @@
<template>
<div v-loading="config.loading" class="message-stream">
<el-button
v-if="showReadAllBtn>0"
plain
size="mini"
style="margin-bottom: 20px"
@click="readAll"
>
清除所有未读
</el-button>
<empty v-if="tableData.length===0"/>
<template v-else>
<el-timeline>
<el-timeline-item
v-for="msg in tableData"
:key="msg.index"
:timestamp="msg.time"
placement="top"
>
<el-card
v-for="child in msg.children"
:key="child.id"
:class="{'message-stream__item':true,viewed:child.read}"
@click.native="read(child)"
>
<div class="message-stream__item__title">
<span v-if="!child.read" class="dot success"/>
<b>{{transformType(child.type)}}</b>
{{child.title}}
</div>
<div v-html="child.content"/>
<div class="message-stream__item__footer">by: {{child.pname}}</div>
</el-card>
</el-timeline-item>
</el-timeline>
<el-pagination
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
hide-on-single-page
layout="prev, pager, next"
@current-change="pageChange"
/>
</template>
</div>
</template>
<script>
import tablePageMixin from '@/mixins/tablePageMixin'
import Empty from '@/components/Empty'
import {search, read, readAll} from "@/api/message/user"
import {isEmpty} from "@/utils"
export default {
name: "MessageStream",
mixins: [tablePageMixin],
components: {Empty},
props: {
mode: String,
},
data() {
return {
searchForm: {
pageSize: 30
},
}
},
computed: {
unreadCount() {
return this.$store.state.message.unreadCount
},
showReadAllBtn() {
return this.mode === 'unread' && this.unreadCount > 0
}
},
watch: {
mode() {
this.search()
}
},
methods: {
search() {
if (this.config.loading) return
this.config.loading = true
search({...this.searchForm, unread: this.mode === 'unread'})
.then(({list, total, data}) => {
!isEmpty(data) && this.$store.commit('message/unreadCount', data)
this.searchForm.total = total
this.tableData = this.transformList(list)
})
.finally(() => this.config.loading = false)
},
//
transformList(list) {
const thisYear = new Date().getFullYear()
const map = {}
let index = 0
list.forEach(item => {
let time = new Date(item.ptime),
year = time.getFullYear(),
month = time.getMonth() + 1,
day = time.getDate()
item.read = this.mode === 'read'
time = (year === thisYear ? '' : `${year}`) + `${month}${day}`
const already = map[time]
if (!already) {
map[time] = {index: index++, time, children: [item]}
}
else already.children.push(item)
})
const result = []
Object.keys(map).forEach(key => result[map[key].index] = map[key])
return result
},
read(msg) {
if (msg.read) return
msg.read = true
this.$store.commit('message/unreadCount', this.unreadCount - 1)
read(msg.id)
},
readAll() {
readAll().then(this.search)
},
transformType(type) {
switch (type) {
case 0:
return '通知提醒'
case 1:
return '系统公告'
}
},
}
}
</script>
<style lang="scss">
.message-stream {
&__item {
cursor: pointer;
position: relative;
color: #666;
background: #FFF9EA;
& + & {
margin-top: 20px;
}
&.viewed {
cursor: auto;
background: transparent;
&::before {
visibility: hidden;
}
}
&__title {
text-align: left;
.dot {
height: 8px;
width: 8px;
margin-right: 0;
}
}
&__footer {
text-align: right;
}
}
}
</style>

@ -0,0 +1,65 @@
<template>
<el-card class="user-message-page">
<el-tabs v-model="activeTab" :tab-position="tabPosition" stretch>
<el-tab-pane v-for="{label,name} in tabs" :key="name" :name="name" :label="label"/>
<div :class="tabContentClass">
<message-stream :mode="activeTab"/>
</div>
</el-tabs>
</el-card>
</template>
<script>
import MessageStream from './MessageStream'
export default {
name: "userMessage",
components: {MessageStream},
data() {
return {
tabs: [{label: '未读消息', name: 'unread'}, {label: '已读消息', name: 'read'}],
activeTab: 'unread',
}
},
computed: {
tabPosition() {
return this.$store.state.app.device === 'pc' ? 'left' : 'top'
},
tabContentClass() {
return this.tabPosition === 'left' ? 'tab-main-right' : 'tab-main-top'
}
}
}
</script>
<style lang="scss">
.user-message-page {
.el-tabs--left {
.el-tabs__item.is-left {
text-align: center;
&.is-active {
background: #f0faff;
}
}
.el-tabs__header.is-left {
min-width: 200px;
}
}
.el-tabs__item {
font-size: 16px;
}
.tab-main {
&-right {
padding: 8px 40px;
}
&-bottom {
padding: 20px 40px;
}
}
}
</style>

@ -96,7 +96,6 @@
</el-row>
<edit-dialog
ref="editDialog"
v-model="editDialog"
:base-url="baseUrl"
:id="row?row.id:null"
@ -108,8 +107,8 @@
<script>
import EditDialog from './components/EditDialog'
import SearchForm from '@/bizComponents/SearchForm'
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from '@/components/SearchForm'
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import documentTableMixin from '@/mixins/bizDocumentTableMixin'
import {del, getSubById, search} from "@/api/purchase/inbound"

@ -124,7 +124,6 @@
</el-row>
<edit-dialog
ref="editDialog"
v-model="editDialog"
:base-url="baseUrl"
:id="row?row.id:null"
@ -136,8 +135,8 @@
<script>
import EditDialog from './components/EditDialog'
import SearchForm from '@/bizComponents/SearchForm'
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from '@/components/SearchForm'
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import documentTableMixin from '@/mixins/bizDocumentTableMixin'
import {del, getSubById, search} from "@/api/purchase/order"

@ -124,7 +124,6 @@
</el-row>
<edit-dialog
ref="editDialog"
v-model="editDialog"
:base-url="baseUrl"
:id="row?row.id:null"
@ -136,8 +135,8 @@
<script>
import EditDialog from './components/EditDialog'
import SearchForm from '@/bizComponents/SearchForm'
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from '@/components/SearchForm'
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import documentTableMixin from '@/mixins/bizDocumentTableMixin'
import {del, getSubById, search} from "@/api/sell/order"

@ -96,7 +96,6 @@
</el-row>
<edit-dialog
ref="editDialog"
v-model="editDialog"
:base-url="baseUrl"
:id="row?row.id:null"
@ -108,8 +107,8 @@
<script>
import EditDialog from './components/EditDialog'
import SearchForm from '@/bizComponents/SearchForm'
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from '@/components/SearchForm'
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import documentTableMixin from '@/mixins/bizDocumentTableMixin'
import {del, getSubById, search} from "@/api/sell/outbound"

@ -32,7 +32,7 @@
<script>
import LinerProgress from '@/components/LinerProgress'
import DialogForm from "@/bizComponents/DialogForm"
import DialogForm from "@/components/DialogForm"
import dialogMixin from "@/mixins/dialogMixin"
import {isEmpty, mul, plus} from "@/utils"
import {getDetail} from "@/api/stock/current"

@ -72,8 +72,8 @@
</template>
<script>
import SearchForm from "@/bizComponents/SearchForm"
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import CategoryTree from '@/bizComponents/CategoryTree'
import DetailDialog from "./DetailDialog"
import {search} from "@/api/stock/current"

@ -50,7 +50,7 @@
</template>
<script>
import DialogForm from '@/bizComponents/DialogForm'
import DialogForm from '@/components/DialogForm'
import RegionSelector from '@/bizComponents/RegionSelector'
import dialogMixin from "@/mixins/dialogMixin"
import {addCustomer, updateCustomer} from "@/api/system/customer"
@ -66,8 +66,7 @@
type: {type: String, default: 'see'},
data: {
type: Object,
default: () => {
}
default: () => ({})
},
},
data() {

@ -78,8 +78,8 @@
</template>
<script>
import SearchForm from "@/bizComponents/SearchForm"
import SearchFormItem from "@/bizComponents/SearchForm/SearchFormItem"
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import RegionSelector from "@/bizComponents/RegionSelector"
import EditDialog from './EditDialog'
import {delCustomer, getCustomers, getLimitRegion} from "@/api/system/customer"

@ -29,7 +29,7 @@
</template>
<script>
import DialogForm from '@/bizComponents/DialogForm'
import DialogForm from '@/components/DialogForm'
import dialogMixin from "@/mixins/dialogMixin"
import {addDepartment, updateDepartment} from "@/api/system/department"
import {isEmpty} from '@/utils'

@ -29,7 +29,7 @@
</template>
<script>
import DialogForm from '@/bizComponents/DialogForm'
import DialogForm from '@/components/DialogForm'
import dialogMixin from "@/mixins/dialogMixin"
import {updateResource} from "@/api/system/resource"
import {mergeObj, resetObj} from '@/utils'

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

Loading…
Cancel
Save