message-card.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <template>
  2. <div v-if="list.length" class="message-bg">
  3. <div class="message-main clickable">
  4. <!-- <div class="card-title">
  5. <span>最新消息</span>
  6. <span class="message-unread" v-if="msgData.unread > 0">
  7. {{ setUnRead }}
  8. </span>
  9. <div class="icon-box" @click="goMore">
  10. <AppIcon name="youbian" size="14" color="#c0c4cc" />
  11. </div>
  12. </div> -->
  13. <div ref="msgCard" class="message-card">
  14. <div
  15. class="message-group"
  16. :style="{
  17. transform: `translateY(-${offsetHeight}px)`,
  18. height: `${groupHeight}px`,
  19. }"
  20. >
  21. <div
  22. v-for="(item, index) in list"
  23. :key="index"
  24. ref="listItem"
  25. v-bound-phone="bindPhoneGoMessage(item)"
  26. class="message-item"
  27. :class="{ visited: item.visited }"
  28. @click="goMessage(item)"
  29. >
  30. <!-- <span class="dot"></span> -->
  31. <van-cell class="message-item-cell" is-link>
  32. <template #title>
  33. <span class="icon iconfont icon-nav_un_message" />
  34. <span class="type">{{ item.type }}</span>
  35. <span class="flex-1 title van-ellipsis">{{ item.title }}</span>
  36. </template>
  37. <template #default>
  38. <span class="time">{{ item.time }}</span>
  39. </template>
  40. </van-cell>
  41. <!-- <div class="van-ellipsis title">{{ item.title }}</div>
  42. <span class="time">{{ item.time }}</span> -->
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. </template>
  49. <script>
  50. import { Cell } from 'vant'
  51. import { mapGetters } from 'vuex'
  52. import {
  53. ajaxMarkRead,
  54. ajaxMessageList,
  55. ajaxMessageOpenLog
  56. } from '@/api/modules/message'
  57. import { AppIcon } from '@/ui'
  58. import { dateMatter } from '@/utils/date'
  59. import { appCallReloadTab } from '@/utils/callFn/appFn'
  60. import { callChangeTab, openLinkOfOther, px2viewport, vw2px } from '@/utils'
  61. // const MSG_TYPE = ['活动通知', '服务通知', '订阅消息', '项目动态', '企业动态', '分析报告', '系统通知', '剑鱼学堂', '商机情报']
  62. export default {
  63. name: 'MessageCard',
  64. components: {
  65. [AppIcon.name]: AppIcon,
  66. [Cell.name]: Cell
  67. },
  68. data() {
  69. return {
  70. list: [],
  71. msgData: {
  72. rollingTiming: 10
  73. },
  74. offsetHeight: 0,
  75. groupHeight: 20,
  76. msgListParams: {
  77. isColumn: true,
  78. isColumnNewMsg: false,
  79. isContainLetter: false,
  80. isMsgList: true,
  81. isRead: 0,
  82. offset: 1,
  83. size: 20
  84. }
  85. }
  86. },
  87. computed: {
  88. ...mapGetters('user', ['isLogin']),
  89. setUnRead() {
  90. return this.msgData.unread > 99 ? '99+' : this.msgData.unread
  91. },
  92. setHeight() {
  93. const height = !this.msgData.unread
  94. if (height) {
  95. return 70 / 3.75
  96. }
  97. else {
  98. return 76 / 3.75
  99. }
  100. }
  101. },
  102. created() {
  103. if (this.isLogin) {
  104. this.loadList()
  105. }
  106. },
  107. mounted() {
  108. setTimeout(() => {
  109. this.$nextTick(() => {
  110. this.startScrolling()
  111. })
  112. }, 1000)
  113. },
  114. destroyed() {
  115. this.stopScrolling()
  116. },
  117. methods: {
  118. decimalValue(numberValue) {
  119. // 将数值转换为字符串并截取前两位小数
  120. const decimalValue = numberValue.toString().match(/^\d+(?:\.\d?)?/)
  121. // 返回处理后的数值字符串
  122. return decimalValue ? decimalValue[0] : ''
  123. },
  124. startScrolling() {
  125. const offsetY = 20
  126. const offsetYvw = px2viewport(offsetY)
  127. const offsetYPx = vw2px(offsetYvw)
  128. let listItem
  129. if (this.$refs.listItem) {
  130. listItem = this.$refs.listItem[1] // 列表项高度
  131. }
  132. else {
  133. listItem = {
  134. offsetHeight: offsetYPx
  135. }
  136. }
  137. const listItemHeight = listItem?.offsetHeight || offsetYPx
  138. this.groupHeight = this.list.length * listItemHeight
  139. this.scrollInterval = setInterval(() => {
  140. this.offsetHeight += listItemHeight // 每次滚动的距离,根据需求调整
  141. if (-this.offsetHeight <= -(this.list.length * listItemHeight)) {
  142. this.offsetHeight = 0
  143. }
  144. }, this.msgData.rollingTiming * 1000) // 滚动间隔,单位为秒
  145. if (this.msgData.unread && this.msgData.unread <= 2) {
  146. this.stopScrolling()
  147. }
  148. },
  149. stopScrolling() {
  150. clearInterval(this.scrollInterval)
  151. },
  152. loadList() {
  153. ajaxMessageList(this.msgListParams).then((res) => {
  154. this.msgData = res
  155. let { data = [], column } = res
  156. if (!Array.isArray(data)) {
  157. data = []
  158. }
  159. if (!column) {
  160. column = []
  161. }
  162. this.list = [].concat(
  163. data.map((v) => {
  164. const MSG_TYPE = column.filter(m => m.msg_type === v.msg_type)
  165. return {
  166. title: v.title,
  167. visited: v.isRead === 1,
  168. time: dateMatter(v.createtime, 'nor'),
  169. type: MSG_TYPE && MSG_TYPE.length !== 0 ? MSG_TYPE[0].name : '',
  170. data: v
  171. }
  172. })
  173. )
  174. })
  175. },
  176. goVisited(item) {
  177. if (item.data.realType === 14) {
  178. this.$router.push({
  179. path: '/message/materialDetail',
  180. query: {
  181. id: Number(item.data.id),
  182. msgLogId: Number(item.data.msgLogId)
  183. }
  184. })
  185. }
  186. else {
  187. this.$router.push({
  188. path: '/message/msgDetail',
  189. query: {
  190. id: Number(item.data.id),
  191. msgLogId: Number(item.data.msgLogId)
  192. }
  193. })
  194. }
  195. },
  196. goMore() {
  197. callChangeTab('message', this.$router)
  198. },
  199. // 消息点击
  200. async onClickMsg(logId) {
  201. await ajaxMessageOpenLog({
  202. msgLogId: logId,
  203. platform: this.getPlatformType()
  204. })
  205. },
  206. getPlatformType() {
  207. return this.$envs.inWX ? 3 : 2
  208. },
  209. goMessage(item) {
  210. this.onClickMsg(item.data.msgLogId)
  211. if (item.visited) {
  212. return this.goVisited(item)
  213. }
  214. else {
  215. this.changeMessageStatus(item)
  216. }
  217. },
  218. changeMessageStatus(item) {
  219. ajaxMarkRead({
  220. msgId: Number(item.data.id)
  221. }).then(() => {
  222. // 刷新消息中心列表小红点
  223. this.msgLinkAction(item)
  224. appCallReloadTab('message', 1)
  225. // appCallReloadTab('search', 0)
  226. })
  227. },
  228. // 链接跳转
  229. async msgLinkAction(item) {
  230. const url = item.data.url
  231. const { inWX, inIOS } = this.$envs
  232. if (inWX) {
  233. if (url.weChatUrl) {
  234. this.wxLocationTo(url.weChatUrl)
  235. }
  236. else {
  237. this.goVisited(item)
  238. }
  239. }
  240. else {
  241. let toLink = inIOS ? url.iosUrl : url.androidUrl
  242. if (toLink) {
  243. const hasHttp = /^http(s)?:\/\//.test(toLink)
  244. if (!hasHttp && toLink[0] !== '/') {
  245. toLink = `/${toLink}`
  246. }
  247. openLinkOfOther(toLink)
  248. }
  249. else {
  250. this.goVisited(item)
  251. }
  252. }
  253. },
  254. // 微信端跳转
  255. wxLocationTo(url) {
  256. // 链接中如果是www.jianyu360.cn域名,微信中会唤起app,该用window.open
  257. // window.open(url)
  258. // window.open在安卓正常,但是ios无法跳转。改为location.href跳转
  259. if (!url)
  260. return
  261. location.href = url
  262. },
  263. bindPhoneGoMessage(item) {
  264. return {
  265. props: {
  266. name: '首页-消息列表'
  267. },
  268. next: () => {
  269. this.goMessage(item)
  270. }
  271. }
  272. }
  273. }
  274. }
  275. </script>
  276. <style lang="scss" scoped>
  277. .message-bg {
  278. background-color: #f5f6f7;
  279. padding: 12px 8px;
  280. }
  281. .message-main {
  282. position: relative;
  283. display: flex;
  284. flex-direction: column;
  285. justify-content: center;
  286. // padding: 6px 0 10px;
  287. height: 100%;
  288. background-color: #fff;
  289. border-radius: 12px;
  290. overflow: hidden;
  291. box-shadow: 0px 2px 8px rgba(54, 147, 179, 0.05);
  292. .card-title {
  293. display: flex;
  294. justify-content: space-between;
  295. align-items: center;
  296. position: relative;
  297. margin-bottom: 4px;
  298. font-size: 12px;
  299. line-height: 18px;
  300. color: #171826;
  301. padding: 0 12px;
  302. }
  303. .message-unread {
  304. line-height: 14px;
  305. padding: 1px 4px 0 4px;
  306. border-radius: 10px;
  307. background: #fb483d;
  308. color: #fff;
  309. font-size: 10px;
  310. margin-right: 16px;
  311. }
  312. }
  313. .message-card {
  314. display: flex;
  315. flex-direction: row;
  316. align-items: flex-start;
  317. justify-content: space-between;
  318. background-color: #ffffff;
  319. padding: 0 12px;
  320. height: 40px;
  321. overflow: hidden;
  322. &.have-one {
  323. align-items: center;
  324. }
  325. .message-group {
  326. //max-width: 315px;
  327. width: 100%;
  328. transition: transform 0.5s ease; /* 添加过渡效果 */
  329. }
  330. .message-item {
  331. display: flex;
  332. flex-direction: row;
  333. align-items: center;
  334. height: 40px;
  335. line-height: 18px;
  336. &-cell {
  337. padding: 0;
  338. line-height: normal;
  339. display: flex;
  340. align-items: center;
  341. }
  342. &.visited {
  343. .dot {
  344. background-color: #c0c4cc;
  345. }
  346. }
  347. .dot {
  348. flex-shrink: 0;
  349. width: 4px;
  350. height: 4px;
  351. border-radius: 50%;
  352. background: #2abed1;
  353. margin-right: 4px;
  354. }
  355. .type {
  356. flex-shrink: 0;
  357. font-weight: 700;
  358. font-size: 12px;
  359. line-height: 18px;
  360. color: #171826;
  361. margin-right: 12px;
  362. }
  363. .title {
  364. font-size: 11px;
  365. line-height: 24px;
  366. color: #171826;
  367. margin-right: 12px;
  368. }
  369. .time {
  370. flex-shrink: 0;
  371. font-size: 11px;
  372. line-height: 24px;
  373. color: #9b9ca3;
  374. }
  375. .icon-nav_un_message {
  376. margin-right: 4px;
  377. color: $color_main;
  378. }
  379. ::v-deep {
  380. .van-cell__title {
  381. display: flex;
  382. align-items: center;
  383. white-space: nowrap;
  384. overflow: hidden;
  385. text-overflow: ellipsis;
  386. }
  387. .van-cell__value {
  388. flex: unset;
  389. }
  390. .van-cell__right-icon {
  391. color: $color_main;
  392. font-size: 12px;
  393. line-height: 26px;
  394. }
  395. }
  396. }
  397. .icon-youbian {
  398. font-size: 16px;
  399. color: #c0c4cc;
  400. }
  401. }
  402. .icon-box {
  403. position: absolute;
  404. top: 53%;
  405. right: 12px;
  406. transform: translateY(-50%);
  407. z-index: 2;
  408. }
  409. </style>