NoticeDialog.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <template>
  2. <Modal
  3. class="notice-dialog marker-modal"
  4. v-model="modalIsShow"
  5. title="消息"
  6. :mask-closable="false"
  7. footer-hide
  8. fullscreen
  9. @on-visible-change="visibleChange"
  10. >
  11. <div class="notice-container">
  12. <div v-if="IS_MARK_LEADER" class="notice-users">
  13. <div class="user-item user-all">
  14. <div>小组成员</div>
  15. <div
  16. :class="['user-selection', { 'is-active': userSelectAll }]"
  17. @click="selectAll"
  18. >
  19. <Icon v-if="userSelectAll" type="ios-checkmark-circle" />
  20. <Icon v-else type="md-radio-button-off" />
  21. </div>
  22. </div>
  23. <div
  24. v-for="user in markers"
  25. :key="user.id"
  26. class="user-item"
  27. @click="selectUser(user)"
  28. >
  29. <div class="user-avatar"><Icon type="md-contact" /></div>
  30. <div class="user-name" :title="user.name">{{ user.name }}</div>
  31. <div :class="['user-selection', { 'is-active': user.selected }]">
  32. <Icon v-if="user.selected" type="ios-checkmark-circle" />
  33. <Icon v-else type="md-radio-button-off" />
  34. </div>
  35. </div>
  36. </div>
  37. <div class="notice-body">
  38. <div ref="NoticeContent" class="notice-content">
  39. <div v-if="hasMore" class="notice-item notice-more" @click="nextPage">
  40. 获取更多
  41. </div>
  42. <div v-for="notice in notices" :key="notice.id" class="notice-item">
  43. <div class="notice-item-head">
  44. <span class="notice-head-users">
  45. {{ notice.relateUsers }}
  46. </span>
  47. <span class="notice-head-time">
  48. {{ notice.createTime }}
  49. </span>
  50. </div>
  51. <div class="notice-item-content" v-html="notice.content"></div>
  52. </div>
  53. </div>
  54. <div v-if="IS_MARK_LEADER" class="notice-send">
  55. <textarea
  56. placeholder="请输入"
  57. v-model="content"
  58. maxlength="300"
  59. ></textarea>
  60. <div class="notice-send-footer">
  61. <Button type="primary" :loading="loading" @click="toSend"
  62. >发送</Button
  63. >
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. </Modal>
  69. </template>
  70. <script>
  71. import { mapState, mapMutations } from "vuex";
  72. import {
  73. userSendNoticeList,
  74. userReceiveNoticeList,
  75. fetchReleaseUnreadNotice,
  76. readNotice,
  77. sendNotice,
  78. leaderMarkUserList
  79. } from "@/api";
  80. import timeMixin from "@/plugins/timeMixin";
  81. export default {
  82. name: "ribbon-set-dialog",
  83. mixins: [timeMixin],
  84. data() {
  85. return {
  86. workId: "",
  87. subject: "",
  88. modalIsShow: false,
  89. curUserRoleType: this.$ls.get("user", { role: "" }).role,
  90. userSelectAll: false,
  91. markers: [],
  92. notices: [],
  93. current: 0,
  94. size: 10,
  95. content: "",
  96. hasMore: false,
  97. fetching: false,
  98. loading: false
  99. };
  100. },
  101. computed: {
  102. ...mapState("marker", ["curSubject"]),
  103. IS_MARKER() {
  104. return this.curUserRoleType === "MARKER";
  105. },
  106. IS_MARK_LEADER() {
  107. return this.curUserRoleType === "MARK_LEADER";
  108. }
  109. },
  110. mounted() {
  111. const [workId, subject] = this.$route.params.subjectId.split("-");
  112. this.workId = workId;
  113. this.subject = subject;
  114. this.initData();
  115. },
  116. beforeDestroy() {
  117. this.clearSetTs();
  118. },
  119. methods: {
  120. ...mapMutations("marker", ["setShortcut", "recoverShortcut"]),
  121. async initData() {
  122. if (this.IS_MARK_LEADER) {
  123. this.getLeaderMarkerList();
  124. this.getLeaderMarkerNoticeList();
  125. return;
  126. }
  127. if (this.IS_MARKER) {
  128. await this.getMarkerNoticeList();
  129. // await this.getNewNotice();
  130. }
  131. },
  132. visibleChange(visible) {
  133. if (visible) {
  134. if (this.IS_MARKER) {
  135. this.setNoticeRead();
  136. }
  137. this.goToBottom();
  138. this.setShortcut([]);
  139. } else {
  140. this.recoverShortcut();
  141. }
  142. },
  143. goToBottom() {
  144. this.$nextTick(() => {
  145. this.$refs.NoticeContent.scrollTop = this.$refs.NoticeContent.offsetHeight;
  146. });
  147. },
  148. async getLeaderMarkerList() {
  149. const markerId = this.$ls.get("user", { id: "" }).id;
  150. const data = await leaderMarkUserList({
  151. workId: this.workId,
  152. subject: this.subject,
  153. stage: this.curSubject.stage,
  154. markerId
  155. });
  156. const markers = data || [];
  157. this.markers = markers.map(item => {
  158. item.selected = false;
  159. return item;
  160. });
  161. },
  162. async nextPage() {
  163. if (this.fetching) return;
  164. this.fetching = true;
  165. this.current++;
  166. if (this.IS_MARKER) {
  167. await this.getMarkerNoticeList().catch(() => {});
  168. } else {
  169. await this.getLeaderMarkerNoticeList().catch(() => {});
  170. }
  171. this.fetching = false;
  172. },
  173. async getLeaderMarkerNoticeList() {
  174. const sendUserId = this.$ls.get("user", { id: "" }).id;
  175. const lastId = this.notices.length ? this.notices.slice(-1)[0].id : null;
  176. const data = await userSendNoticeList({
  177. subject: this.subject,
  178. stage: this.curSubject.stage,
  179. sendUserId,
  180. lastId,
  181. page: this.current,
  182. size: this.size
  183. });
  184. const notices = data.content.map(item => {
  185. item.receiveUser = JSON.parse(item.receiveUser);
  186. item.relateUsers = item.receiveUser.map(user => user.name).join(",");
  187. return item;
  188. });
  189. notices.reverse();
  190. this.hasMore = this.current + 1 < data.totalPages;
  191. this.notices = [...notices, ...this.notices];
  192. },
  193. parseMarkerNotice(info) {
  194. return {
  195. ...info.message,
  196. id: info.id,
  197. read: info.read,
  198. relateUsers: info.message.sendUserName
  199. };
  200. },
  201. async getMarkerNoticeList() {
  202. const receiveUserId = this.$ls.get("user", { id: "" }).id;
  203. const lastId = this.notices.length ? this.notices.slice(-1)[0].id : null;
  204. const data = await userReceiveNoticeList({
  205. subject: this.subject,
  206. stage: this.curSubject.stage,
  207. receiveUserId,
  208. lastId,
  209. page: this.current,
  210. size: this.size
  211. });
  212. const notices = data.content.map(item => {
  213. return this.parseMarkerNotice(item);
  214. });
  215. notices.reverse();
  216. this.hasMore = this.current + 1 < data.totalPages;
  217. this.notices = [...notices, ...this.notices];
  218. },
  219. async getNewNotice() {
  220. this.clearSetTs();
  221. const receiveUserId = this.$ls.get("user", { id: "" }).id;
  222. const resData = await fetchReleaseUnreadNotice({
  223. subject: this.subject,
  224. stage: this.curSubject.stage,
  225. receiveUserId
  226. }).catch(() => {});
  227. const data = resData || [];
  228. const noticeIds = this.notices.map(item => item.id);
  229. const validNotices = data
  230. .filter(item => !noticeIds.includes(item.id))
  231. .map(item => this.parseMarkerNotice(item));
  232. if (validNotices.length) {
  233. this.notices.push(...validNotices);
  234. this.open();
  235. this.goToBottom();
  236. this.$nextTick(() => {
  237. this.setNoticeRead();
  238. });
  239. }
  240. this.addSetTime(() => {
  241. this.getNewNotice();
  242. }, 10 * 1000);
  243. },
  244. setNoticeRead() {
  245. const noticeIds = this.notices
  246. .filter(item => !item.read)
  247. .map(item => item.id);
  248. if (!noticeIds.length) return;
  249. readNotice(noticeIds);
  250. },
  251. cancel() {
  252. this.modalIsShow = false;
  253. },
  254. open() {
  255. this.modalIsShow = true;
  256. },
  257. selectChange() {
  258. this.userSelectAll = !this.markers.some(item => !item.selected);
  259. },
  260. selectUser(user) {
  261. user.selected = !user.selected;
  262. this.selectChange();
  263. },
  264. selectAll() {
  265. this.userSelectAll = !this.userSelectAll;
  266. this.markers.forEach(user => {
  267. user.selected = this.userSelectAll;
  268. });
  269. },
  270. async toSend() {
  271. if (!this.content) {
  272. this.$Message.error("请输入内容");
  273. return;
  274. }
  275. if (this.content.length > 300) {
  276. this.$Message.error("内容最多300个字符");
  277. return;
  278. }
  279. const user = this.$ls.get("user", { id: "", name: "" });
  280. const markers = this.markers
  281. .filter(item => item.selected)
  282. .map(item => {
  283. return {
  284. id: item.id,
  285. name: item.name
  286. };
  287. });
  288. if (!markers.length) {
  289. this.$Message.error("请选择评卷员");
  290. return;
  291. }
  292. if (this.loading) return;
  293. this.loading = true;
  294. const data = {
  295. subject: this.subject,
  296. stage: this.curSubject.stage,
  297. sendUserId: user.id,
  298. sendUserName: user.name,
  299. content: this.content,
  300. markers
  301. };
  302. const res = await sendNotice(data).catch(() => {});
  303. this.loading = false;
  304. if (!res) return;
  305. this.content = "";
  306. res.receiveUser = JSON.parse(res.receiveUser);
  307. res.relateUsers = res.receiveUser.map(item => item.name).join(",");
  308. this.notices.push({ ...res });
  309. this.goToBottom();
  310. }
  311. }
  312. };
  313. </script>