sd-message-remind.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <template>
  2. <a-card>
  3. <a-tabs v-model="activeKey">
  4. <a-tab-pane key="reminder">
  5. <template slot="tab">
  6. 催办提醒
  7. <a-badge v-if="unreadCount[type.reminder] > 0" dot />
  8. </template>
  9. <sd-data-table
  10. ref="reminder"
  11. data-url="api/framework/v1/person/messages/reminder-with-trustor"
  12. :columns="reminderColumns"
  13. :row-key="'reminderId'"
  14. :search-fields="['msgTitle']"
  15. @rowClick="(...args) => rowClick(type.reminder, ...args)"
  16. >
  17. <msg-title-display
  18. slot="msgTitle"
  19. slot-scope="text, record"
  20. :context="_self"
  21. :text="text"
  22. :read="!!record.msgStatus"
  23. />
  24. </sd-data-table>
  25. </a-tab-pane>
  26. <a-tab-pane key="expiry">
  27. <template slot="tab">
  28. 到期提醒
  29. <a-badge v-if="unreadCount[type.expiry] > 0" dot />
  30. </template>
  31. <sd-data-table
  32. ref="expiry"
  33. data-url="api/framework/v1/person/messages/expiry-with-trustor"
  34. :columns="expiryColumns"
  35. :search-fields="['msgTitle']"
  36. @rowClick="(...args) => rowClick(type.expiry, ...args)"
  37. >
  38. <msg-title-display
  39. slot="msgTitle"
  40. slot-scope="text, record"
  41. :context="_self"
  42. :text="text"
  43. :read="!!record.msgStatus"
  44. />
  45. <div slot="flowTimeToDeadLine" slot-scope="text, record">
  46. <div
  47. v-if="record.flowTimeToDeadline"
  48. :class="{
  49. [$style.limittime]: record.flowColor === 'blue',
  50. [$style.expiretime]: record.flowColor === 'orange',
  51. [$style.overtime]: record.flowColor === 'red',
  52. }"
  53. >
  54. <a-icon type="sd-3-circle" title="全流程限时" :class="$style.remind" />{{
  55. deadline(record.flowColor)
  56. }}:
  57. <div :class="$style.time" :title="record.flowTimeToDeadline"
  58. >{{ record.flowTimeToDeadline }}
  59. </div>
  60. </div>
  61. </div>
  62. <div slot="stepTimeToDeadLine" slot-scope="text, record">
  63. <div
  64. v-if="record.stepTimeToDeadline"
  65. :class="{
  66. [$style.limittime]: record.stepColor === 'blue',
  67. [$style.expiretime]: record.stepColor === 'orange',
  68. [$style.overtime]: record.stepColor === 'red',
  69. }"
  70. >
  71. <a-icon type="exclamation-circle" title="本环节限时" :class="$style.remind" />{{
  72. deadline(record.stepColor)
  73. }}:
  74. <div :class="$style.time" :title="record.stepTimeToDeadline"
  75. >{{ record.stepTimeToDeadline }}
  76. </div>
  77. </div>
  78. </div>
  79. </sd-data-table>
  80. </a-tab-pane>
  81. <a-tab-pane key="xitong">
  82. <template slot="tab">
  83. 系统提醒
  84. <a-badge v-if="unreadCount[type.sys] > 0" dot />
  85. </template>
  86. <sd-data-table
  87. ref="xitong"
  88. data-url="api/framework/v1/person/messages/sys-with-trustor"
  89. :columns="columns"
  90. :actions="actions"
  91. :search-fields="['msgTitle']"
  92. :filter-expressions="xtexpressions"
  93. :show-selection="true"
  94. >
  95. <msg-title-display
  96. slot="msgTitle"
  97. slot-scope="text, record"
  98. :context="_self"
  99. :text="text"
  100. :read="!!record.msgStatus"
  101. :clickable="!!record.module"
  102. tool-tip
  103. @click="sysRemindClick(record)"
  104. />
  105. </sd-data-table>
  106. </a-tab-pane>
  107. <a-tab-pane key="inform" tab="知会提醒">
  108. <sd-data-table
  109. ref="inform"
  110. data-url="api/framework/v1/person/messages/inform"
  111. :columns="informColumns"
  112. :search-fields="['msgTitle']"
  113. @rowClick="(...args) => rowClick('', ...args)"
  114. >
  115. <div slot="msgTitle" slot-scope="text, record">
  116. <a v-if="record.openLink" @click="rowClick('', record)">{{ text }}</a>
  117. <span v-else>{{ text }}</span>
  118. </div>
  119. </sd-data-table>
  120. </a-tab-pane>
  121. </a-tabs>
  122. </a-card>
  123. </template>
  124. <script>
  125. import storeMixin from '@/common/store-mixin'
  126. import TodoListService from '@/todo/todo-list-service'
  127. import TableColumnTypes from '@/common/services/table-column-types'
  128. import TableActionTypes from '@/common/services/table-action-types'
  129. import { Message, Modal, Tooltip, Badge } from 'ant-design-vue'
  130. import eventBus from '@/common/services/event-bus'
  131. import crossWindowWatcher from '@/common/services/cross-window-watcher'
  132. import loginService from '@/login/login-service'
  133. import navMenuService from '@/frame/nav-menu-service'
  134. import components from './_import-components/sd-message-remind-import'
  135. import WorkbenchService from './workbench-service'
  136. const reminderColumns = [
  137. {
  138. title: '标题',
  139. dataIndex: 'msgTitle',
  140. sdClickable: true,
  141. scopedSlots: { customRender: 'msgTitle' },
  142. },
  143. {
  144. title: '发送人',
  145. dataIndex: 'senderName',
  146. },
  147. {
  148. title: '催办内容',
  149. dataIndex: 'msgContent',
  150. },
  151. {
  152. title: '接收时间',
  153. dataIndex: 'sentDate',
  154. sdRender: TableColumnTypes.dateTime,
  155. sorter: true,
  156. defaultSortOrder: 'desc',
  157. },
  158. ]
  159. const expiryColumns = [
  160. {
  161. title: '标题',
  162. dataIndex: 'msgTitle',
  163. sdClickable: true,
  164. scopedSlots: { customRender: 'msgTitle' },
  165. },
  166. {
  167. title: '接收时间',
  168. dataIndex: 'sentDate',
  169. sdRender: TableColumnTypes.dateTime,
  170. sorter: true,
  171. defaultSortOrder: 'desc',
  172. },
  173. {
  174. title: '截止时间',
  175. dataIndex: 'dueDate',
  176. sdRender: TableColumnTypes.dateTime,
  177. },
  178. {
  179. title: '全流程限时',
  180. dataIndex: 'flowTimeToDeadLine',
  181. scopedSlots: { customRender: 'flowTimeToDeadLine' },
  182. },
  183. {
  184. title: '本环节限时',
  185. dataIndex: 'stepTimeToDeadLine',
  186. scopedSlots: { customRender: 'stepTimeToDeadLine' },
  187. },
  188. ]
  189. const columns = [
  190. {
  191. title: '提示内容',
  192. dataIndex: 'msgTitle',
  193. scopedSlots: { customRender: 'msgTitle' },
  194. },
  195. {
  196. title: '接收时间',
  197. dataIndex: 'sentDate',
  198. sorter: true,
  199. sdRender: TableColumnTypes.dateTime,
  200. defaultSortOrder: 'desc',
  201. width: '250px',
  202. },
  203. {
  204. title: '状态',
  205. dataIndex: 'msgStatus',
  206. sdHidden: true,
  207. },
  208. ]
  209. const informColumns = [
  210. {
  211. title: '标题',
  212. dataIndex: 'msgTitle',
  213. scopedSlots: { customRender: 'msgTitle' },
  214. },
  215. {
  216. title: '审批意见',
  217. dataIndex: 'msgContent',
  218. },
  219. {
  220. title: '发送人',
  221. dataIndex: 'senter',
  222. width: '16%',
  223. },
  224. {
  225. title: '发送时间',
  226. dataIndex: 'sentDate',
  227. sdRender: TableColumnTypes.dateTime,
  228. defaultSortOrder: 'desc',
  229. width: '16%',
  230. },
  231. {
  232. title: '发送环节',
  233. dataIndex: 'sentStepName',
  234. width: '16%',
  235. },
  236. ]
  237. const msgTitleDisplay = {
  238. props: {
  239. context: {
  240. type: Object,
  241. default: () => {},
  242. },
  243. read: {
  244. type: Boolean,
  245. default: false,
  246. },
  247. clickable: {
  248. type: Boolean,
  249. default: false,
  250. },
  251. text: {
  252. type: String,
  253. default: '',
  254. },
  255. toolTip: {
  256. type: Boolean,
  257. default: false,
  258. },
  259. },
  260. render() {
  261. const content = (
  262. <span
  263. class={{
  264. [this.context.$style.notice]: !this.read,
  265. [this.context.$style.readDoc]: this.clickable,
  266. }}
  267. on={{ click: () => this.$emit('click') }}
  268. >
  269. {!this.read ? <Badge dot title='未读' /> : ''}
  270. {this.text}
  271. </span>
  272. )
  273. return this.toolTip ? <Tooltip title={this.text}> {content}</Tooltip> : content
  274. },
  275. }
  276. export default {
  277. name: 'SdMessageRemind',
  278. metaInfo: {
  279. title: '消息提醒',
  280. },
  281. components: {
  282. ...components,
  283. msgTitleDisplay,
  284. },
  285. mixins: [storeMixin],
  286. data() {
  287. return {
  288. activeKey: 'reminder',
  289. reminderColumns,
  290. expiryColumns,
  291. columns,
  292. informColumns,
  293. actions: [
  294. {
  295. label: '已阅',
  296. id: 'read',
  297. permission: 'oaRemindMessage-readed',
  298. type: TableActionTypes.batch, // 主按钮,高亮色显示
  299. callback: (keys, records) => {
  300. this.haveReadRows(keys, records)
  301. },
  302. },
  303. {
  304. label: '删除',
  305. id: 'delete',
  306. permission: 'oaRemindMessage-delete',
  307. type: TableActionTypes.batch, // 批处理按钮,选中文档时才能点击
  308. callback: (keys, records) => {
  309. const result = records.some((item) => item.msgStatus === 0)
  310. if (result) {
  311. Modal.confirm({
  312. content: '您有未阅的消息,是否删除?',
  313. okText: '是',
  314. okType: 'danger',
  315. cancelText: '否',
  316. onOk: () => {
  317. this.handleDelete(keys)
  318. },
  319. })
  320. } else {
  321. this.handleDelete(keys)
  322. }
  323. },
  324. },
  325. ],
  326. expressions: [
  327. {
  328. dataType: 'str',
  329. name: 'msgStatus',
  330. op: 'eq',
  331. stringValue: '0',
  332. },
  333. ],
  334. xtexpressions: [
  335. {
  336. dataType: 'str',
  337. name: 'msgType',
  338. op: 'like',
  339. stringValue: '%%',
  340. },
  341. ],
  342. type: {
  343. reminder: 'reminder',
  344. expiry: 'expiry',
  345. sys: 'sys',
  346. },
  347. unreadCount: {},
  348. }
  349. },
  350. computed: {
  351. deadline() {
  352. return function(type) {
  353. let deadline = ''
  354. switch (type) {
  355. case 'blue':
  356. deadline = '限时'
  357. break
  358. case 'orange':
  359. deadline = '快到期'
  360. break
  361. case 'red':
  362. deadline = '已超时'
  363. break
  364. }
  365. return deadline
  366. }
  367. },
  368. },
  369. created() {
  370. // 初始化,未读数设置为0
  371. Object.keys(this.type).forEach((key) => {
  372. this.$set(this.unreadCount, this.type[key], 0)
  373. })
  374. this.updateUnreadCount()
  375. },
  376. methods: {
  377. sysRemindClick(task) {
  378. let url = ''
  379. const setUrl = (value) => (url = value)
  380. return eventBus.emit('sdSysMessageItemClick', task, { setUrl }).then((evt) => {
  381. if (evt.defaultPrevented) return
  382. if (!url) return
  383. if (task.pcAddr && !url.startsWith('http')) {
  384. let pcAddr = task.pcAddr.split('#')[0]
  385. if (pcAddr.substr(-1) !== '/') pcAddr = pcAddr + '/'
  386. url =
  387. pcAddr +
  388. `#/sd-thirdparty-login?redirect=${encodeURIComponent(
  389. url
  390. )}&local_token=${encodeURIComponent(loginService.getTokens().access_token)}`
  391. }
  392. return crossWindowWatcher.waitForChanged(url, task.srcTrustId)
  393. })
  394. },
  395. rowClick(type, record, rowIndex) {
  396. this.msgToRead(type, record)
  397. TodoListService.openTaskItem(record, this.activeKey).then((refreshFlag) => {
  398. if (refreshFlag) this.$refs[this.activeKey].refresh()
  399. })
  400. },
  401. msgToRead(type, record) {
  402. if (type) {
  403. // 消息是未读时,设置成已读
  404. if (!record.msgStatus) {
  405. WorkbenchService.haveReadMessage(record.id).then((res) => {
  406. if (res.status === 200) {
  407. record.msgStatus = 1
  408. this.unreadCount[type]--
  409. eventBus.emit('updateRemindCount', this.unreadCount)
  410. }
  411. })
  412. }
  413. }
  414. },
  415. // 系统提醒已阅功能
  416. haveReadRows(selectedRowKeys, records) {
  417. const msgStatus = records.every((item) => {
  418. return item.msgStatus
  419. })
  420. // 选中的只是已阅的消息
  421. if (msgStatus) {
  422. Message.warning('您选中的消息已是已阅!')
  423. } else {
  424. if (selectedRowKeys.length > 1) {
  425. const params = {
  426. ids: selectedRowKeys,
  427. }
  428. WorkbenchService.haveReadMessages(params).then((res) => {
  429. if (res.status === 200) {
  430. this.$refs.xitong.clearSelection()
  431. this.$refs.xitong.refresh()
  432. this.updateUnreadCount(this.type.sys)
  433. }
  434. })
  435. } else {
  436. WorkbenchService.haveReadMessage(selectedRowKeys.join(',')).then((res) => {
  437. if (res.status === 200) {
  438. this.$refs.xitong.clearSelection()
  439. this.$refs.xitong.refresh()
  440. this.updateUnreadCount(this.type.sys)
  441. }
  442. })
  443. }
  444. }
  445. },
  446. // 系统提醒 删除
  447. handleDelete(keys) {
  448. Modal.confirm({
  449. title: '您确定删除这项内容吗?',
  450. content: '删除这条数据后,就无法恢复初始的状态。',
  451. okText: '删除',
  452. okType: 'danger',
  453. onOk: () => {
  454. const params = {
  455. ids: keys.join(),
  456. }
  457. WorkbenchService.delete(params).then((res) => {
  458. if (res.status === 200) {
  459. Message.success('删除成功')
  460. this.$refs.xitong.clearSelection()
  461. this.$refs.xitong.refresh()
  462. this.updateUnreadCount(this.type.sys)
  463. } else {
  464. Message.error(res.statusText)
  465. }
  466. })
  467. },
  468. onCancel() {
  469. Message.warning('删除无效')
  470. },
  471. })
  472. },
  473. updateUnreadCount(type) {
  474. navMenuService.getRemindCount(type).then((res) => {
  475. if (res.data) {
  476. if (type) {
  477. this.$set(this.unreadCount, type, Number(res.data[type.toUpperCase()]) || 0)
  478. } else {
  479. this.$set(this.unreadCount, this.type.reminder, Number(res.data.REMINDER) || 0)
  480. this.$set(this.unreadCount, this.type.expiry, Number(res.data.EXPIRY) || 0)
  481. this.$set(this.unreadCount, this.type.sys, Number(res.data.SYS) || 0)
  482. }
  483. eventBus.emit('updateRemindCount', this.unreadCount)
  484. }
  485. })
  486. },
  487. },
  488. }
  489. </script>
  490. <style module lang="scss">
  491. @use '@/common/design' as *;
  492. .title {
  493. font-family: $typography-title-font-weight;
  494. }
  495. .timeout {
  496. color: $highlight-color;
  497. }
  498. .time {
  499. overflow: hidden;
  500. text-indent: 20px;
  501. text-overflow: ellipsis;
  502. white-space: nowrap;
  503. }
  504. .remind {
  505. margin-right: 4px;
  506. }
  507. /* 限时 */
  508. .limittime {
  509. color: $blue-6;
  510. }
  511. /* 快到期 */
  512. .expiretime {
  513. color: $yellow-6;
  514. }
  515. .overtime {
  516. color: $red-6;
  517. }
  518. .notice {
  519. :global(.ant-badge) {
  520. margin-right: 6px;
  521. vertical-align: baseline;
  522. }
  523. font-weight: 600;
  524. }
  525. .read-doc {
  526. color: $blue-6;
  527. cursor: pointer;
  528. }
  529. </style>